Handling buy and scaleout signal on same bar

I suppose that when multiple signals occur on a bar low level CBT is needed. below is my code.
But buy and scaleout occurs one bar before intended. It should happen at 14:55 but in log it shows at 14:45. Data file [10 minues timeframe] is here https://filebin.net/52uy7ttf2ofotmif
This is the detailed log. code below will run on copy paste.

SetCustomBacktestProc( "" );
_TRACE( "!CLEAR!" );
_SECTION_BEGIN( "Price" );
SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}} ", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
PlotOHLC( O, H, L, C, "Traded", colorRed, styleBar , Null, Null, 0, 1, 1 );
_SECTION_END();
chartid = 1;

dt = DateNum();
tnum = timenum();

PositionEntrySignal = PositionScaleInSignal = PositionScaleOutSignal = PositionExitSignal = 0;

PositionEntrySignal = IIf( tnum == 145500, 1, PositionEntrySignal );
Buy = PositionEntrySignal;
BuyPrice = 2270;

PositionScaleOutSignal = IIf( tnum == 145500, 1, PositionScaleOutSignal );
scaleoutPrice=2283;
StaticVarSet( Name() + chartid + "scaleoutPrice", scaleoutPrice, false, cmAlways );


PositionExitSignal = IIf( tnum == 151500, 1, PositionExitSignal ); //ohh stop hit
Sell = PositionExitSignal;
SellPrice = 2289;

StaticVarSet( Name() + chartid + "PositionEntrySignal", PositionEntrySignal, false, cmAlways );
StaticVarSet( Name() + chartid + "PositionScaleOutSignal", PositionScaleOutSignal, false, cmAlways );
StaticVarSet( Name() + chartid + "PositionExitSignal", PositionExitSignal, false, cmAlways );

SetPositionSize( 10, spsPercentOfEquity * PositionEntrySignal );

Atr5 = ATR( 5 );
locationAboveBar = H + Atr5;
locationBelowBar = L - Atr5 / 3;

PlotShapes( IIF( PositionEntrySignal, shapeUpArrow, shapeNone ), colorGreen, 0, locationBelowBar );
PlotShapes( IIF( PositionScaleInSignal, shapeHollowUpArrow, shapeNone ), colorLime, 0, locationBelowBar );
PlotShapes( IIF( PositionScaleOutSignal, shapeHollowDownArrow, shapeNone ), colorLime, 0, locationAboveBar );
PlotShapes( IIF( PositionExitSignal, shapeCircle, shapeNone ), colorLime, 0, locationAboveBar );

if( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject(); 
    bo.PreProcess();
    stat = bo.GetPerformanceStats( 0 ); 
    dt = DateTime();
    thisBarDate = DateTimeConvert( 0, dt );
    dontProcessTradeSignals = 0;

    for( i = 0; i < BarCount; i++ )
    {
        formatedDateTime = DateTimeFormat( "%d-%m-%Y %H:%M:%S", dt[i] );

        for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {
            openPosition = bo.FindOpenPos( sig.Symbol );

            positionEntrySignal = StaticVarGet( sig.Symbol + chartid + "positionEntrySignal" );
            positionScaleInSignal = StaticVarGet( sig.Symbol + chartid + "positionScaleInSignal" );
            positionScaleOutSignal = StaticVarGet( sig.Symbol + chartid + "positionScaleOutSignal" );
            positionExitSignal = StaticVarGet( sig.Symbol + chartid + "positionExitSignal" );
			scaleoutPrice= StaticVarGet( sig.Symbol + chartid + "scaleoutPrice" );
				
				if( PositionEntrySignal[i] && PositionScaleOutSignal[i] )
                {
                    bo.EnterTrade( i, sig.Symbol, true, sig.Price, sig.PosSize );
                    bo.ScaleTrade( i, sig.Symbol, false, scaleoutPrice[i], sig.PosSize/2 );
                    dontProcessTradeSignals[i] = 1;
                }  
                
        }

        if( dontProcessTradeSignals[i] == 0 )
            bo.ProcessTradeSignals( i );
    }

    bo.PostProcess();

}

Scaling signals are stored in Buy/Short arrays, therefore you can't have a buy and scale in the same bar, at least not without custom backtester.

It is doable with custom backtester as anything else since you don't really need signals at all in low-level custom backtester.

As to the "problem", there is no problem with code. It is problem with data.
Detailed log shows each bar available in the data set. You simply DO NOT have data bar at 14:55. You only have 14:45 and next bar is 15:05.

Run Exploration to find out the data you really have.

To get better understanding of what is happening in your code and how functions work, use advice given here: How do I debug my formula?

No problem with data.

No matter how much you would deny it does not change the facts:

No problem with data.

Debugging is your task. No-one is going to debug your formulas for you. Bars may be removed if you use padding to symbol that has holes. Chart is irrelevant. Chart is NOT using padding settings from analysis. You don't have that bar in Detailed Log, which means it does not exist in that particular run.
Don't expect others to do your homework.You have received enough information to conduct debugging yourself. Check Analysis SETTINGS, Simplify your formula, do ONE thing at a time, divide and conquer as explained in (already quoted many times): How do I debug my formula?

And by the way, this line is BAD. I MEAN REALLY BAD:

// NEVER EVER skip ProcessTradeSignals
if( dontProcessTradeSignals[i] == 0 ) bo.ProcessTradeSignals( i );

Where did you get that line from? Certainly not from documentation. Documentation clearly calls this function ALWAYS, unconditionally

ProcessTradeSignals MUST BE CALLED ALWAYS in mid-level CBT.

If you want to use low-level CBT, the code is given in documentation, but you ignored it. There is even a code snipped in the editor that gives you correct code, still you ignored it.

1 Like

thanks tomasz. I am currently working on your suggestion.

  1. bo.ProcessTradeSignals : i was using if( dontProcessTradeSignals[i] == 0 ) to selectively skip processTradeSignals(). Because my cbt code is a mix of high, mid and low level.

  2. I am not sure at this stage but i think there is some unhandled exception in amibroker that is causing the certain bars to disappear from detailed log.

I will post the code here when I have something.

ProcessTradeSignals not only processes signals but CALCULATES EQUITY on bar by bar basis and does a lot of house keeping. It MUST be called, or low-level backtest must be performed with appropriate housekeeping calls (see the docs).

If you want to skip signals you DO NOT skip ProcessTradeSignals, instead assign sig.Price = -1

It is also documented in many places including this document:

All problems occur because you are not obeying CBT rules.

It is clearly said in the docs:

https://www.amibroker.com/guide/a_custombacktest.html

Quote:

bool ProcessTradeSignals( long Bar )
This mid-level method processes all trading signals for given bar. It should be called once per every bar in your custom backtesting loop.

Just follow the rules.

1 Like

Below is my working code. There are only 3 multiple signal on same bar scenarios supported by my trading system so I have only covered those. I have several questions to ask you.

  1. Is this code agreeable? I have not skipped processTradingSignals but used sig.price=-1 as per your suggestion.

  2. Does bo.GetNextSignal( i ) do something funny in the background?

  3. Still don't know why it works only if i write the cbt low level code below the processTradingSignals call.
    If i write it above then trades are executed one bar before. Why does this happen???

Other wise this works as per requirement.


SetCustomBacktestProc( "" );
_TRACE( "!CLEAR!" );
_SECTION_BEGIN( "Price" );
SetChartOptions( 0, chartShowArrows | chartShowDates | chartWrapTitle );
_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}} ", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
PlotOHLC( O, H, L, C, "Traded", colorRed, styleBar , Null, Null, 0, 1, 1 );
_SECTION_END();
chartid = 1;

dt = DateNum();
tnum = timenum();

PositionEntrySignal = PositionScaleInSignal = PositionScaleOutSignal = PositionExitSignal = BuyAndScaleoutSignalOnSamebar = 0;
EntryPrice = ScaleInPrice = ScaleOutPrice = ExitPrice = 0;

//buy, scalein, exit 11:55
PositionEntrySignal = IIf( tnum == 115500, 1, PositionEntrySignal );
EntryPrice = IIf( tnum == 115500, 2268, EntryPrice );
PositionScaleInSignal = IIf( tnum == 115500, 1, PositionScaleInSignal); 
ScaleInPrice=IIf( tnum == 115500, 2269, ScaleInPrice); 
PositionExitSignal = IIf( tnum == 115500, 1, PositionExitSignal ); //stop hit
ExitPrice = IIf( tnum == 115500, 2271, ExitPrice );

//Buy, scaleout 14:55
PositionEntrySignal = IIf( tnum == 145500, 1, PositionEntrySignal );
EntryPrice = IIf( tnum == 145500, 2270, EntryPrice );
PositionScaleOutSignal = IIf( tnum == 145500, 1, PositionScaleOutSignal );
scaleoutPrice = IIf( tnum == 145500, 2283, scaleoutPrice);

//scaleout x 2, scalein 15:05
PositionScaleInSignal = IIf( tnum == 150500, 1, PositionScaleInSignal); 
ScaleInPrice=IIf( tnum == 150500, 2284, ScaleInPrice); 
PositionScaleOutSignal = IIf( tnum == 150500, 1, PositionScaleOutSignal );
scaleoutPrice = IIf( tnum == 150500, 2288, scaleoutPrice);
PositionScaleOutSignal = IIf( tnum == 150500, 1, PositionScaleOutSignal );
scaleoutPrice = IIf( tnum == 150500, 2289, scaleoutPrice);

//scaleout, exit 15:15
PositionScaleOutSignal = IIf( tnum == 151500, 1, PositionScaleOutSignal );
scaleoutPrice = IIf( tnum == 151500, 2288, scaleoutPrice);
PositionExitSignal = IIf( tnum == 151500, 1, PositionExitSignal ); //stop hit
ExitPrice = IIf( tnum == 151500, 2289, ExitPrice );



Buy = IIf( PositionEntrySignal, 1, IIf( PositionScaleInSignal, sigScaleIn, IIf( PositionScaleOutSignal, sigScaleOut, 0 ) ) );
BuyPrice = IIf( PositionEntrySignal, EntryPrice, IIf( PositionScaleInSignal, ScaleInPrice, IIf( PositionScaleOutSignal, scaleoutPrice, 0 ) ) );

Sell = PositionExitSignal;
SellPrice = ExitPrice;

BuyAndScaleoutSignalOnSamebar=PositionEntrySignal && PositionScaleOutSignal;

StaticVarSet( Name() + chartid + "PositionEntrySignal", PositionEntrySignal, false, cmAlways );
StaticVarSet( Name() + chartid + "PositionScaleOutSignal", PositionScaleOutSignal, false, cmAlways );
StaticVarSet( Name() + chartid + "PositionExitSignal", PositionExitSignal, false, cmAlways );

SetPositionSize( 10, spsPercentOfEquity * PositionEntrySignal  );

Atr5 = ATR( 5 );
locationAboveBar = H + Atr5;
locationBelowBar = L - Atr5 / 3;

PlotShapes( IIF( PositionEntrySignal, shapeUpArrow, shapeNone ), colorGreen, 0, locationBelowBar );
PlotShapes( IIF( PositionScaleInSignal, shapeHollowUpArrow, shapeNone ), colorLime, 0, locationBelowBar );
PlotShapes( IIF( PositionScaleOutSignal, shapeHollowDownArrow, shapeNone ), colorLime, 0, locationAboveBar );
PlotShapes( IIF( PositionExitSignal, shapeCircle, shapeNone ), colorLime, 0, locationAboveBar );

//_TRACE("Buy="+Buy);

SetCustomBacktestProc("");

if (Status("action") == actionPortfolio)
{
    bo = GetBacktesterObject();	//  Get backtester object
    dt = DateTime();
    bo.PreProcess();	//  Do pre-processing
  
    for (i = 0; i < BarCount; i++)	//  Loop through all bars
    {
      sigcount=symb=ps=0;
   
      
	  formatedDateTime = DateTimeFormat( "%d-%m-%Y %H:%M:%S", dt[i] );
    
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	//  Loop through all signals at this bar
			//sig.Type 1- buy, 2 - sell, 3 - short, 4 - cover, 5 - scale-in, 6 - scale-out 
			sigcount = sigcount + 1;			
  
                
           if((PositionEntrySignal[i] || PositionScaleInSignal[i]) && PositionExitSignal[i] )
           {
             //2 signals on this bar	
			symb=sig.Symbol;			
			sig.Price=-1;

			sig = bo.GetNextSignal( i );// skip the 2nd signal which is the exit signal, we are handling this using low level cbt
			sig.Price=-1;
            }    
                
           if((PositionEntrySignal[i] || PositionScaleInSignal[i]) && PositionScaleOutSignal[i] )
           {
             //1 signal on this bar	
			symb=sig.Symbol;			
			sig.Price=-1;
            }    
        
                
           if(PositionScaleOutSignal[i] && PositionExitSignal[i])
           {
           //2 signals on this bar				
			symb=sig.Symbol;	
			ps=bo.Equity/5;	
			
			_TRACE( "PositionScaleOutSignal[i] && PositionExitSignal[i], sig.type=" + sig.Type);
			sig.Price=-1;
			sig = bo.GetNextSignal( i );// skip the 2nd signal which is the exit signal, we are handling this using low level cbt
			sig.Price=-1;
			
            } 
                
           _TRACE( "sigcount=" + sigcount + ", sig.type=" + sig.Type + ", sig.PosSize="+sig.PosSize+", sig.Price="+sig.Price+" " + formatedDateTime );
 
        }	
        
       	bo.ProcessTradeSignals( i );
	    
	                 
           if((PositionEntrySignal[i] || PositionScaleInSignal[i]) && PositionExitSignal[i] )
           {
           
			entryValue=bo.Cash/2;
			if (PositionEntrySignal[i])
            bo.EnterTrade( i, symb, true, EntryPrice[i], entryValue);       
            
            if (PositionScaleInSignal[i])
			bo.ScaleTrade( i, symb, true, ScaleInPrice[i], entryValue/2);
			
			bo.ExitTrade(i, symb, ExitPrice[i] );
								
			//_TRACE("symb="+symb+", EntryPrice["+i+"]="+EntryPrice[i]+", ps="+ps+" " + formatedDateTime);
			
            }
            
            
             if((PositionEntrySignal[i] || PositionScaleInSignal[i]) && PositionScaleOutSignal[i] )
            {
			entryValue=bo.Cash/2;
			if (PositionEntrySignal[i])
            bo.EnterTrade( i, symb, true, EntryPrice[i], entryValue);       
            
            if (PositionScaleInSignal[i])
			bo.ScaleTrade( i, symb, true, ScaleInPrice[i], entryValue/2 );
			
			//scaleout
			bo.ScaleTrade( i, symb, false, ScaleOutPrice[i], entryValue/2);
			//_TRACE("symb="+symb+", EntryPrice["+i+"]="+EntryPrice[i]+", ps="+ps+" " + formatedDateTime);
			
            }    
            
			 if(PositionScaleOutSignal[i] && PositionExitSignal[i])
            {				
			//scaleout
			//_TRACE("i="+i+", symb="+symb+", ScaleOutPrice["+i+"]="+ScaleOutPrice[i]+" " + formatedDateTime);

			bo.ScaleTrade( i, symb, false, ScaleOutPrice[i], ps);
			
			bo.ExitTrade(i, symb, ExitPrice[i] );
            } 
       
        bo.HandleStops( i );	//  Handle programmed stops at this bar
        bo.UpdateStats( i, 1 );	//  Update MAE/MFE stats for bar
        bo.UpdateStats( i, 2 );	//  Update stats at bar's end
    }	//  End of for loop over bars
    
    bo.PostProcess();	//  Do post-processing
}

1 Like

Your code is all wrong. You are constantly doing wrong things.

You MUST NOT mix low-level (HandleStops/UpdateStats) with mid-level (ProcessTradeSignals).

This is NOT ALLOWED and NOT SUPPORTED.

ProcessTradeSignals INTERNALLY does HandleStops/UpdateStats.
If you call BOTH you are doing many things TWICE which leads to incorrect results.

CLOSELY FOLLOW instructions from the manual AND NEVER EVER "INVENT" your own thing.

Re-read the manual:
https://www.amibroker.com/guide/a_custombacktest.hml

Re-read other docs:
http://www.amibroker.org/userkb/2008/03/16/amibroker-custom-backtester-interface-2/

1 Like

@Tomasz, if i remove these 3 lines then does it make it right?

        bo.HandleStops( i );	//  Handle programmed stops at this bar
        bo.UpdateStats( i, 1 );	//  Update MAE/MFE stats for bar
        bo.UpdateStats( i, 2 );	//  Update stats at bar's end

As I wrote already, you should definitely remove those lines if you are already calling ProcessTradeSignals.

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