backtestRegularRawMulti Mode in low-level CBT

I'm trying to understand AFL coding in low-level CBT. Begin with create a basic system in 2 different level, mid and low level CBT in same entry/exit rules and compare the results. As you can see in following code of both level, I use backtestRegularRawMulti as backtest mode for both which multiple positions per symbol will be open when entry signals come and exit all positions if it's exit signal as explained in http://www.amibroker.com/guide/h_portfolio.html

Mid-level code

/////////////////////////////// GLOBAL SETTINGS /////////////////////////////////
SetOption("InitialEquity", 1000000 ); 
SetOption("AllowPositionShrinking", True );
SetOption("MinShares", 1 ); 
RoundLotSize = 1;
SetOption("CommissionMode", 1 );
SetOption("CommissionAmount", 0.25 );
SetOption("MaxOpenPositions", 1000);
BuyPrice = Close;
SellPrice = Close;
SetTradeDelays( 0, 0, 0, 0 );
SetBacktestMode(backtestRegularRawMulti);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////// BUY&SELL CONDITION //////////////////////////////
Buy = Cross(C,Ref(HHV(H,100),-1));
Sell = Cross(Ref(LLV(L,50),-1),C);
Short = 0;
Cover = 0;
////////////////////////////////////////////////////////////////////////////////

////////////////////////////// POSITION SIZING /////////////////////////////////
PositionSize = -5;
////////////////////////////////////////////////////////////////////////////////

SetCustomBacktestProc("");
if(Status("action") == actionPortfolio){
	bo = GetBacktesterObject();
	bo.PreProcess();
	for(i=0; i<BarCount; i++)
	{
		for(sig=bo.getfirstsignal(i); sig; sig=bo.getnextsignal(i))
		{
		}
		bo.processtradesignals(i);
	}
	bo.postprocess();
}

Low-Level code

/////////////////////////////// GLOBAL SETTINGS /////////////////////////////////
SetOption("InitialEquity", 1000000 ); 
SetOption("AllowPositionShrinking", True );
SetOption("MinShares", 1 ); 
RoundLotSize = 1;
SetOption("CommissionMode", 1 );
SetOption("CommissionAmount", 0.25 );
SetOption("MaxOpenPositions", 1000);
BuyPrice = Close;
SellPrice = Close;
SetTradeDelays( 0, 0, 0, 0 );
SetBacktestMode(backtestRegularRawMulti);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////// BUY&SELL CONDITION //////////////////////////////
Buy = Cross(C,Ref(HHV(H,100),-1));
Sell = Cross(Ref(LLV(L,50),-1),C);
Short = 0;
Cover = 0;
////////////////////////////////////////////////////////////////////////////////

////////////////////////////// POSITION SIZING /////////////////////////////////
PositionSize = -5;
////////////////////////////////////////////////////////////////////////////////

SetCustomBacktestProc("");
if(Status("action") == actionPortfolio){
	bo = GetBacktesterObject();
	bo.PreProcess();
	for(i=0; i<BarCount; i++)
	{
		for(sig=bo.getfirstsignal(i); sig; sig=bo.getnextsignal(i))
		{
			if (sig.isEntry() AND sig.IsLong()) 
			{ 
				bo.EnterTrade(i, sig.symbol, sig.IsLong, sig.Price, sig.PosSize, sig.PosScore,sig.RoundLotSize); // Enter trade
			}
			else
			{
				if (sig.isExit())
				{
					bo.ExitTrade(i,sig.symbol,sig.Price);
				}
			}

		}
		bo.HandleStops(i);
		bo.UpdateStats(i,1);
		bo.UpdateStats(i,1);
	}
	bo.postprocess();
}

Problem is that the output from my low-level isn't correct :frowning:, only first open position exit when the sell signal comes but the rest multiple positions have been hold until last date of testing. I've attached here the trade list result as below.

Mid-level result (Correct one)

Low-level result (Wrong one)

Could you please guide me out here what's wrong in my code and my understanding :cry:. Thank you in advance for your help.

“Your” code is incorrect (many errors for example if-else in side CBT is simply wrong).
I know you copy-pasted that from http://www.amibroker.org/userkb/2008/03/16/amibroker-custom-backtester-interface-2/ without giving proper credit and modified introducing new errors (for example incorrect calls to HandleStops).

My advise is do not touch low-level CBT unless you are equally skilled in programming as Chopin was with playing the piano. At that level there is no room for copy-pasting.

Hello,

I don’t want to override Tomasz notes regarding this post… these are just general guidelines which I think are useful for all the users starting with CBT, since when starting out it’s easy to get stuck with doubts.

I think it’s really important to start by reading most of the documentation available:

I think that, probably one of the best ways to learn is to follow along these examples and use the Trace Function (http://www.amibroker.com/guide/afl/_trace.html ) in several parts of your code to see what is happening “under the code”.
I don’t think you need extensive programming experience to use mid/low level CBT… but for those users which don’t have a programming background (like me), I think the best way to learn is by testing a lot of examples and see the results. This exercise will help to understand how the backtester object model works.
After following these examples, the questions which will naturally arise, will be more specific.
I’m no expert, and usually when I start doing something in CBT, I have a few questions but most of them I answer by using Trace command.
If, after several trial and error approaches there’s still something I don’t understand, I ask here in the forum or AB support.

Regarding your specific question, and since you have enabled RawMulti backtest mode, you need to loop through each open position at each exit signal, something like this:

SetCustomBacktestProc("");

if (Status("action") == actionPortfolio)
{
    bo = GetBacktesterObject();	//  Get backtester object
    
    bo.PreProcess();	//  Do pre-processing
    
    for (i = 0; i < BarCount; i++)	//  Loop through all bars
    {
        
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	//  Loop through all signals at this bar
			
			if (sig.IsEntry() ) 
            {
				bo.EnterTrade( i, sig.Symbol, sig.IsLong(), sig.Price,sig.PosSize );
            }
			
			if ( sig.IsExit() )
            {    
                for ( openpos = bo.GetFirstOpenPos(); openpos; openpos = bo.GetNextOpenPos() ) 
				{ 
					bo.ExitTrade( i, sig.Symbol, sig.Price );
				}
			}
        }	
        
        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
}

11 Likes

Fantastic response @pmxgs ! You are right that extensive programming experience is not really needed for CBT. What is really required is patience and attention to detail. And you definitely have both those qualities.

Responses like yours prove that setting up this forum was good idea.

2 Likes

Thanks @Tomasz.

I just wanted to share this, because I felt some “newbie” users could be a bit discouraged by your Chopin analogy :slight_smile:

I agree with you. This forum has proved to be a good idea and it is only just starting… Thanks for setting up the AB forum.

Well, my daugther plays the guitar. She was studying playing the guitar in 6 year musical school (in addition to ‘regular’ school). I was watching her starting with picking one string. The teacher insisted on just picking single string for 3 weeks. Just watching the movement and shape of hand and taking care that they are both correct. Then another and then another. Only when she was able to pick correctly individual strings, they proceeded to picking 2 strings at once. The songs were brain-dead easy. It took months. One may think it is absurd, but it is not. This has built the foundation that flourished in amazing playing ability later. Music teachers know that you can not speed things up. You have to practice slowly. On simple pieces. If you want to play the guitar correctly you have to take things slow. Don’t expect miracles after month or a year.
If you are patient enough and if you practice you will get there… after 4-5 years your playing ability would be worth listening. After 6 years it will be enjoyable.

Now my son plays violin… don’t get me started :slight_smile:

Programming (especially AFL) is way easier than playing the guitar at that level. Believe me. But it still takes time and patience to learn.

5 Likes

:slight_smile:

Agree 100%, time and patience are essential.

Dear @Tomasz and @pmxgs,

Many thanks to both of you for wasting your time reply and teach me lots of thing. I’ll be patient, keep learning and practice AFL from the basic as advised. :slight_smile: