Code review appreciated

Hello again

I've been playing around with AFL and want to create small building blocks that I can depend on.

I want to create an intraday range(bound by time) breakout system. It should only go long when the price gets above the range by a certain buffer.
I want to trade it on CFDs where I pay no commission only spread.

Given I'm new to AFL, I appreciate any feedback from you guys!

I did my best to make a code that simulates how the broker would fill my orders. I run this code on 1m chart.

Expectations:

  • Enter at the break of the range by treshold and exit at either TP or SL
  • Intention is to simulate a limit buy with a bracket SL/TP as limit orders
  • To actually get filled like the broker would a CFD
  • To understand how the spread actually works
  • To plot what I've ploted accurately(I hope it's self explanatory)
  • I specically did not use ApplyStop so to control the process. Could this be an issue?

Issues I encountered:

  • I feel my code is clunky
  • The way I get slOrTPExecutionPrice is not ok! I know this because sometimes it comes as Null. I've also plotted it so I see it in the Data Window. When this happens, the exit price is the candle's Close
  • I don't get filled ok when the price plus the spread gets outside the candle. I think I might have seen some option for this in the forum/docs but it does not come to mind now
  • The above issues get obvious when I increase the spread

Overall I I'm kindof accomplishing what I want so for a first run I'm starting to be hopeful I can learn AFL. :smiley:

Any feedback/review or help with the issues encountered is immensely appreciated!
If things can be improved or you smell something funny please also let me know.

Thanks in advance

SetOption( "InitialEquity", 100000 );
SetOption( "MaxOpenPositions", 1000 );
SetOption( "AccountMargin", 80 );
SetTradeDelays( 0, 0, 0, 0 );

spread = 0.9;
pointsBufferEntry = 2;

//Vars
rangeStart = ParamTime("rangeStart", "11:00");
rangeEnd = ParamTime("rangeEnd", "11:30");
targetMult = 2;
maxTradesPerDay = 1;

//Helping stuff
timeNow = TimeNum();
isNewDay = DateNum() > Ref(DateNum(), -1);

//What to draw
chartShowRange = False;
chartDetails = True;

//Range stuff
rangeBarStart = timeNow == rangeStart;
rangeBarEnd = timeNow == rangeEnd;
rangeHigh = ValueWhen(rangeBarEnd, HighestSince(rangeBarStart, High));
rangeLow = ValueWhen(rangeBarEnd, LowestSince(rangeBarStart, Low));
range = rangeHigh - rangeLow;


entryPriceTheoretical = rangeHigh + pointsBufferEntry; //the breakout trigger price
entryPriceBrokerTreshold = entryPriceTheoretical; //threshold price for broker to execute my order
entryPriceExecution = entryPriceBrokerTreshold + spread; //price I get filled at

targetPoints = range * targetMult;

targetTheoretical = entryPriceTheoretical + targetPoints; //the target I want
targetBrokerTreshold = targetTheoretical + spread; //threshold that price hass to cross for sell to execute at broker
targetExecution = targetTheoretical; //price I get filled at when I sell

lossTheoretical = rangeLow; //the stop loss I want
lossBrokerTreshold = lossTheoretical + spread; //threshold. price needs to dip below this to sell position
lossExecution = lossTheoretical; //price I get filled at when I sell

breakout = H >= entryPriceBrokerTreshold;

isTPHit = H >= targetBrokerTreshold;
isSLHit = L <= lossBrokerTreshold;

Buy = breakout AND timeNow > rangeEnd; //buy the breakout after range time
Buy = Buy AND Sum(Buy, BarsSince(isNewDay) +1) <= maxTradesPerDay; //Allow only maxTradesPerDay
Sell = isTPHit OR isSLHit;

//What I try to do here is get the actual exit price but can't use <if> :(
//THIS IS NOT GOOD as sometimes slOrTPExecutionPrice is NULL on exit and the exit defaults to the bar's close
tpPrice = IIf(isTPHit, targetExecution, Null);
slPrice = IIf(isSLHit, lossExecution, Null);

slOrTPExecutionPrice= IIf(tpPrice, tpPrice, slPrice);

BuyPrice  = entryPriceExecution;
SellPrice = slOrTPExecutionPrice;


if(chartShowRange){
	Plot(rangeHigh, "rangeHigh", colorBlue, styleDashed);
	Plot(rangeLow, "rangeLow", colorBlue, styleDashed);
}

if(chartDetails){
	Plot(BuyPrice, "Entry/BuyPrice/fill from Ami", colorRed, styleDots);
	Plot(entryPriceBrokerTreshold, "Entry treshold", colorIndigo, styleLine);
	
	Plot(targetBrokerTreshold, "TP treshold", colorIndigo, styleLine);
	Plot(targetExecution, "TP fill price ", colorRed, styleDots);
	
	Plot(lossBrokerTreshold, "SL treshold", colorIndigo, styleLine);
	Plot(lossExecution, "SL fill price", colorRed, styleDots);
	
	Plot(slOrTPExecutionPrice, "Exit Price", colorOrange, styleLine);
}



Plot( timeNow >= rangeStart AND timeNow <= rangeEnd, "Colored Range", ColorBlend( colorYellow, colorLightOrange, 0.5 ), styleArea | styleOwnScale, 0, 1, 0, -1);

To be honest, it takes quite some time to understand AB, especially if you write software.
There are many examples in library, have a look.

Now, for example you are missing REF() and CROSS()

For example, consider


entryPriceTheoretical = rangeHigh + pointsBufferEntry;

This will point to current developing candle, which is not real life.
instead if you did Ref( rangeHigh, -1) for example, then it is realistic meaning upto the previous "completed" bar.

breakout = H >= entryPriceBrokerTreshold;

breakout =  Cross( C, entryPriceBrokerTreshold); ## ex2

similarly, above would rather look like ex2 which means current candle Close which is last price triggered a signal.

in AB see this concept of STATE vs IMPULSE,

There is alot more in your code but it is time and your learning. You cant just buy AB and start using it live the next week :slight_smile: (at least its my opinion)

Thanks for taking the time to respond @nsm51

The STATE/IMPULSE concept is good to know. I kind of had an idea about it but reading the thread helped understand it better! Switching to Cross for the entry does speed up the code! For the exit I notice behavior that I don't understand vs the comparison approach I am already using. Will investigate and post about it...

Regarding the backtest and live trading I want to place a Stop Market Buy order(I mentioned limit order before, but that is not correct) order above the range. When the price gets hit I'll be in a trade long, slippage or no slippage. Maybe I understand this wrong, but I do want to enter on the exact candle that triggers it, that's why I put SetTradeDelays( 0, 0, 0, 0 );.
But doing so I need to be sure I set the right price for the execution. For this I need to fiddle with the spread.

I am well aware that what I do is prone to not being as it is in real life and AB assumes things when there's not enough granularity in the data. I think for testing purposes it should give me a rough idea if it works or not.

My biggest issue though is with this code, as this does not produce the results I want all the time:

isTPHit = H >= targetBrokerTreshold;
isSLHit = L <= lossBrokerTreshold;
tpPrice = IIf(isTPHit, targetExecution, Null);
slPrice = IIf(isSLHit, lossExecution, Null);
slOrTPExecutionPrice= IIf(tpPrice, targetExecution, lossExecution);
SellPrice = slOrTPExecutionPrice;

My logic is: if price hits tp, take the TP price and assign it to SellPrice, but if the SL gets hit take the SL price and assign it to SellPrice
Is there another way to set TP and SL to SellPrice? For now I'm not so sure I use the Iif correctly.

Modifying the code a bit I end up with something apparently more reliable (this sets the price to SL since the end of the range and changes it only on the candles with isTPHit :

isTPHit = H >= targetBrokerTreshold;
isSLHit = L <= lossBrokerTreshold;
slOrTPExecutionPrice= IIf(isTPHit, targetExecution, lossExecution);
SellPrice = slOrTPExecutionPrice;

AB wise, I know about it for about 2 years and been meaning to get into it but was put off by it's complexity and way of working, not to mention the frequent replies I've seen in the forum, 'read the docs' :smiley:
Finally bit the bullet. I'm not new to trading nor systems, nor automated trading.
On top of this the way I seem to learn is to break things :smiley:

Cheers