Help for general approach to lavoid same bar entry in the portfolio case

Hello,

I am running a portfolio backtest with entries on a limit order and exits on close. Occasionally, I get an exit and entry on the same bar, which I would like to avoid. Using SettlementDelay does not work as I use dynamic position sizing and quite often cash is available to handle a same bar exit and entry.

Before I go an a wild goose chase using the mid-level custom backtesting approach (for the first time), I am looking for comments on the way I think this should be handled. Reading through the manual, my general approach would be as follows:

  1. Pre-Process
  2. Loop through all bars performing the following steps:
    • Check for an open position via GetFirstOpenPosition
    • If any open position get signals and modify any sig.IsEntry = True to sig.IsEntry = False (not sure
      if I can do this?!)
    • ProcessTradeSignals for the current bar
  3. Post-Process

Would really appreciate if anybody could validate this general approach or point out any inherent flaws in my understanding how the mid-level works.

Thanks a lot,

Robert

SetOption("MaxOpenPositions", 5);
SetPositionSize(1, spsShares);
SetSortColumns(3);

m = MA( Close, 20 );
Buy = Cross( Close, m ); 
Sell = Cross( m, Close ); 
Short = Cover = 0;

ApplyStop( stopTypeNBar, stopModeBars, 5, True ); 

/// @link https://forum.amibroker.com/t/help-for-general-approach-to-lavoid-same-bar-entry-in-the-portfolio-case/26891/2
SetCustomBacktestProc("", 1);
if( Status("action") == actionPortfolio )  {	
	bo = GetBacktesterObject(); 
	bo.PreProcess();

	for (i = 0; i < BarCount; i++) {		
		cnt_sigex = 0;     
		for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i)) {
			if (sig.IsExit()) {
				cnt_sigex++;
			}
		}		
		for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i)) {
			// Cancel signal if there are exit signals at same bar
			if (sig.IsEntry() AND cnt_sigex > 0) {
				sig.Price = -1;
			}
		}
        
		bo.ProcessTradeSignals( i );
	}    
	bo.PostProcess();
}

Thanks a lot for the code. But I am surprised that this should work only by manipulating signals.

Here is a simple example showing why this may not work - as long as I have understood the logic of course. It shows buy and sell signals for two symbols and 15 bars. Only one position is allowed. On bar 3, the buy for symbol2 is correctly suppressed and the next buy occurs on bar 4 for symbol 1. The next buy is for symbol2 on bar 7 with exit on bar 9. On bar 12 there is a buy on symbol2 which should be taken as there is no open trade, but it will be suppressed by the sell on symbol1!

BAR SYM1 SYM2
1 B
2
3 S B
4 B
5 S
6 S
7 B
8 B
9 S
10
11
12 S B
13
14
15 S

This is why I was thinking that I must somehow know on each bar if a trade is open or not. But, as always, I could be missing something ...

Have you tried it?
It does not seem to be the case.
First try the meal then talk about the meal being tasty or not.

First set
SetCustomBacktestProc("", 0);

Then set
SetCustomBacktestProc("", 1);

Then compare results (check Detailed log).

But as alternative here is version with additional check for open positions.

SetOption("MaxOpenPositions", 5);
SetPositionSize(1, spsShares);
SetSortColumns(3);

m = MA( Close, 20 );
Buy = Cross( Close, m ); 
Sell = Cross( m, Close ); 
Short = Cover = 0;

ApplyStop( stopTypeNBar, stopModeBars, 5, True ); 

/// @link https://forum.amibroker.com/t/help-for-general-approach-to-lavoid-same-bar-entry-in-the-portfolio-case/26891/4
SetCustomBacktestProc("", 1);
if( Status("action") == actionPortfolio )  {	
	bo = GetBacktesterObject(); 
	bo.PreProcess();

	for (i = 0; i < BarCount; i++) {		
		cnt_sigex = 0;   
		for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i)) {
			if (sig.IsExit() ) {
				if (bo.FindOpenPos(sig.Symbol)) {
					cnt_sigex++;
				}
			}
		}		
		for (sig = bo.GetFirstSignal(i); sig; sig = bo.GetNextSignal(i)) {
			// Cancel signal if there are exit signals at same bar
			if (sig.IsEntry()) {
				if ( cnt_sigex > 0 ) {
					sig.Price = -1;
				}
			}
		}
        
		bo.ProcessTradeSignals( i );
	}    
	bo.PostProcess();
}

Tried it. For my specific trading rules I get:

  • No CustomBackTestProc: 460 trades
  • CustomBackTestProc and signal cleanup only: 406 trades (and no entries on exit days)
  • Custom BacktTestProc and signal cleanup with position checking: 420 trades (and no entries on exit days)

So, it looks like position checking is needed. Just doing signal cleanup removes more trades than needed and those missed trades are pretty much invisible unless you do a detailed debug.

Thanks!

For clarification:

When I say "signal cleanup only" I am using the first posted code. Then when I say "signal cleanup with position checking" I am using the second posted code.

This also works. Again, only for a single position. If MaxOpenPositions > 1 one would have to check the total number of positions. If = MaxOpenPositions cancel all entry signals. If not, only keep as many entry signals as positions to be filled, ranking by position score. Much more complex to implement.

SetOption("UseCustomBacktestProc", True);
if( Status("action") == actionPortfolio )  
{	
	bo = GetBacktesterObject(); 
	bo.PreProcess();

	for (bar = 0; bar < BarCount; bar++) 
	{			
		// Cancel all entry signals if an open position is found
		if ( bo.GetOpenPosQty() > 0 )
		{
			for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar) )
			{
				if ( sig.IsEntry() ) sig.Price = -1;
			}
		}
        
		bo.ProcessTradeSignals(bar);
	}
	
	bo.PostProcess();
}

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