Can PositionScore be used or changed within CBT?

Trying to use different rules for PositionScore based on signal criteria. Not sure what's possible within CBT.

My attempt to code this is listed below. The CBT code appears to have no impact on the strategy (it's not working).

Any guidance you can offer would be greatly appreciated.

//S - Test System for CBT


NumPos = 14; 	//Number of allowed positions

//INPUTS
MA1_len = 20;
MA2_len = 30; //Optimize ("CT exit", 0, 0, 2, 1);
ExitRank = 50; //Optimize("ExitRank", 50, 50, 150, 5);

SetPositionSize(100/NumPos,spsPercentOfEquity);
SetOption( "MaxOpenPositions", NumPos );
EnableRotationalTrading();
SetOption("AllowPositionShrinking", True ); 
SetOption("WorstRankHeld", ExitRank);
SetTradeDelays(1,1,1,1);
BuyPrice = Open;
SellPrice = Open;

MA1 = MA(C,MA1_len);
MA2 = MA(C,MA2_len);

CT_buy = 	MA1 > Ref(MA1, -1) AND
			MA2 > Ref(MA2, -1);

CT_hold = MA2 > Ref(MA2, -1);

//Calculate momentum for ranking positions that meet trend criteria
MOMO = ( ROC(C,6) + ROC(C,13) + ROC(C,26) + ROC(C,52)) /4;

PositionScore = IIf( CT_buy, Max( 1000 + MOMO, 0 ), 0); 

/* ***************************************************************
	START CBT to adjust PositionScore for new entries AND for existing
		positions.
*/
SetCustomBacktestProc("");

if (Status("action") == actionPortfolio) 
{
    bo = GetBacktesterObject();	//  Get backtester object
    bo.PreProcess();	//  Do pre-processing (always required)

    for (i = 0; i < BarCount; i++)	//  Loop through all bars
    {
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
        {	
              if(sig.IsEntry() )
              {
					PositionScore = IIf( CT_buy, Max( 1000 + MOMO, 0 ), 0);
              }
              
              if(sig.IsLong() )
              {
					PositionScore = IIf( CT_hold, Max( 1000 + MOMO, 0 ), 0);
              }
        }	
			
        
        bo.ProcessTradeSignals( i );	//  Process trades at bar (always required)
    }	  
      
    bo.PostProcess();	//  Do post-processing (always required)
}


PositionScore array is writable in FIRST phase of backtest. It is used to SORT signals based on score values, before CBT starts. Changing it later makes no sense, as it is already AFTER sorting.

Ok. This is helpful.

It seems that building code to sort all possible trades within CBT then would be the only way to assign ranking values to new entry signals and existing positions if there are different conditions for a new entry and an existing position.

Would you agree, Tomasz?

Thanks so much for your input.

You seem to be trying to do ROTATIONAL mode with different exit rules for existing positions.
That is already available out-of-the-box from EnableRotationalTrading.
The exit rank for existing positions can be DIFFERENT than entry rank. This is done by "WorstRankHeld" and "MaxOpenPositions".

The "MaxOpenPositions" is the allowable rank for entries. But "WorstRankHeld" can be greater than that and existing position would not be exited unless it drops out of "WorstRankHeld".

https://www.amibroker.com/guide/afl/enablerotationaltrading.html

Thanks for your attention and the killer program, Tomasz!

What I'm actually trying to accomplish is ranking on momentum using the same metric but treating new entries differently than existing holdings. I gave an example earlier but a simple one might be like this:

entry signal: RSI(2) below 20 in the last 15 bars AND MA(C,10) > MA(C, 30)

hold signal: MA(C,10) > MA(C, 30)

A "hold" in the example above is the condition required to continue to hold an existing position. So two different situations (entry, hold) but use Rotational Trading and the same momentum metric to score each.

I've read a lot and tinkered with various ideas so far but can't figure out how to get there.

In that case you don't need rotational at all. And you don't need CBT. Use plain and simple regularBacktest.

Buy = Sum ( RSI( 2 ) < 20, 15 ) == 15 /* RSI2 below 20 in last 15 bars */ 
          AND MA( C, 10 ) > MA( C, 30 );
Sell = Cross( MA( C, 30 ), MA( C, 10  ) );

PositionScore = ..whatever you wish..

In regular mode PositionScore is only used for entries.
In regular mode it won't exit already opened position, no matter what PositionScore is, until you get Sell signal.

1 Like