Issue with StaticVarGenerateRanks

I am trying to use StaticVarGenerateRanks to create a dynamic universe of stocks on which I will use additional conditions for entry/exit. However, I am noticing that the back-test includes lower ranked stocks as well. Running a clean back-test with only ranking logic is producing the following results when I'm trying to restrict universe of stocks to top-10 stocks only:

Here is the code I used for the above:

if (Status("stocknum") == 0)
{
	StaticVarRemove("ROC_*");
	StaticVarRemove("Rank_ROC_*");
	
	SymList = CategoryGetSymbols(categoryWatchlist, GetOption("FilterIncludeWatchlist"));
	
	for (i = 0; (Sym = StrExtract(SymList, i)) != ""; ++i)
	{
		SetForeign(Sym);
		
		roc_ = ROC(Close, 21);
		
		RestorePriceArrays();
		
		StaticVarSet("ROC_" + Sym, Max(roc_, 0));
	}  
	
	StaticVarGenerateRanks("Rank_", "ROC_", 0, 1234);
}

rocRank = StaticVarGet("Rank_ROC_" + Name());

scrips = 10;

SetTradeDelays(1, 1, 1, 1);
BuyPrice = SellPrice = Open;
ShortPrice = CoverPrice = Open;

Buy = rocRank <= scrips;
Sell = rocRank > scrips;
Short = Cover = False; 

RoundLotSize = 1;
PointValue = 1;

SetOption("FuturesMode", False );
SetOption("InitialEquity", 1000000);
SetOption("AllowSameBarExit", False );
SetOption("ActivateStopsImmediately", True );
SetOption("AllowPositionShrinking", True );
SetOption("MaxOpenPositions", scrips);
SetOption("CommissionMode", 1 );
SetOption("CommissionAmount", 0.11);
SetOption("AccountMargin", 100);
SetOption("ReverseSignalForcesExit", True );
SetOption("UsePrevBarEquityForPosSizing", True );
SetOption("MCPosSizeMethod", 3);
SetOption("MCPosSizePctEquity", 100/scrips);
SetPositionSize( 100 / scrips, spsPercentOfEquity );

SetCustomBacktestProc( "" );
if ( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject();
    /* Run default backtest procedure without generating the trade list  */
    bo.Backtest( True );
    /* Iterate through closed trades  */
    for ( trade = bo.GetFirstTrade( ); trade; trade = bo.GetNextTrade( ) )
    {
        symbol_rank = StaticVarGet( "Rank_ROC_" + trade.Symbol);
        trade.AddCustomMetric( "Rank", Lookup( symbol_rank, trade.EntryDateTime ) );
    }
    /* Iterate through open positions */
    for ( trade = bo.GetFirstOpenPos( ); trade; trade = bo.GetNextOpenPos( ) )
    {
        symbol_rank = StaticVarGet( "Rank_ROC_" + trade.Symbol);
        trade.AddCustomMetric( "Rank", Lookup( symbol_rank, trade.EntryDateTime ) );
    }
    /* Generate trade list */
    bo.ListTrades( );
}

Analysis Settings:

I am unsure as to why the ranks above 10 are appearing in the trade list of the back-test.

Can someone please point out where I am making a mistake in the code or the settings?

Thanks in advance,
Rakesh

Firstly, you should add debugging code.

I think this is where you are introducing the problem

StaticVarGenerateRanks("Rank_", "ROC_", 0, 1234);
You have asked SVGR to use 1234 mode in which ties don't share equal RANK.

StaticVarSet("ROC_" + Sym, Max(roc_, 0));
Then, you are doing Max( xyz, 0 )

How many bars for so many symbols are getting set 0 because originally, they would have been negative.
So with many symbols having a lot of 0's in the same bar, are you sure your code is working as intended?

( This is my guess as to what might be happening )
Now you set Trade delay, so you are looking up the RANK on trade datetime which is not the rank of the ranking day.

2 Likes

@rakeshpoluri,

@nsm51 is correct. This is the cause of the wrong values you see in the results. In the CBT you should look for the previous bar value (if its index is > 0).

In any case, I suggest also to add a line similar to:

PositionScore = iif (rocRank > 0, 10000 - rocRank, 0); // a bit redundand since you do not generate signals for ranks below X

Otherwise your positions are entered based on the alphabetical order of the watchlist. Use the "Detailed log" option of the "Analysis settings" dialog (Report tab) to understand better what happens when you use it or not.

This was indeed what was causing the issue I was having, so thank you!

I was mainly using SVGR for a dynamic universe, not for ranking as such. The code I provided was a test code written by me to understand how SVGR does ranking.

I will keep this in mind for any strategies that do require ranking.