Bar-Stop in higher Timeframe

Hello,
I'm facing some problems when trying to implement a bar stop, which exits a trade after e.g. 2 Bar in the higher-TimeFrame (e.g. Daily).
The base TimeFrame is for example 15 minutes.
(I know, the simple suggestion is: set the Base-TF also to Daily. But this is not possible. I need this granularity for several reasons.)
The first and easiest solution therefore would be to go to higher TF (Daily) via TimeFrameSet and code the stop there via Applystop. But in my attempts, it doesn't work.
It exited every time after 2 Bars in the Base-TF but not in the higher TF ( it should have been approximately 100-140 Base-TF-Bars (15 minutes) ) , as it should do.
Maybe I must use a variable for the Applystop and then expand it, but otherwise I'm not able to expand the Applystop. I don't see an other attempt to fix this try.

Sure, I thought about another really simple solution: determine the number of bars of the higher TF (e.g. Daily) in terms of the Base TF. But as far as I know, the relation beetween for example Daily and 15 minutes is fixed and therefore leads to a false solution (if the market isn't open 24 hours).
https://www.amibroker.com/guide/afl/timeframeset.html
(Align to market hours isn't an option. I want to test through different markets with the same formula.)

Ok. Now my solution attempt:


Filter = 1 ;


Buy_Cond = TimeNum() >= 160000 ;    // AND DayOfWeek() == 5 ; 
Buy = Buy_Cond ;
       
BarStop_TF_long = inDaily ;
Count_Bars_long = Param("Count Bars Stop", 2, 0, 100, 1) ;
Incr_Close_long = ParamToggle("Incr_Close?", "No|Yes", 1) ;  

Count_Bars_long = TimeFrameCompress(Count_Bars_long, BarStop_TF_long, compressOpen) ;
jjj = TimeFrameCompress( Buy, BarStop_TF_long, compressHigh );  // compressOpen

TimeFrameSet(BarStop_TF_long) ;
  Holdlength_Bars_long = BarsSince( jjj ) ;                                       
  Sell_Cond_Time_Bars = Holdlength_Bars_long == Count_Bars_long ;   // 
TimeFrameRestore() ;
Holdlength_Bars_long = TimeFrameExpand(Holdlength_Bars_long, BarStop_TF_long, expandFirst) ; 
Sell_Cond_Time_Bars = TimeFrameExpand(Sell_Cond_Time_Bars, BarStop_TF_long, expandFirst) ;  // 
jjj = TimeFrameExpand(jjj, BarStop_TF_long, expandFirst) ;   //
 
 //Sell_Cond_Time_Bars = Holdlength_Bars_long == Count_Bars_long ;  
 if(Incr_Close_long) Sell_Cond_Time_Bars = Sell_Cond_Time_Bars AND  NOT Ref(Sell_Cond_Time_Bars, 1) ;
 
 Sell = Sell_Cond_Time_Bars ;
 
Equity( 1, 0 ); // evaluate stops, all quotes
//SetOption("EveryBarNullCheck", True );	
 
 
 Short = Cover = 0 ;
 

 
AddColumn( Buy, "Buy", 1.0, TextColor = colorDefault, BkgndColor = IIf( Buy >= 1, colorGreen, colorDefault ) );
AddColumn( Buy_Cond, "Buy_Cond", 1.0) ;
AddColumn( ValueWhen( Buy, BarIndex() ), "VW - BarIndex" );

AddColumn( Sell, "Sell", 1.0, TextColor = colorDefault, BkgndColor = IIf( Sell >= 1, colorRed, colorDefault ) );
AddColumn( jjj , "jjj" );
AddColumn( Holdlength_Bars_long , "Holdlength_Bars_long" );
AddColumn( Count_Bars_long , "Count_Bars_long" );
AddColumn( Incr_Close_long , "Incr_Close_long" );
AddColumn( Sell_Cond_Time_Bars , "Sell_Cond_Time_Bars", 1.0, colorDefault, BkgndColor = IIf( Sell_Cond_Time_Bars >= 1, colorRed, colorDefault ) );

Well. This attempt works. But only, if the next Buy-Signal doesn't occur in the time window of the Bar-Stop.
Therefore " DayOfWeek() == 5 " works with a 2 daily-bar stop .
But if the Buy-Signal occurs every trading day, the stop can't work. " jjj " is overwriting itself !
I think there are 2 possible solutions:
a) fix this repeating Buy-Signals which haven't a corresponding Sell-Signal.
I've tried this. But I think I need to work with CBI for this purpose... Am I right?

b) the simpler solution would be to use Applystop.
It's doing exactly what I want: without overwriting, it waits for the Sell-Bar
I know, because of this reason it would be ways easier to use ApplyStop.
But as described above, I was struggling with ApplyStop (bars) in a higher TimeFrame.

I hope someone have a solution or can point me in the right direction.
Do I have to use the CBI?

What you are asking is possible, however, requires lot of work!

In simple words, once you are precisely sure of (stop) BarIndex of the Higher time-frame from the Lower one, then using StaticVars you can capture that stop value and use it on any chart you want, any way you want!

This might help you:

Hmmm, thank you!

Well, I can't understand why it's so difficult to code a simple bar-stop in a higher timeframe. Every time the same problem. The Buy-Signal overwrites itself.
And I can't tell the program that it shouldn't do this as long as a Sell-Signal occured, because the Sell-Signal is dependent on the Buy-Signal.
As said: the Applystop-function solves this problem and keeps the first Buy-Signal.
Can someone explain me, why it isn't possible to use the nbar stop (precisely) in a higher timeframe?!
Furthermore, it's (in my opinion) strange, that no one had the same problem in the past... if I'm searching something like "ApplyStop Multitimeframe" or even "ApplyStop Timeframe", there isn't anything similar, as far as I can see.
((except this one: https://forum.amibroker.com/t/how-can-i-back-test-multiple-time-frames/3072 ))
Is this problem so stupid? :upside_down_face:

For now, it also would be ok, if no new Buy-Signal occurs until the number of bars in the higher TimeFrame is reached. But even this seems unobtainable.
(At least as far as I tried it in many, many hours yesterday and today.) :slightly_frowning_face:

Often simple things need lot of efforts to achieve. Simple ≠ easy. Try harder, here is one more cue to help you further.

1 Like

Sorry, upper code had an issue because of experimenting. That's why mode 0 and mode 2 were not working for forum posted code but they did work before. Forgot to revert back before posting...

I will delete upper post to prevent copying incorrect one.


So here is corrected post/code:

No, you do not need static variables for what you want to achieve.
You just need time frame functions and looping.

I have once posted n-bar looping stop here

I have modified that code to consider longer time frame bars below.
If assigning Interval() to tmfrm argument of the function then stop is applied to set interval's n-bars.
(Needless to say... since it's been my time spent -> commercial use prohibited, only available at this forum).

procedure LTF_Nbar_Stop(tmfrm, nbars, mode, expand) {
	/// @link https://forum.amibroker.com/t/bar-stop-in-higher-timeframe/15328/6
	/// @link http://forum.amibroker.com/t/backtesting-exit-method/1804/2
	/// @link http://www.amibroker.com/guide/h_backtest.html
	/// Copyright AmiBroker.com and fxshrat@gmail.com
	/// Commercial use prohibited!
	global Buy, Sell;
	local i, ltf_bi, bars_since, intra_bars;
	local buyentryprice, buyentrybar,lookback;
	local exit_bars, SellSignal, sellexitbar;
	//
	expand = Min(expand, 1);
	TimeFrameSet(tmfrm);
		ltf_bi = BarIndex();
	TimeFrameRestore();
	ltf_bi = TimeFrameExpand(ltf_bi, tmfrm, expand);
	
	new_ltfbar = Nz(ltf_bi != Ref(ltf_bi, -1),1);	
	cum_ltf_bi = Cum(new_ltfbar);
	bars_since = BarsSince(new_ltfbar);	
	
	exit_bars = nbars;
	sellexitbar = -1e9; 
	lookback = abs(expand-1);	
	buyentryprice = buyentrybar = intra_bars = 0; 
	for ( i = lookback; i < BarCount; i++ ) {	
		// If Buy entry signal
		if ( Buy[ i ] && buyentryprice == 0 ) {
			buyentryprice = BuyPrice[ i ];
			buyentrybar = cum_ltf_bi[ i ];
			switch ( mode ) {				
				case 1: intra_bars = bars_since[ i ];	
						break;
				case 2: if ( ! new_ltfbar[ i-lookback ] ) 
							exit_bars = nbars+1;
						break;	
				default: break;						
			}		
		} else Buy[ i ] = 0;
		//	
		// store i if ltf_bar is nth LTF one after Buy entry
		temp = cum_ltf_bi[ i ] == buyentrybar + exit_bars;	
		if ( temp && buyentrybar > 0 ) {
			sellexitbar = i;
			buyentrybar = 0;
			exit_bars = nbars;
		}	
		//	
		// exit if close is higher than buyprice or if ltf_bar is nth LTF one + (bi@Buy minus bi@ltf_bi)
		SellSignal = /*Close[ i ] > buyentryprice ||*/ i == sellexitbar + intra_bars;	
		if ( SellSignal && buyentryprice > 0 ) {
			Sell[ i ] = 1;    
			buyentryprice = intra_bars = 0;
			sellexitbar = -1e9;
		}	 
	} 
}

nbars = 2;
mode =  Param( "Mode", 1, 0, 2, 1);// 0/1/2
tmfrm = inDaily;// Interval();//

setbars = (nbars+1)*tmfrm/Max(1,Interval());
SetBarsRequired(setbars);

SetPositionSize( 1, spsShares );

Buy = Sell = Short = Cover = 0;

// Buy signal
Buy = Cross( C, MA( C, 20 ) );

// N-bar exit of longer time frame
LTF_nbar_stop(tmfrm, nbars, mode, expandLast);


SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );

TimeFrameSet( tmfrm );
ltf_str = Interval(2);// does not need expansion (not an array)
TimeFrameRestore();
val = TimeFrameExpand(1, tmfrm);// just to prevent warning 509 in AB 6.30
Title += StrFormat("\n%sN-bar's longer TF: %s, Nbars: %g, Mode: %g", EncodeColor(colorRed), ltf_str, nbars, mode);

Plot( C, "Close", ParamColor( "Color Price", colorDefault ), styleNoTitle | ParamStyle( "Style" ) | GetPriceStyle(), 0, 1, 0, 0);

PlotShapes( Buy*shapeUpArrow, colorGreen, 0, L, -15 );
PlotShapes( Sell*shapeDownArrow, colorRed, 0, H, -15 );

As for the function... It has three modes (mode 0, 1, 2).

Mode 0 counts in intraday entry's LTF(longer timeframe) bar as full bar and exits at last intrabar of nth LTF bar.
7

Mode 1 counts "exact" LTF's n-bars (if there are no missing intrabars at last LTF bar)... so it means it exits at same intra bar timenum as it was the case at Buy entry.. e.g Buy entry 14:15 -> then n-bar exit at 14:15 n-LTF-bars after Buy.
8

Last but not least mode 2 exits at end of nth LTF bar and instead of mode 0 it excludes buy entry day from counter (if Buy entry is not at start bar of LTF bar).
9

8 Likes

Thank you very much!! :slight_smile:

Lower timeframe bars can be compressed to form higher timeframe bars and then can be expanded back for proper array execution, however NOT vice-versa. So, to achieve the latter, you would need StaticVars (for example).

The message got clear later! Finally, a Sunday and some spare time... Below is another refined flexible approach, wherein, you can fine tune each & every aspect of your trading system separately.

A demo strategy - Buy/Sell on Base Interval
FastWMA = WMA( Price, FastPer )
SlowWMA = WMA( Price, SlowPer )

Buy = Cross( FastWMA, SlowWMA )
Stop-Loss when Long: Prior 2-bar's Low on a Higher Time-frame which is trailed unless hit or a new Short signal is triggered

Short = Cross( SlowWMA, FastWMA )
Stop-Loss when Short: Prior 2-bar's High on a Higher Time-frame which is trailed unless hit or a new Long signal is triggered

/*_______________________________________________________________________________________________________________________
|This code is for AmiBroker Formula Language (AFL) learning (non-commercial) purposes only. Please do not copy this code|
|(or any other version of it) and paste it over on other forums or anywhere else on the Internet or in any other form	|
|without the AmiBroker Forum owner's consent (https://forum.amibroker.com/).                                            |
_______________________________________________________________________________________________________________________*/

_SECTION_BEGIN( "Trailing Stop-Loss on Higher Timeframe" );
	 SetChartOptions( 1, chartShowDates );
	 
	 BaseIntrvl = Interval( 0 );
	 HghrIntrvl = Param( "Set Higher Timeframe (in hours)", 24, 1, 24, 1 ) * 3600;
	 
	 Price = ParamField( "Price Field", 3 ); // 3 = Close, 4 = Average
	 FastPer = Param( "Fast WMA Period", 34 );
	 SlowPer = Param( "Slow WMA Period", 55 );
	 
	 NumBarBk = Param( "Number of bars back (on higher timeframe)", 2, 0, 3, 1 );
	 
	 if( HghrIntrvl > BaseIntrvl ) {
		 SetChartBkColor( colorBlack );
		 Plot( C, "Price", colorDefault, styleCandle );
		 
		 // A Demo strategy - Buy/Sell on BaseIntrvl
		 FastWMA = WMA( Price, FastPer );
		 SlowWMA = WMA( Price, SlowPer );
		 _Buy = Cross( FastWMA, SlowWMA );
		 _Short = Cross( SlowWMA, FastWMA );
		 
		 // Array Initialization
		 Buy = Sell = Short = Cover = LongTSL = ShrtTSL = Null; // TSL (Trailing Stop-Loss) to be applied on HghrIntrvl
		 LongFlag = ShortFlag = 0; // Flags to record trade state
		 
		 bi = BarIndex();
		 HghrIntrvlBiStrt = TimeFrameExpand( TimeFrameCompress( bi, HghrIntrvl, compressOpen ), HghrIntrvl, expandFirst );
		 HghrIntrvlBiEnd = TimeFrameExpand( TimeFrameCompress( bi, HghrIntrvl, compressLast ), HghrIntrvl, expandFirst );
		 HghrIntrvlH = HighestSince( bi == HghrIntrvlBiStrt, H );
		 HghrIntrvlL = LowestSince( bi == HghrIntrvlBiStrt, L );
		 
		 TslHghrIntrvlH = TimeFrameGetPrice( "H", HghrIntrvl, -NumBarBk, expandFirst );
		 TslHghrIntrvlL = TimeFrameGetPrice( "L", HghrIntrvl, -NumBarBk, expandFirst );
		 
		 GfxSetZOrder( -1 );
		 GfxSetCoordsMode( 1 );
		 for( i = 0; i < BarCount; i++ ) {
			 // Long Positions
			 if( _Buy[ i ] && !LongFlag ) {
				 Buy[ i ] = 1;
				 LongFlag = 1; // To record that we are in Long position
				 
				 BuyPrice[ i ] = C[ i ];
			 }
			 if( LongFlag ) {
				 // Defining LongTSL
				 if( LongTSL[ i - 1 ] >= TslHghrIntrvlL[ i ] )
					 LongTSL[ i ] = LongTSL[ i - 1 ];
				 else {
					 LongTSL[ i ] = TslHghrIntrvlL[ i ];
					 ShrtTSL[ i ] = Null;
				 }
			 }
			 if( ( _Short[ i ] || L[ i ] <= LongTSL[ i ] ) && LongFlag ) {
				 Sell[ i ] = 1; // Selling-off the Long position
				 LongFlag = 0;  // Reseting LongFlag back to False, to denote that we are no longer in "Long" position
				 
				 if( _Short[ i ] ) SellPrice[ i ] = C[ i ];
				 else if( L[ i ] <= LongTSL[ i ] ) SellPrice[ i ] = LongTSL[ i ];
			 }
		 
			 // Short Positions
			 if( _Short[ i ] && !ShortFlag ) {
				 Short[ i ] = 1;
				 ShortFlag = 1;	 // To record that we are in Short position
				 
				 ShortPrice[ i ] = C[ i ];
			 }
			 if( ShortFlag ) {
				 // Defining ShrtTSL
				 if( ShrtTSL[ i - 1 ] <= TslHghrIntrvlH[ i ] )
					 ShrtTSL[ i ] = ShrtTSL[ i - 1 ];
				 else {
					 ShrtTSL[ i ] = TslHghrIntrvlH[ i ];
					 LongTSL[ i ] = Null;
				 }
			 }
			 if( ( _Buy[ i ] || H[ i ] >= ShrtTSL[ i ] ) && ShortFlag ) {
				 Cover[ i ] = 1; // Covering the Short position
				 ShortFlag = 0;  // Reseting ShortFlag back to False, to denote that we are no longer in "Short" position
				 
				 if( _Buy[ i ] ) CoverPrice[ i ] = C[ i ];
				 else if( H[ i ] >= ShrtTSL[ i ] ) CoverPrice[ i ] = ShrtTSL[ i ];
			 }
			 
			 // For visuals (https://forum.amibroker.com/t/bar-stop-in-higher-timeframe/15328/9?u=cougar)
			 if( i == HghrIntrvlBiStrt[ i ] ) {
				 VarSet( "x1", i );		VarSet( "y1", O[ i ] );
			 }
			 if( i == HghrIntrvlBiEnd[ i ] ) {
				 GreenCandle = C[ i ] > VarGet( "y1" );
				 RedCandle = C[ i ] < VarGet( "y1" );
				 
				 GfxSelectSolidBrush(
				 IIf( GreenCandle, ColorRGB( 0, 28, 0 ),
				 IIf( RedCandle, ColorRGB( 28, 0, 0 ), colorGrey50 ) ) );				 
				 GfxRectangle( VarGet( "x1" ), VarGet( "y1" ), i, C[ i ] );
				 
				 GfxSelectSolidBrush( colorDarkGrey );
				 GfxRectangle( ( VarGet( "x1" ) + i ) / 2 - 1, HghrIntrvlH[ i ], ( VarGet( "x1" ) + i ) / 2 + 1, IIf( GreenCandle, C[ i ], IIf( RedCandle, VarGet( "y1" ), C[ i ] ) ) );
				 GfxRectangle( ( VarGet( "x1" ) + i ) / 2 - 1, HghrIntrvlL[ i ], ( VarGet( "x1" ) + i ) / 2 + 1, IIf( GreenCandle, VarGet( "y1" ), IIf( RedCandle, C[ i ], O[ i ] ) ) );
			 }
		 }
		 
		 // Plotting
		 Plot( FastWMA, "Fast WMA", colorDarkGreen, styleNoRescale | styleNoLabel );
		 Plot( SlowWMA, "Slow WMA", colorBrown, styleNoRescale | styleNoLabel );
		 
		 PlotShapes( IIf( Buy, shapeUpArrow, shapeNone ), colorBrightGreen, 0, L, -30 ); // Long Entry
		 PlotShapes( IIf( Sell, shapeSmallDownTriangle, shapeNone ), colorBrown, 0, H, -15 ); // Long Exit
	 
		 PlotShapes( IIf( Short, shapeDownArrow, shapeNone ), colorRed, 0, H, -30 ); // Short Entry
		 PlotShapes( IIf( Cover, shapeSmallUpTriangle, shapeNone ), colorDarkGreen, 0, L, -15 ); // Short Exit
	 
		 Plot( LongTSL, "Long TSL", colorPink, styleThick | styleNoRescale | styleNoLabel );
		 Plot( ShrtTSL, "Short TSL", colorPink, styleThick | styleNoRescale | styleNoLabel );
		 
		 Title = "";
	 }
	 else {
		 SetChartBkColor( colorDarkRed );
		 Title =
		 "Lower timeframe bars can be compressed to form higher timeframe bars
		 and then can be expanded back for proper array execution, however NOT
		 vice-versa.";
	 }
_SECTION_END();

1

2 Likes

I have modified the formula slightly, which fxshrat kindly had shared.
I'm trying to get a Calendar-Day-Stop.
The rules are as follows: the amount of calendar days for the stop is e.g. 5.
If today is the fourth day but the next trading day is the seventh one (e.g. weekend), the exit should be earlier (at this fourth calendar day).

It seems that this line isn't working as expected:

		temp = ((Cum_CDdiff[ i ] < buyCalendarDay + exit_Cdays) AND (Cum_CDdiff[ i+1 ] > buyCalendarDay + exit_Cdays) )

Any hints?


Filter = 1 ;


procedure HTF_N_Cdays_Stop(nCdays, mode, expand) {
	/// Copyright AmiBroker.com and fxshrat@gmail.com
	/// Commercial use prohibited!
	global Buy, Sell;                                                             
	local i, bi, CD_HTF, CD_Diff, CD_HTF_Ref1, bars_since, intrabar;
	local buyentryprice, buyentrybar, buyCalendarDay, lookback;
	local exit_Cdays, SellSignal, sellexitbar;
	//
	BaseTF = Interval() ;
	HTF = inDaily ;
	expand = Min(expand, 1);    // 1 == expandFirst   // >= 2  == expandPoint   // (0 == expandLast)  
	
	bi = BarIndex() ;
	bi = TimeFrameCompress(bi, inDaily, compressOpen) ;      
	TimeFrameSet(inDaily);
		CD_HTF =  DaysSince1900() ;  // Calendar Days (higher TimeFrame)
		CD_HTF_Ref1 = Ref(DaysSince1900(), -1) ;
		CD_Diff = IIf( bi >= 1, CD_HTF - CD_HTF_Ref1, 0 ) ;
	TimeFrameRestore();
	CD_HTF = TimeFrameExpand(CD_HTF, HTF, expand);        
	CD_Diff = TimeFrameExpand(CD_Diff, HTF, expand);  
	
	new_HTFbar = Nz(CD_HTF != Ref(CD_HTF, -1), 1);   // because of TimeFrameSet 
	CD_Diff = IIf(new_HTFbar, CD_Diff, 0) ;	
	Cum_CDdiff = Cum(CD_Diff) ;
	bars_since = BarsSince(new_HTFbar);	
	                                
	exit_Cdays = nCdays;
	sellexitbar = -1e9; 
	lookback = abs(expand-1) + HTF/BaseTF ;	   // expand - 1  // extra lookback because of Ref( ,-1) in Daily-TF
	buyentryprice = buyCalendarDay = intrabar = 0; 
	for ( i = lookback; i < BarCount - 1; i++ ) {	
		// If Buy entry signal
		if ( Buy[ i ] && buyentryprice == 0 ) {
			buyentryprice = BuyPrice[ i ];
			buyCalendarDay = Cum_CDdiff[ i ];
			switch ( mode ) {	
			    case "Incr_Close": exit_Cdays = nCdays + 1 ;
						           break;				
				case "Incr_Open":  intrabar = 1;	  
						           break;
				case "Incr_Intraday":  intrabar = bars_since[ i ] ;	 // bars since buy
						               break;
				default:  exit_Cdays = nCdays + 1 ;
						  break;						
			}		
		} else Buy[ i ] = 0;
				
		// store i if htf_bar is nth hTF one after Buy entry
		temp = ((Cum_CDdiff[ i ] < buyCalendarDay + exit_Cdays) AND (Cum_CDdiff[ i+1 ] > buyCalendarDay + exit_Cdays) )
		       OR  (Cum_CDdiff[ i ] == buyCalendarDay + exit_Cdays) ;	
		if ( temp && buyCalendarDay > 0 ) {
			sellexitbar = i;
			buyCalendarDay = 0;
			exit_Cdays = nCdays;         
		}	   
			
		// exit if close is higher than buyprice or if htf_bar is nth LTF one + (bi@Buy minus bi@htf_bi)
		SellSignal = /*Close[ i ] > buyentryprice ||*/ (i == sellexitbar + intrabar) ;	
		if ( SellSignal && buyentryprice > 0 ) {
			Sell[ i ] = 1;    
			buyentryprice = intrabar = 0;
			sellexitbar = -1e9;
		}	 
	} 
} 
                                                

nCdays = Param("Calendar Days Time-Stop", 2, 0, 1000, 1) ;
mode =  ParamList("Mode", "Incr_Close|Incr_Open|Incr_Intraday", 0) ; 

setbars = (nCdays+1)*inDaily/Max(1,Interval());
SetBarsRequired(setbars);

SetPositionSize( 1, spsShares );

Buy = Sell = Short = Cover = 0;

// Buy signal
Buy = TimeNum() >= 160000 ;  // Cross( C, MA( C, 20 ) );
Buy &= NOT Ref(Buy, -1) ; 

if( nCdays == 0 AND mode == "Incr_Intraday" )  Sell = 1 ; 

// N-bar exit of longer time frame
HTF_N_Cdays_Stop(nCdays, mode, expandFirst) ;   //expandLast        


SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );


TimeFrameSet( inDaily );
HTF_str = Interval(2);// does not need expansion (not an array)
TimeFrameRestore();
val = TimeFrameExpand(1, inDaily);// just to prevent warning 509 in AB 6.30
Title += StrFormat("\n%sN-bar's longer TF: %s, Nbars: %g, Mode: %g", EncodeColor(colorRed), HTF_str, nCdays, mode);

Plot( C, "Close", ParamColor( "Color Price", colorDefault ), styleNoTitle | ParamStyle( "Style" ) | GetPriceStyle(), 0, 1, 0, 0);

PlotShapes( IIf(Buy, shapeCircle, shapeNone), colorBrightGreen, 0, BuyPrice, 0 );
PlotShapes( IIf(Sell, shapeCircle, shapeNone), colorRed, 0, SellPrice, 0 );  
PlotShapes( IIf(Buy, shapeUpArrow, shapeNone), colorGreen, 0, Low, -20 );
PlotShapes( IIf(Sell, shapeDownArrow, shapeNone), colorRed, 0, High, -20 );


AddColumn( Buy, "Buy", 1.0, TextColor = colorDefault, BkgndColor = IIf( Buy >= 1, colorGreen, colorDefault ) );
AddColumn( Sell, "Sell", 1.0, TextColor = colorDefault, BkgndColor = IIf( Sell >= 1, colorRed, colorDefault ) );

I do not have desire to decipher your code and what is wrong there but here is quick addition to my original one.

procedure LTF_Nbar_Stop(tmfrm, nbars, mode, expand) {
	/// LTF -> longer time frame
	/// version 1.1, added Calendar day exit (if difference is larger 1)
	/// @link https://forum.amibroker.com/t/bar-stop-in-higher-timeframe/15328/6
	/// @link http://forum.amibroker.com/t/backtesting-exit-method/1804/2
	/// @link http://www.amibroker.com/guide/h_backtest.html
	/// Copyright AmiBroker.com and fxshrat@gmail.com
	/// Commercial use prohibited!
	global Buy, Sell, cd_diff_fut;
	local i, ltf_bi, bars_since, intra_bars;
	local buyentryprice, buyentrybar,lookback;
	local exit_bars, SellSignal, sellexitbar;
	//
	expand = Min(expand, 1);
	lookback = abs(expand-1);
	TimeFrameSet(tmfrm);
		ltf_bi = BarIndex();
		cd_today = DaysSince1900();  // Calendar Days
		cd_diff_fut = Ref(cd_today, 1) - cd_today; 
	TimeFrameRestore();
	ltf_bi = TimeFrameExpand(ltf_bi, tmfrm, expand);
	// tomorrow cal.day diff expanded
	cd_diff_fut = TimeFrameExpand(cd_diff_fut, tmfrm, expandLast); 	

	new_ltfbar = Nz(ltf_bi != Ref(ltf_bi, -1),1);
	cum_ltf_bi = Cum(new_ltfbar);
	bars_since = BarsSince(new_ltfbar);		
	cum_buy = SumSince(new_ltfbar, Buy);

	exit_bars = nbars;
	sellexitbar = -1e9; 	
	buyentryprice = buyentrybar = intra_bars = 0; 
	for ( i = lookback; i < BarCount; i++ ) {	
		// If Buy entry signal
		if ( Buy[ i ] && buyentryprice == 0 ) {			
			buyentryprice = BuyPrice[ i ];
			buyentrybar = cum_ltf_bi[ i ];	
			switch ( mode ) {	
				case 1: intra_bars = bars_since[ i ];	
						break;
				case 2: if ( ! new_ltfbar[ i-lookback ] ) 
							exit_bars = nbars+1;
						break;	
				default: break;	
			}	
		} else Buy[ i ] = 0;
		//	
		// Default LTF nbar exit
		ltf_exit = cum_ltf_bi[ i ] == buyentrybar + exit_bars;
		// Exit if calendar day diff is larger 1 and 
		// cum. buy since start of LTF bar is 1		
		cd_exit = cd_diff_fut[ i ] > 1 AND cum_buy[i] < 1;
		// Combined temporary exit condition
		temp = ltf_exit OR cd_exit;	
		// Store i if ltf_bar is nth LTF one after Buy entry
		// or if calendar day difference is larger than 1
		if ( temp && buyentrybar > 0 ) {
			if ( ltf_exit )	sellexitbar = i + intra_bars;
			// if calendar day cond true then immediate exit on that day
			else			sellexitbar = i;
			buyentrybar = 0;
			exit_bars = nbars;
		}	
		//	
		// Exit if close is higher than buyprice or if ltf_bar is nth LTF one + (bi@Buy minus bi@ltf_bi)
		SellSignal = /*Close[ i ] > buyentryprice ||*/ i == sellexitbar;	
		if ( SellSignal && buyentryprice > 0 ) {
			Sell[ i ] = 1;    
			buyentryprice = intra_bars = 0;
			sellexitbar = -1e9;			
		} else Sell[ i ] = 0;
	} 
}


nbars = 3;
mode =  Param( "Mode", 1, 0, 2, 1);// 0/1/2

tmfrm = inDaily;// Interval();//
setbars = (nbars+1)*tmfrm/Max(1,Interval());
SetBarsRequired(setbars);
SetPositionSize( 1, spsShares );

Buy = Sell = Short = Cover = 0;

// Buy signal
Buy = Cross( C, MA( C, 20 ) );

// N-bar exit of longer time frame
LTF_nbar_stop(tmfrm, nbars, mode, expandLast);

Plot( cd_diff_fut, "CD diff tomorrow", colorRed, styleOwnScale );

SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );


TimeFrameSet( tmfrm );
ltf_str = Interval(2);// does not need expansion (not an array)
TimeFrameRestore();
val = TimeFrameExpand(1, tmfrm);// just to prevent warning 509 in AB 6.30
Title += StrFormat("\n%sN-bar's longer TF: %s, Nbars: %g, Mode: %g", EncodeColor(colorRed), ltf_str, nbars, mode);

Plot( C, "Close", ParamColor( "Color Price", colorDefault ), styleNoTitle | ParamStyle( "Style" ) | GetPriceStyle(), 0, 1, 0, 0);

PlotShapes( Buy*shapeUpArrow, colorGreen, 0, L, -15 );
PlotShapes( Sell*shapeDownArrow, colorRed, 0, H, -15 );

8

2 Likes

@fxshrat Thank you for this smart idea! I didn't thought about it's that easy :upside_down_face: