What does "Buy[i] = 0" do in these AFL examples?

I don't think I fully understand what Buy[i] = 0; does in the context of the following two examples:

How to plot a trailing stop in the Price chart
The specific line in this example is:

else Buy[ i ] = 0; // remove excess buy signals

Example 4: partial exit (scaling out) on profit target stops
The specific line in this example is:

Buy[ i ] = 0;

I am seeing two issues in my attempted recreation of this code.

  1. When I use else Buy[ i ] = 0; like the first example and test it with it there and with it commented out, I don't see any differences. So I'm not clear what it is doing.

  2. When I use Buy[ i ] = 0; in a similar context to the second example a huge problem occurs - if I use an unusually small stop level, the strategy will not take valid signals that would violate the intraday stop. As an example, you can change the stop level to 2 and watch all the entries disappear on the chart that have breakout candles of larger than 2%.

I originally thought that Buy[i] = 0; was resetting the Buy variable once the trailing stop or profit target executed so that the strategy could accept a new Buy entry when a new signal was given. But the two points above have me questioning this.

The code I was using to experiment with this is below:

// ==========================================================================================
// === TRADING SYSTEM PARAMETERS ===
// ==========================================================================================
SetOption("InitialEquity", 100000);
SetTradeDelays( 0, 0, 0, 0 ); // 1 - Delays the trades to the next day, 0 - Trades same day via stops
SetOption("AllowPositionShrinking", True);
SetOption("AccountMargin",100); // 100 = Cash account, 75 = Using half available margin
SetOption("MaxOpenPositions", 20);
RoundLotSize = 1;  // Disallow fractional share purchasing


// ==========================================================================================
// === RISK MANAGEMENT & POSITION SIZING ===
// ==========================================================================================
// Initial (MAX) stop loss
SetOption("ActivateStopsImmediately", False);
stopLoss = 10; // Change this to a small number to experience the issue with trade entries being ignored
ApplyStop(stopTypeLoss, stopModePercent, stopLoss, ExitAtStop = 1);

// Position size based on risk and max stop loss
PositionRisk = 1;  
PosSize = 100 * PositionRisk / stopLoss;
SetPositionSize(PosSize, spsPercentOfEquity);
         

// ==========================================================================================
// === PLOT CHART & INDICATORS ===
// ==========================================================================================
// Chart
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} - " + FullName() + " | {{INTERVAL}} {{DATE}}: Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
Plot( C, "Close", ParamColor("Color", colorDefault ), styleNoTitle | ParamStyle("Style") | GetPriceStyle() ); 

// Donchian Channel
pdsU=10;  // High channel lookback
pdsL=5;  // Low channel lookback

DonchianUpper =HHV(H,pdsU);
DonchianLower = LLV(L,pdsL);

Plot(DonchianUpper,"DU",color = colorBlueGrey,styleDashed);
Plot(DonchianLower,"DL",color = colorBlueGrey, styleDashed);

  
// ==========================================================================================
// === BUY & SELL LOGIC ===
// ==========================================================================================
Buy=Cross(High, Ref(DonchianUpper, -1)); 
//Buy= (High > Ref(DonchianUpper, -1));
BuyPrice=Max(Ref(DonchianUpper, -1), Open);

// === TRAILING STOP ===
// Initialize variables
Sell=0;
trailArray = Null;
trailStop = 0;

//Trailing stop logic
for( i = 0; i < BarCount; i++ )
{  
  if( trailstop == 0 AND Buy[ i ] ) 
  { 
	trailstop = BuyPrice[i] * (1- stopLoss/100);
  }
  //else Buy[i] = 0; // remove excess buy signals
  if( trailstop > 0 AND Low[ i ] < trailstop )
   {   
	 Sell[ i ] = 1; 
	 SellPrice[ i ] = Min( Open[ i ], trailstop );
	 trailstop = 0;
	 Buy[i] = 0; // this is preventing entries when stop loss setting is smaller than candle range
   }
   
  if( trailstop > 0 )
   {
	  trailStop = Max(DonchianLower[i], trailStop); 
	  trailARRAY[ i ] = trailstop;
   }
}

Plot(trailArray, "Trailing Stop Level", colorRed, styleThick);

// Hide buy and sell signals if in position so that it doesn't plot extra arrows
Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell,Buy);

// Plot arrows indicating trades
PlotShapes(IIf(Buy, shapeUpTriangle, shapeNone),color = colorLime, 0, L, Offset=-50);
PlotShapes(IIf(Sell, shapeDownTriangle, shapeNone),color = colorRed, 0, H, Offset=-50);



Is used to remove excess Buy signals when already in a trade.

1 Like

Thank you, two follow up questions

  1. This code (in the code block below) being present seems to do the same thing (remove excess buy/sell signals), is this correct? If this is correct that might explain why I didn't see any difference when commenting out else Buy[ i ] = 0; in the first example.
Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell,Buy);
  1. I figured out why my second issue is happening. When you override your buy signal with Buy[i] = 0; on the same candle the signal is given, it will cancel out that signal. So... if the stop level is within the signal candle, it will not take the trade because of Buy[i] = 0; being present in this code.
    This is also true in the example code here: Example 4: partial exit (scaling out) on profit target stops.
    I just tested it and see the same behavior.

How can this code be structured in such a way as to not override the entry signals based on the stop level? Can we remove Buy[i] = 0; from this trailing stop loop and replace it with the ExRem code above outside of the loop? With an initial test, this seems to behave as I would expect, but I want to make sure I'm not overlooking something.

Yes it's doing same thing but use Buy [i] = 0 method - avoid using 'ExRem' (in backtester).

Don't use 'ExRem', Use Buy[i] = 0 (but don't override on new first signals!).

1 Like

I'm also using this AFL for charting and visualization. ExRem seems to be necessary to avoid plotting extra arrows where trades are taken. Possibly can do the same thing with Buy[i] = 0. I'll need to experiment seeing how to incorporate it without overriding initial buy signal and then seeing if that also solves the problem I had with multiple arrows plotting that ExRem solved. Thanks for your assistance.

Yes - it's the same Buy array.

1 Like

I tested out with and without ExRem enabled. While the results were very similar, one thing stood out.

With the ExRem fields commented out, breakout candles that trigger buys that are larger than the trailing stop are auto stopped out for max loss even though they likely shouldn't be like this example here.
exrem off

With ExRem fields enabled, this candle is correctly treated as a buy and not sold until the lower DC channel is crossed. The second buy arrow is ignored.
exrem turned on

This obviously doesn't look right with the two buy arrows but it behaves correctly. I have SetOption("ActivateStopsImmediately", False); set to try to avoid this.

The issue seems to be due to the initial stop level being within the breakout candle in my trailing stop logic. With ExRem on, it ignores it, with ExRem off, it is causing the breakout candle to trigger the stop even though in most cases they likely would not have triggered the stop due to the bullish nature of them.

I am not sure the best way to solve this. Since this code is looking for breakouts, you want to capture these and not be incorrectly stopped out. Potentially modifying the trailing stop to start the candle AFTER the buy signal is given would be one way so the intraday stop on the day of the trigger wouldn't cause this. But I'm not quite sure how to go about that or if that is the best method.

In my opinion, you want to trade and code 'real life' rules. True and final indicator values are not determined until the completion of each particular bar. So entering and exiting on the same bar off completed bar indicator values is out of sync with real life trading.

With your entry bar, AmiBroker does not know the exact point of entry for the entry bar. So, If you are using an intra-bar stop and the low breaches your stop but that happened before you entered the trade and not after you entered the trade then in your 'For Loop' you need accommodate for this scenario. eg, entryBarStopOut_ignore = True.

1 Like

One has to understand that ExRem is not really anything magical, it just goes thru all bars and internally does exactly this:

// for illustration purposes only
function ExRemEquivalent( set, reset )
{
	InTrade = False;
	for( i = 0; i < BarCount; i++ )
	{
       output[ i ] = False;
       
	   if( ! InTrade AND set[ i ] ) 
	   { 
			InTrade = True;
			output[ i ] = True;
	   }
	   
	   if( InTrade AND reset[ i ] )
	   {
			InTrade = False;
		}
	}

	return output;
}

One can do the same in the looping code if he/she wants to or can do anything else. It is your code and you are free to do anything that you imagine.

2 Likes

I think I figured this out with only one new line of code and one change to another line.
Added: entryBar = BarsSince(Buy); as a variable.
Changed conditions for the trailing stop to NOT exist if this entryBar variable is equal to 0.

if( trailstop > 0 AND Low[ i ] < trailstop AND entryBar[i] != 0)

And I am now seeing what was an intraday stopout become a correct breakout with a trailing stop that is within the candle.

Is this in line with what you were thinking?

Final code below for reference.

// ==========================================================================================
// === TRADING SYSTEM PARAMETERS ===
// ==========================================================================================
SetOption("InitialEquity", 100000);
SetTradeDelays( 0, 0, 0, 0 ); // 1 - Delays the trades to the next day, 0 - Trades same day via stops
SetOption("AllowPositionShrinking", True);
SetOption("AccountMargin",100); // 100 = Cash account, 75 = Using half available margin
SetOption("MaxOpenPositions", 20);
RoundLotSize = 1;  // Disallow fractional share purchasing


// ==========================================================================================
// === RISK MANAGEMENT & POSITION SIZING ===
// ==========================================================================================
// Initial (MAX) stop loss
SetOption("ActivateStopsImmediately", False);
stopLoss = 7; // Change this to a small number to experience the issue with trade entries being ignored
ApplyStop(stopTypeLoss, stopModePercent, stopLoss, ExitAtStop = 1);

// Position size based on risk and max stop loss
PositionRisk = 1;  
PosSize = 100 * PositionRisk / stopLoss;
SetPositionSize(PosSize, spsPercentOfEquity);
         

// ==========================================================================================
// === PLOT CHART & INDICATORS ===
// ==========================================================================================
// Chart
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} - " + FullName() + " | {{INTERVAL}} {{DATE}}: Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
Plot( C, "Close", ParamColor("Color", colorDefault ), styleNoTitle | ParamStyle("Style") | GetPriceStyle() ); 

// Donchian Channel
pdsU=4;  // High channel lookback
pdsL=5;  // Low channel lookback

DonchianUpper =HHV(H,pdsU);
DonchianLower = LLV(L,pdsL);

Plot(DonchianUpper,"DU",color = colorBlueGrey,styleDashed);
Plot(DonchianLower,"DL",color = colorBlueGrey, styleDashed);

  
// ==========================================================================================
// === BUY & SELL LOGIC ===
// ==========================================================================================
Buy=Cross(High, Ref(DonchianUpper, -1)); 
BuyPrice=Max(Ref(DonchianUpper, -1), Open);

// === TRAILING STOP ===
// Initialize variables
Sell=0;
trailArray = Null;
trailStop = 0;
entryBar = BarsSince(Buy);

//Trailing stop logic
for( i = 0; i < BarCount; i++ )
{  
  if( trailstop == 0 AND Buy[ i ] ) 
  { 
	trailstop = BuyPrice[i] * (1- stopLoss/100);
  }
  else Buy[i] = 0; // remove excess buy signals
  if( trailstop > 0 AND Low[ i ] < trailstop AND entryBar[i] != 0)
   {   
	 Sell[ i ] = 1; 
	 SellPrice[ i ] = Min( Open[ i ], trailstop );
	 trailstop = 0;
   }
   
  if( trailstop > 0 )
   {
	  trailStop = Max(DonchianLower[i], trailStop); 
	  trailARRAY[ i ] = trailstop;
   }
}


Plot(trailArray, "Trailing Stop Level", colorRed, styleThick);

// Hide buy and sell signals if in position so that it doesn't plot extra arrows - should not use for backtests
//Buy = ExRem(Buy,Sell);
//Sell = ExRem(Sell,Buy);

// Plot arrows indicating trades
PlotShapes(IIf(Buy, shapeUpTriangle, shapeNone),color = colorLime, 0, L, Offset=-50);
PlotShapes(IIf(Sell, shapeDownTriangle, shapeNone),color = colorRed, 0, H, Offset=-50);

// ==========================================================================================
// === EXPLORATION / SCANNING COLUMNS ===
// ==========================================================================================
Filter = 1;
AddTextColumn(FullName(), "Name");
AddColumn(Buy, "Buy Array");
AddColumn(entryBar, "BarsSince Buy");


1 Like

This is not accommodating for excess Buy signals.

This will give you no stop for the entry bar and no stop for any new raw buy signal bars while in the current trade.

I have this code in the loop which seems to do this. If I comment it out, you can see excess buy signals, but with it there, it only shows one until the sell signal is given.

  else Buy[i] = 0; // remove excess buy signals

Yes this is removing excess buy signals,

else Buy[i] = 0

This is not.

entryBar = BarsSince(Buy);

I see what you are saying. When looking at the exploration, I can see that the entryBar is resetting to 0 every time the upper Donchian Channel is crossed even when Buy does not register as a 1. Back to the drawing board, thank you for your feedback on this.

1 Like

Yes, exactly.

No, just control 'entryBar' variable inside your loop, entryBar [i] = True;

Well, I get some odd values in the exploration for all the bars that aren't actual buy signals (would have expected them to be 0 or false), but every time the buy signal exists, the entryBar is equal to 1, so I think this might do it. Thanks again for your continued help. Really helps out someone trying to learn AFL.

// ==========================================================================================
// === TRADING SYSTEM PARAMETERS ===
// ==========================================================================================
SetOption("InitialEquity", 100000);
SetTradeDelays( 0, 0, 0, 0 ); // 1 - Delays the trades to the next day, 0 - Trades same day via stops
SetOption("AllowPositionShrinking", True);
SetOption("AccountMargin",100); // 100 = Cash account, 75 = Using half available margin
SetOption("MaxOpenPositions", 20);
RoundLotSize = 1;  // Disallow fractional share purchasing


// ==========================================================================================
// === RISK MANAGEMENT & POSITION SIZING ===
// ==========================================================================================
// Initial (MAX) stop loss
SetOption("ActivateStopsImmediately", False);
stopLoss = 7; // Change this to a small number to experience the issue with trade entries being ignored
ApplyStop(stopTypeLoss, stopModePercent, stopLoss, ExitAtStop = 1);

// Position size based on risk and max stop loss
PositionRisk = 1;  
PosSize = 100 * PositionRisk / stopLoss;
SetPositionSize(PosSize, spsPercentOfEquity);
         

// ==========================================================================================
// === PLOT CHART & INDICATORS ===
// ==========================================================================================
// Chart
SetChartOptions(0,chartShowArrows|chartShowDates);
_N(Title = StrFormat("{{NAME}} - " + FullName() + " | {{INTERVAL}} {{DATE}}: Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
Plot( C, "Close", ParamColor("Color", colorDefault ), styleNoTitle | ParamStyle("Style") | GetPriceStyle() ); 

// Donchian Channel
pdsU=4;  // High channel lookback
pdsL=5;  // Low channel lookback

DonchianUpper =HHV(H,pdsU);
DonchianLower = LLV(L,pdsL);

Plot(DonchianUpper,"DU",color = colorBlueGrey,styleDashed);
Plot(DonchianLower,"DL",color = colorBlueGrey, styleDashed);


  
// ==========================================================================================
// === BUY & SELL LOGIC ===
// ==========================================================================================
Buy=Cross(High, Ref(DonchianUpper, -1)); 
BuyPrice=Max(Ref(DonchianUpper, -1), Open);

// === TRAILING STOP ===
// Initialize variables
Sell=0;
trailArray = Null;
trailStop = 0;

//Trailing stop logic
for( i = 0; i < BarCount; i++ )
{  
  if( trailstop == 0 AND Buy[ i ] ) 
  { 
	trailstop = BuyPrice[i] * (1- stopLoss/100);
	entryBar[i] = True;
  }
  else Buy[i] = 0; // remove excess buy signals
  if( trailstop > 0 AND Low[ i ] < trailstop AND entryBar[i] != True)
   {   
	 Sell[ i ] = 1; 
	 SellPrice[ i ] = Min( Open[ i ], trailstop );
	 trailstop = 0;
   }
   
  if( trailstop > 0 )
   {
	  trailStop = Max(DonchianLower[i], trailStop); 
	  trailARRAY[ i ] = trailstop;
   }
}


Plot(trailArray, "Trailing Stop Level", colorRed, styleThick);

// Hide buy and sell signals if in position so that it doesn't plot extra arrows - should not use for backtests
//Buy = ExRem(Buy,Sell);
//Sell = ExRem(Sell,Buy);

// Plot arrows indicating trades
PlotShapes(IIf(Buy, shapeUpTriangle, shapeNone),color = colorLime, 0, L, Offset=-50);
PlotShapes(IIf(Sell, shapeDownTriangle, shapeNone),color = colorRed, 0, H, Offset=-50);

// ==========================================================================================
// === EXPLORATION / SCANNING COLUMNS ===
// ==========================================================================================
Filter = 1;
AddTextColumn(FullName(), "Name");
AddColumn(Buy, "Buy Array");
AddColumn(entryBar, "BarsSince Buy", 1);


1 Like

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.