Difference between backtest and explore ranked stocks

Hello,

I want to rank stocks by coppock, and then buy that stocks at the beginning of the month ordered by the ranking. The problem that I have is when I compare the "explore" stocks with the "backtest" buyed stocks are different. I am trying to figure out what is happening, but no idea...I think that I have some concept wrong...

// ################ START SETUP ######################################  
PosQty = 10;										
SetOption("MaxOpenPositions", PosQty);				
PositionSize = 100/PosQty;							
SetPositionSize( PositionSize, spsPercentOfEquity );
SetTradeDelays(1,1,1,1);
SetOption("initialequity",10000);
BuyPrice = O;
SellPrice = O;
// ################ END SETUP #########################################

// ################ START Ranking #####################################

// watchlist should contain all symbols included in the test
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, wlnum ) ;

if( Status( "stocknum" ) == 0 )
{
    // cleanup variables created in previous runs (if any)
    StaticVarRemove( "rank*" );
    StaticVarRemove( "values*" );
    categoryList = ",";

    for( n = 0; ( Symbol = StrExtract( List, n ) )  != "";  n++ )
    {
        SetForeign( symbol );

        // START CODE CRITERIA
        // COPPOCK CRITERIA BASED RANKING
        WMAPeriods	=Param("WMA periods",10,2,200,1,0);
		ROC1Periods =Param("ROC1 periods",14,2,200,1,0);
		ROC2Periods =Param("ROC2 periods",11,2,200,1,0);
		Coppock=WMA((ROC(C,ROC1Periods)+ROC(C,ROC2Periods)),WMAPeriods);
        values = Coppock;
        
        // END CODE CRITERIA

        RestorePriceArrays();

        // write ranked values to a static variable
        StaticVarSet( "values_" + symbol, values );

    }

    // generate  ranks
    StaticVarGenerateRanks( "rank", "values_", 0, 1224 );
    
}

symbol = Name();
values = StaticVarGet( "values_" + symbol );	//Coppock criteria based ranking value
rank = StaticVarGet( "rankvalues_" + symbol );	//from 1 to end, ranking the top

// exploration code for verification
if( Status( "action" ) == actionExplore )
{
	AddColumn( values, "values" );
	AddColumn( rank, "rank" );
	Filter = rank <= 10;
	SetSortColumns(-2, 4);
}

//For the backtest. Start from the stocks that have more coppock critera based ranking
PositionScore = values;

// ################ END Ranking ########################################################


// ################ START BUY/SELL #####################################################

BuySignal  = 1;//(Close > SAR(0.002,0.2));	
SellSignal = (Month() != Ref(Month(), -1)); 

Buy  = ExRem(BuySignal, SellSignal);
Sell = ExRem(SellSignal, BuySignal);

// trade on next bar open
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = SellPrice = Open; 

// ################ END BUY/SELL #########################################################


Thanks for all the help!
Iaki

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?

Use Detailed Log.

1 Like

@iaki, as suggested by @Tomasz use the detailed log to check the position score of the entries vs. your exploration results, after changing this line (since you are delaying your Buy operations) :

PositionScore = Ref(values, -1);
1 Like

Hi Tomasz and Beppe,

First of all, thank you very much for your help and time — I really appreciate it.
I tried both of your suggestions, but unfortunately, I'm still getting the same results.

I have added a few lines to the code to inspect the value of PositionScore during the test, and I noticed that the value is negative at the point where it seems to be making unexpected purchase decisions.

I believe that instead of buying "LUMN", "YELLQ", and "FRCB", it should be selecting "CAR", "MUR", and "X". Could I be mistaken in this assessment?



Modified code:

// ################ START SETUP ######################################  
PosQty = 10;										
SetOption("MaxOpenPositions", PosQty);				
PositionSize = 100/PosQty;							
SetPositionSize( PositionSize, spsPercentOfEquity );
SetTradeDelays(1,1,1,1);
SetOption("initialequity",10000);
BuyPrice = O;
SellPrice = O;
// ################ END SETUP #########################################

// ################ START Ranking #####################################

// watchlist should contain all symbols included in the test
wlnum = GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categoryWatchlist, wlnum ) ;

if( Status( "stocknum" ) == 0 )
{
    // cleanup variables created in previous runs (if any)
    StaticVarRemove( "rank*" );
    StaticVarRemove( "values*" );
    categoryList = ",";

    for( n = 0; ( Symbol = StrExtract( List, n ) )  != "";  n++ )
    {
        SetForeign( symbol );

        // START CODE CRITERIA
        // COPPOCK CRITERIA BASED RANKING
        WMAPeriods	=Param("WMA periods",10,2,200,1,0);
		ROC1Periods =Param("ROC1 periods",14,2,200,1,0);
		ROC2Periods =Param("ROC2 periods",11,2,200,1,0);
		Coppock=WMA((ROC(C,ROC1Periods)+ROC(C,ROC2Periods)),WMAPeriods);
        values = Coppock;
        
        // END CODE CRITERIA

        RestorePriceArrays();

        // write ranked values to a static variable
        StaticVarSet( "values_" + symbol, values );

    }

    // generate  ranks
    StaticVarGenerateRanks( "rank", "values_", 0, 1224 );
    
}

symbol = Name();
values = StaticVarGet( "values_" + symbol );	//Coppock criteria based ranking value
rank = StaticVarGet( "rankvalues_" + symbol );	//from 1 to end, ranking the top

// exploration code for verification
if( Status( "action" ) == actionExplore )
{
	AddColumn( values, "values" );
	AddColumn( rank, "rank" );
	Filter = rank <= 10;
	SetSortColumns(-2, 4);
}

//For the backtest. Start from the stocks that have more coppock critera based ranking
PositionScore = Ref(values, -1); //values;

// ################ END Ranking ########################################################


// ################ START BUY/SELL #####################################################

BuySignal  = 1;//(Close > SAR(0.002,0.2));	
SellSignal = (Month() != Ref(Month(), -1)); 

Buy  = ExRem(BuySignal, SellSignal);
Sell = ExRem(SellSignal, BuySignal);

// trade on next bar open
SetTradeDelays( 1, 1, 1, 1 );
BuyPrice = SellPrice = Open; 

// ################ END BUY/SELL #########################################################

StaticVarSet("PositionScore" + Name(), PositionScore); // Store PositionScore for each symbol
SetCustomBacktestProc(""); 
if( Status("action") == actionPortfolio ) 
{ 
	bo = GetBacktesterObject(); 
	bo.Backtest(True); // run default backtest procedure 

    for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade())
    {
		// iterate through closed trades
		PosScore 		= StaticVarGet("PositionScore" + trade.Symbol); // Recall stored PositionScore for this trade's symbol
		PosScoreAtEntry = Lookup(PosScore, trade.EntryDateTime); // Find the value of PositionScore at the time of trade entry
		trade.AddCustomMetric("PosScore At Entry", PosScoreAtEntry); // Output it to an extra column on the trade list
    }
    
        // iterate through open positions
    for ( trade = bo.GetFirstOpenPos( ); trade; trade = bo.GetNextOpenPos( ) )
    {       
		PosScore 		= StaticVarGet("PositionScore" + trade.Symbol); // Recall stored PositionScore for this trade's symbol
		PosScoreAtEntry = Lookup(PosScore, trade.EntryDateTime); // Find the value of PositionScore at the time of trade entry
		trade.AddCustomMetric("PosScore At Entry", PosScoreAtEntry); // Output it to an extra column on the trade list
    }
    
    bo.ListTrades();

}

I truly appreciate your help and guidance, as I’m still new to this.
Best regards,
Iaki

Hi Tomasz and Beppe,

I have finally figured out what is happening. AmiBroker uses the absolute value of the PositionScore, meaning it disregards the sign (Link: Custom Backtester Interface converts sig.PosScore to positive numbers).

Therefore, I believe the correct PositionScore should be the following:

PositionScore = IIF(values>0,values,0);

Thanks for all your help, and I really appreciate your work.
Best regards,
Iñaki

Yes it does.

Excerpt from Portfolio-level back testing

USING POSITION SCORE

You can use new PositionScore variable to decide which trades should be entered if there are more entry signals on different securities than maximum allowable number of open positions or available funds. In such case AmiBroker will use the absolute value of PositionScore variable to decide which trades are preferred.

1 Like