Incorrect CBT code, was: Differences between

Hello. I just wonder why BT has different results than CBT. Let's say than only two positions can be opened and stop loss is defined with re-entry delay. Both BT and CBT recognize the re-entry delay, but CBT does not buy another symbol in a row ordered by any position score:

Detail trade log for BT (right):
image

Detail trade log for CBT (wrong, next symbol not bought):
image

It's unlikely anyone will be able to help you unless you post your code. There are many ways to create a CBT.

@Unlimited - In short: your code has a problem. Show your code, if you want help.

Longer version: Unfortunately your question does not provide all necessary details to give you an answer. Please follow this advice: How to ask a good question

Ok, I am sorry, I am a beginner in CBT at all, so I would like to change my question. How can I implement in CBT the re-entry delay? The problem is that for mentioned signal the CBT does not remove the re-entry signal, see my CBT code:

if (UseCBT == True) {
	_TRACE("!CLEAR!");
	SetBacktestMode(backtestRegularRaw);
	SetOption("UseCustomBacktestProc", True);
	SetCustomBacktestProc("");

	if( Status("action") == actionPortfolio )
	{
		bo = GetBacktesterObject();
		bo.PreProcess();
		dt = DateTime();		
	
		for (i = 0; i < BarCount; i++)   
		{			
			OpenedPosition = bo.GetOpenPosQty();
			PosInDay = 0;
			BarDate = DateTimeToStr(dt[i], 3) + ":  ";	
		
			for( sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) ) 
			{
				CurEquity = bo.Equity; 
				
				if (sig.IsEntry()) {				
					if (bo.FindOpenPos(sig.Symbol)) {	
						sig.Price = -1;
						_TRACE(BarDate + "Signal SKIPped due position already opened: " + sig.Symbol); 
					} else if (PosInDay >= EntriesPerDay) {
						sig.Price = -1;
						_TRACE(BarDate + "Signal SKIPped due reach day limit : " + sig.Symbol);
					} else if (OpenedPosition >= MaxPositions) {
						sig.Price = -1;
						_TRACE(BarDate + "Signal SKIPped due no empty slot: " + sig.Symbol + " Price=" + sig.Price + " Slot = " + OpenedPosition + " PosScore = " + sig.PosScore + " Equity = " + bo.Equity);
					} else if (sig.Price > bo.Equity * (AllocPossition/100)) {
						sig.Price = -1;
						_TRACE(BarDate + "Signal SKIPped due low capital: " + sig.Symbol + " Price=" + sig.Price + " Slot = " + OpenedPosition + " PosScore = " + sig.PosScore + " Equity = " + bo.Equity);
					} else {
						// do entry
						OpenedPosition++; 	
						PosInDay++;
						_TRACE(BarDate + "ENTERed Long:" + sig.Symbol + " Price=" + sig.Price + " Slot = " + OpenedPosition + " PosScore = " + sig.PosScore + " Equity = " + bo.Equity); 	
					}		
				} 
			}
			bo.ProcessTradeSignals( i );			
		}
		bo.PostProcess();
	}
}

The output in detail log is as I mentioned:
image

But the _TRACE log shows me different:
image

Means that next signal is skipped due no empty slot. So I need to remove PEP signal in CBT due re-entry delay - how?

In short: just DON'T. DO NOT use Custom backtester for re-inventing the wheel.

Use built-in backtester. Re-entry delay is built-in for stops (ApplyStop). For regular signals it should be implemented in first phase (WITHOUT custom backtester).

The code you posted shows that you have no idea about absolute basics of backtester and the code is totally incorrect and contains stuff that is simply put redundant and does not serve any purpose and only cause your confusion.

  1. You do NOT need to look for open positions and to assign sig.Price = -1. That is wrong and useless. Backtester will NOT open new trade if position is already open. No CBT needed.
  2. Again you don't need to assign sig.Price = -1 if you don't have enough capital. Backtester will NOT open positions if there is not enough cash. If you don't want shrinking positions switch "AllowPositionShrinking" to false. No CBT needed.
  3. You don't need to check if you already opened MaxOpenPositions. Again backtester does that automatically and won't open new pos. No CBT needed.
  4. Logging is wrong (_TRACE in "do entry" stuff). Just because that you left the signal does not necessarily mean that ProcessTradeSignal would open a trade (see above). It may not. ProcessTradeSignal contains a lot of code that rejects signals on many conditions. That is why you are getting wrong log - it is your code that writes wrong (misleading) message that it entered trade, while in fact it did NOT because minimum delay requirement (set in ApplyStop) wasn't met.

Just use normal backtester. Custom backtester is NOT needed for everything you want to do.

2 Likes

Hello Tomasz,
thank you for your post, maybe I am wrong but I feel my code/question getting you annoying. But well, I probably really do not have any idea about BT/CBT... therefore I am asking here..

My first idea was how to implement max opened positions in day -> is it possible to do it without CBT? I suppose no and therefore I used CBT.
Then I started to play with the CBT, e.g. how to remove any signal if I do not want it for any reason. And I thought that the good point is to have exactly the same results as have built-in BT before I start to change anything in the order of trades/signals.

One reason as an example, suppose I am working with EOD and putting orders to a broker before open:
I am buying for a limit price, I have defined MaxOpenPositions and my variable maxOpenPosPerDay (mentioned above). Explorer shows me twice more signals in the day than MaxOpenPositions (sorted by any Position Score). In such case I will put to broker only such amount of opens as corresponds to MaxOpenPositions and until reach maxOpenPosPerDay. Some orders at broker side will be fulfilled during the day, some not - limit price is not reached. How can I do that with built-it BT? Therefore conditions in my posted CBT.
Well, could you advice me, please, how to do it in the proper way? Thank you in advance.

  1. There are always many ways to do anything. You can do one trade/one signal per day without CBT (using static variables) - although it is advised NOT to do this at all for reasons outlined below. Generally it is good idea to generate good signals from the start instead of generating wrong signals and removing them later. CBT can be used for that but it is not the only way.

  2. This was discussed many times on this forum - use search https://forum.amibroker.com/search?q=limit%20orders%20backtester

Generally speaking things like "one trade per day" and opening trades using limit orders make your system open for way too much randomness. They are not stable and not reproducible (you don't know intra-bar timing of entries especially if you use EOD data - you won't be able to reproduce timing effects of orders hitting limits at different times during the day). Robust systems don't need limit orders for entries. Limits and stops should be in your exit strategy.

2 Likes

Q: In other words, if I use simple CBT code to remove signal after reached number of trades per day only (I am sorry, I did not find a way how to do that via static variable):

	SetBacktestMode(backtestRegularRaw);
	SetOption("UseCustomBacktestProc", True);
	SetCustomBacktestProc("");

	if (Status("action") == actionPortfolio) {
		bo = GetBacktesterObject();
		bo.PreProcess();
				
		for (i = 0; i < BarCount; i++) {			
			PosInDay = 0;						
			
			for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i )) {
				CurEquity = bo.Equity; 				
				
				if (sig.IsEntry()) {				
				
					if (PosInDay >= EntriesPerDay) {
						sig.Price = -1;
						//_TRACE(BarDate + "Signal SKIPped due reach day limit : " + sig.Symbol);
					} else {
						PosInDay++;
					}		
				} 
			}
			bo.ProcessTradeSignals( i );			
		}
		bo.PostProcess();		
	}

And where EntriesPerDay == MaxOpenPositions and I got different results between CBT and built-in BT I have a problem in my strategy?