Objective: Create Buy/Hold with Rebalance Performance Benchmark of basket of tickers, where # active of tickers time varies.
Current Approach: StaticVarAdd # of tickers with filter on active. Rotation Trade with CBT example of rebalance.
Error: I believe error is sourced by the current approach in CBT expects a static value not an array based # of holdings (EachPosPercent). Any guidance on how to work around this problem to achieve this objective is greatly appreciated.
Background: In evaluating a quant strategy on a basket of securities, a best practice technique is to the benchmark the performance of this strategy against simply holding all the securities and periodically rebalancing. This version of the code does that (current using annual rebalance, but can easily be done with monthly, etc.). The challenge is that makes this harder than “standard” example solutions is that many baskets (watchlists) of tickers do not exist of the entire length of the backtest, so simply counting # of tickers as a fixed answer creates a process that does not keep yield fully exposure nor rebalanced.
Code Below.
_SECTION_BEGIN("Setup Options");
formn = ParamStr( "set name", "Eq Wtg Rebal" );
SetFormulaName(formn);
SetBacktestMode( backtestRotational );
SetOption("InitialEquity",1000000);
SetOption( "CommissionMode", 3 ); /* 3 set commissions $ per share; 1=% of trade */
comamt =Param("comamt shr", .01,.0,.1,.01); // sw more conservative ver
SetOption( "CommissionAmount", comamt ); /* per share + SLIPPAGE */
SetOption("AccountMargin",100);
SetOption("AllowPositionShrinking", True);
RoundLotSize = 1;
RModeN = Param("0=Trade,1=det,2=sum,3=no", 1,0,3,1);
SetOption("PortfolioReportMode", RModeN );
_SECTION_END();
DBVOn = ParamToggle("DebugView","OFF|ON", 0);
// --- detect watchlist ---
wlnumber = GetOption( "FilterIncludeWatchlist" );
watchlist = GetCategorySymbols( categoryWatchlist, wlnumber );
numberOfSymbols = StrCount( watchlist, "," ) + 1;
NoS = numberOfSymbols;
SetOption("MaxOpenPositions", NoS);
ActiveTicka =ActiveTicka2=SmpleadjCt=SmpleadjCtx =0;
if ( Status( "stocknum" ) == 0 )
{
{
wlist = GetOption("FilterIncludeWatchlist");
symlist = CategoryGetSymbols( categoryWatchlist, wlist ) ;
StaticVarRemove("*"); // delete static variables
}
for( i = 0; ( symbol= StrExtract( symlist, i ) ) != ""; i++ )
{
SetForeign(symbol);
Symctact = iif(isNull(c),0,iif(c>0.01,1,0)); // Filter on active ticker
if( DBVOn ) _TRACE("Symctact p1= " + Symctact);
StaticVarAdd( "~Symctact", Symctact );
if( DBVOn ) _TRACE("Symctact vadd= " + Symctact);
StaticVarAdd( "~SymbolCount", 1 );
RestorePriceArrays();
}
}
SmpleadjCtx = staticvarget("~Symctact"); // Using active tickers
Symbol = Name();
PositionSize = nz(-100/SmpleadjCtx); // Equally divide capital among the positions held - works
EachPosPercent = 100/SmpleadjCtx;
BuyPrice = C;
SellPrice = C;
Firstdayoftheyear = year()!= ref(year(),-1);
Lastdayoftheyear = year()!= ref(year(),1);
dt = DateTime();
Start = dt[ 0 ] ;
buyok = (Firstdayoftheyear OR Start) and c>0;
Rebal = (Lastdayoftheyear OR Start) and c>0;
_SECTION_BEGIN("trade");
PScore = ROC( C, 1 );
PositionScore = IIf(Rebal, PScore, scoreNoRotate) ;
_SECTION_END();
//
Filter = buyok OR Rebal;
AddColumn( C , "C" , 3.2 );
AddColumn( NoS , "numberOfSymbols" , 3.0 );
Addcolumn(StaticVarGet ("~SymbolCount"), "~SymbolCount", 3.0);
Addcolumn(StaticVarGet ("~Symctact"), "~Symctact"); // works
AddColumn( SmpleadjCtx , "SmpleadjCtx" , 3.0 ); // works
AddColumn( PositionSize , "PositionSize" , 3.0 );
SetOption("UseCustomBacktestProc", True );
if( Status("action") == actionPortfolio )
{
bo = GetBacktesterObject();
bo.PreProcess(); // Initialize backtester
for(bar=0; bar < BarCount; bar++)
{
bo.ProcessTradeSignals( bar );
CurEquity = bo.Equity;
for( pos = bo.GetFirstOpenPos(); pos; pos = bo.GetNextOpenPos() )
{
posval = pos.GetPositionValue();
diff = posval - 0.01 * EachPosPercent * CurEquity;
price = pos.GetPrice( bar, "O" );
if( diff != 0 AND
abs( diff ) > 0.005 * CurEquity AND
abs( diff ) > price )
{
bo.ScaleTrade( bar, pos.Symbol, diff < 0, price, abs( diff ) );
}
}
}
bo.PostProcess(); // Finalize backtester
}