Manual ATR stoploss vs. ApplyStop ATR based trailing stop

I am working on various exit methods and was having trouble with a sanity check on my ATR trailing stop so I created a simple example to test my understanding. I used a slightly modified example of code from the kb article on plotting trailing stops and compared it to the example of the ATR based trailing stop from the AFL ApplyStop function reference page. I setup a simple single symbol backtest of the two stops for comparison but I cannot get the two methods to line up.

I am sure this is an oversight on my part but after much thrashing around I still cannot spot my logic or programming flaw. Any help would be appreciated.

Jim

// test the applystop trailing stop with 3*ATR(14)
// with manual plotted stop
SetOption( "InitialEquity", 10000 );
SetTradeDelays( 0, 0, 0, 0 ); 
SetOption( "MaxOpenPositions", 1000 );
SetOption("ExtraColumnsLocation", 1 ); 
SetPositionSize( 20, spsPercentOfEquity );
// --- end backtester settings ---
Buy = (PDI(14) > MDI(14));
ExitOption = Optimize("Exit Opt", 2, 1, 2, 1);
switch(ExitOption)
{
  case 1: // Trailing ATR Stop (ApplyStop NO PLOT OF SL	)
	Sell = 0;
	ApplyStop(stopTypeTrailing, stopModePoint, 3*ATR(14), True, True );
	break;

  case 2: // Trailing ATR Stop (manual)

	Sell = 0;
	trailARRAY = Null;
	trailstop = 0;
	stoplevel = 3*ATR(14);
	
	for( i = 1; i < BarCount; i++ )
	{

	   if( trailstop == 0 AND Buy[ i ] ) 
	   { 
		  trailstop = High[ i ] - stoplevel[ i ];
	   }
	   else Buy[ i ] = 0; // remove excess buy signals

	   if( trailstop > 0 AND Low[ i ] <= trailstop )
	   {
		  Sell[ i ] = 1;
		  SellPrice[ i ] = trailstop;
		  trailstop = 0;
	   }

	   if( trailstop > 0 )
	   {
		  trailstop = Max( High[ i ] - stoplevel[ i ], trailstop );
		  trailARRAY[ i ] = trailstop;
	   }

	}
//	Plot( trailARRAY,"trailing stop level", colorRed )
	Plot( stopLevel,"trailing stop level", colorBlack);  // for sanity compare to 3 times the ATR indicator - they match
	break;
};

2 Likes

Your manual stop is simplified and not equivalent to full-featured ApplyStop. One of simplifications it does not take into account that if trailing stop is reached at the beginning of the bar at the OPEN (because of gap down), the trade would occur on OPEN, not at trailstop level.

Tomasz, thanks or the fast reply. I have adjusted for my oversight with respect to opening gaps which helped in those cases but I still see various differences. At least I know now that I am on the right track and can expand this logic to other exit strategies.

Jim

Hi there,
I was wondering if JM2138 received a comprehensive answer to his latest post as he is still seeing various differences. The reason I'm asking is because I have a similar issue. When using the applystop function as in the code below, I am getting a sell signal on the 21/09/17 although my ATR trailstop is not being hit.

SetOption( "InitialEquity", 100000);								
SetOption("MaxOpenPositions", 1000);								
SetOption("AccountMargin", 100);									
SetOption("CommissionMode",1);             							
SetOption("CommissionAmount", 0.5);        							
SetBacktestMode(backtestRegular);
SetTradeDelays(0,0,0,0);          
BuyPrice 	= C;                        							
SellPrice	= C;                        							
Short = Cover = 0;

longtrade = C>EMA(C,70);
stoplength = 4*ATR(14);

Buy = longtrade;
Sell = 0;

ApplyStop(stopTypeTrailing, stopModePoint, stoplength, 1, True, 1);

Equity( 1, 0 ); // evaluate stops, all quotes

InTrade = Flip(Buy, Sell);

SetOption("EveryBarNullCheck", True );
stopline = IIf( InTrade, HighestSince( Buy, H - stoplength), Null );

PlotShapes(Buy*shapeUpArrow,colorWhite,0,Low);
PlotShapes(Sell*shapeDownArrow,colorYellow,0,High);

Plot( Close,"Price",colorBlack,styleBar);
Plot( stopline, "trailing stop line", colorPaleTurquoise);

image

However, there is no sell signal when using the manual trailstop function below.

SetOption( "InitialEquity", 100000);								
SetOption("MaxOpenPositions", 1000);								
SetOption("AccountMargin", 100);									
SetOption("CommissionMode",1);             							
SetOption("CommissionAmount", 0.5);        							
SetBacktestMode(backtestRegular);
SetTradeDelays(0,0,0,0);          
BuyPrice 	= C;                        							
SellPrice	= C;                        							
Short = Cover = 0;

longtrade = C>EMA(C,70);
//stoplength = 4*ATR(14);

Buy = longtrade;


//StopLevel = 1 - Param("trailing stop %", 3, 0.1, 10, 0.1)/100;
StopLevel = 4*ATR(14);


Sell = 0;
trailARRAY = Null;
trailstop = 0;

for( i = 1; i < BarCount; i++ )
{

   if( trailstop == 0 AND Buy[ i ] ) 
   { 
      trailstop = High[ i ] - stoplevel[i];
   }
   else Buy[ i ] = 0; // remove excess buy signals

   if( trailstop > 0 AND Low[ i ] < trailstop )
   {
      Sell[ i ] = 1;
      SellPrice[ i ] = trailstop;
      trailstop = 0;
   }

   if( trailstop > 0 )
   {
      trailstop = Max( High[ i ] - stoplevel[i], trailstop );
      trailARRAY[ i ] = trailstop;
   }

}


PlotShapes(Buy*shapeUpArrow,colorGreen,0,Low);
PlotShapes(Sell*shapeDownArrow,colorRed,0,High);

Plot( Close,"Price",colorBlack,styleBar);
//Plot( stopline, "trailing stop line", colorPaleTurquoise);

image

Is there something I'm not considering using the applystop function? I have checked out the various knowledgebase sites as well as used the exploration tool for debugging. The latter also shows no sell signal on the 21/09/17. It would be much appreciated if anyone was so kind to point me into the right direction.

Thank you and a happy new year.

What does detail log report says for this day ?

Avg exit price 5661.7. Yet trailstop at 5627.8

I was never able to completely replicate the ApplyStop function. In his reply earlier @Tomasz said

and I was never able to fully figure out the features of the ApplyStop and create equivalent code.

Have you tried using some simple printf() or trace() to see what the levels are?

First thing noticed is why is top chart showing different entry then bottom chart?

Is this from trade signals in trade report or is it simply a chart with some plots?

No, I haven't. I'm a beginner and not familiar with the application of these functions. I have used the exploration tool . Using that and also other softwares, the trailing stop based on a stop length of 4*ATR(14) with a level of 5627.8 is not hit at all. The top chart only shows one example of the misalignment. In addition, if you look carefully on the top chart, there is sell signal generated on the 19/05/17 although the bar closes below the trailstop on the 18/05/17 and the applystop function is using exit mode = 1. Again, a sell signal on the 18/05/17 is generated using the manual stop loss.

Thank you for pointing out the difference in the buy signal on the bottom chart. I have copied the correct chart below.The sell signal is the same. I will update it and also familiarise myself with the trace function but I do not think it will address the essence of the problem.

image

Wish I could help but I don’t have much time with a newborn and I’m very confused about what your doing.

I see two different AFL’s, one doesn’t even have a a plot for the stop line? So are both pictures from one of the AFL?

The arrows is one from backtest and one from the plots?

First goal of debugging is to realize that exploration, chart, scan, and backtest all do stuff a little different.

Comparing backtest and chart you need to account for Equity, multiple Buysignals. Backtester handles multiple signals while just using plot(buy) does not, etc.

If comparing backtest to some chart plot, plot the buyprice, compare to backtest buyprice , etc.

Try plotting the trail array in first example. Compare to he two.

Thank you for your help. Yes, they are two AFL's since one is using the applystop function and the other the manual stop code.

I'm basically asking the question, why does the apply stop function generate a sell signal at 5661.7 when the trail stop is at 5627.8 and the lowest low since the buy is at 5639.4.

In contrast, the manual stop does not generate a sell signal.

Looks like you got the AFL from
https://www.amibroker.com/kb/2007/03/24/how-to-plot-a-trailing-stop-in-the-price-chart/comment-page-1/

Now the fancy one with the loop. I double checked and the way it's wrote you did get it correct using "stoplevel[i]". The way the loop is wrote it pretty much flips everything to where it needs at that one point. Be careful however in the future. In the example provided it uses a fixed level in the param options. Your using an array as a stop. So depending on the loop, stoplevel[i] can change dynamically as it's an array but in this case the "if" statements lock the level in.

I did add a line to the sellprice as if it gaps down past, sellprice variable will overide and in backtester it will be stoplevel.

SetOption( "InitialEquity", 100000);								
SetOption("MaxOpenPositions", 1000);								
SetOption("AccountMargin", 100);									
SetOption("CommissionMode",1);             							
SetOption("CommissionAmount", 0.5);        							
SetBacktestMode(backtestRegular);
SetTradeDelays(0,0,0,0);          

Buy = C > EMA(C,70);

stoplevel = 4*ATR(14);
Sell = 0;
trailARRAY = Null;
trailstop = 0;


for( i = 1; i < BarCount; i++ )
{

   if( trailstop == 0 AND Buy[ i ] ) 
   { 
      trailstop = High[ i ] - stoplevel[i];
   }
   else Buy[ i ] = 0; // remove excess buy signals

   if( trailstop > 0 AND Low[ i ] < trailstop )
   {
      Sell[ i ] = 1;
      SellPrice[ i ] = Min(trailstop,Open[i]); // adjust for gap downs 
      trailstop = 0;
   }

   if( trailstop > 0 )
   {
      trailstop = Max( High[ i ] - stoplevel[i], trailstop );
      trailARRAY[ i ] = trailstop;
   }

}


PlotShapes(Buy*shapeUpArrow,colorGreen,0,Low);
PlotShapes(Sell*shapeDownArrow,coloryellow,0,High);

Plot( Close,"Price",colorBlack,styleBar);
Plot( TrailARRAY, "trailing stop line", colorPaleTurquoise);
Plot(High - 4*ATR(14),"ATR",colorDarkBlue);// added to reference ATR and TrailArray.

Now the other example, I don't have much time to really dig into how to fix it for your case but the issues I noticed are.

StopLevel = Param("trailing stop %", 3, 0.1, 10, 0.1 );

SetTradeDelays(0,0,0,0);

Buy = Cross( MACD(), Signal() );
Sell = 0;
ApplyStop( stopTypeTrailing, stopModePercent, StopLevel, True );

Equity( 1, 0 ); // evaluate stops, all quotes

InTrade = Flip( Buy, Sell );

SetOption("EveryBarNullCheck", True );
stopline = IIf( InTrade, HighestSince( Buy, High ) * ( 1 - 0.01 * StopLevel ), Null );

PlotShapes(Buy*shapeUpArrow,colorGreen,0,Low);
PlotShapes(Sell*shapeDownArrow,colorRed,0,High);

Plot( Close,"Price",colorBlack,styleBar);
Plot( stopline, "trailing stop line", colorRed )

The example above from the knowledge base is using a buy logic that is a "Pulse" signal meaning using cross() the signal comes in once at the cross. Your case you want to buy when C > EMA(C,20), this is a "state" signal.

The other issue I believe is that in the example it uses a fixed percent for the stop, while your example you want to use an array which changes every bar. If I plot the stop level, it's going down and up while it should just go up.

And thinking about it now, the first example probably suffers the same issue of state vs pulse signal.

A way to get around that I believe is doing something like

Buy = Exrem(Buy,Sell);
Sell = Exrem(Sell,Buy);
stoplevel = valuewhen(Buy, 4*ATR(14));

Getting late now, wish I had the solution but not much time this week. I'm pretty sure that's the issue with your use of ApplyStop.

I generally use variables in my sell conditions and haven't used ApplyStop much.

Thank you very much for your detailed reply. Very helpful. I have adjusted the manual stop loss AFL and will use it until I figure out what is going on with the applystop function.

Thank you once again and hopefully you'll get some sleep.