Here I think is a properly working example. It ranks according to RSI(14), then buys only if higher than the desired rank AND if the rank has improved in the last month. Buy/sell monthly.
The position size is set based on the number of entries . It looks to work, but test for yourself to make sure.
What I am still not sure of is how I would handle this if I don’t buy/sell all positions each month, and then have some positions that remain open. In that case, I would want to set the position size of the new entries to be divided evenly among available capital. I tried to make that work without success.
/*
Example of adjusting position size based on number of current signals
*/
SetCustomBacktestProc("");
// custom backtest procedure to adjust position size
if ( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject();
bo.PreProcess();
for ( bar = 0; bar < BarCount; bar++ )
{
Cnt = bo.GetSignalQty(bar, 1); // get entries
_TRACEF("Bar: %g, Cnt: %g", bar, Cnt);
// set the position size based on number of entries + open positions
for ( sig = bo.GetFirstSignal( bar ); sig; sig = bo.GetNextSignal( bar ) )
{
if (sig.IsEntry())
sig.PosSize = -100/Cnt;
}
bo.ProcessTradeSignals( bar );
}
bo.PostProcess();
}
NumberHeld = Param("# of Positions", 2, 1, 100, 1);
// The following block gets all the filter values and does the ranking
// only need to do the calculations once -- so do for the first stock
// read in all symbols
Mainlistnum = GetOption( "FilterIncludeWatchlist" );
Mainlist = GetCategorySymbols( categoryWatchlist, Mainlistnum );
NumSymbols = 0;
if (Status("stocknum") == 0) {
// delete static variables
StaticVarRemove("*ValuesToSort*");
for (i = 0; (sym = StrExtract(Mainlist, i)) != ""; i++ ){
NumSymbols++;
// make the symbol be the 'current' symbol (so the OHLC arrays can be used directly)
SetForeign(sym);
filterScore = RSI(14);
StaticVarSet("ValuesToSort_filter" + sym, IIf(IsEmpty(filterScore), -100, filterScore));
// restore price arrays to the active symbol
RestorePriceArrays();
}
// Generate ranks. resulting ranks will be held in the static var set "RankValuesToSort_filter1'sym'"
StaticVarGenerateRanks("Rank", "ValuesToSort_filter", 0, 1224); // normal rank mode
}
// get this symbol's rank from the Rank array
SymRank = StaticVarGet("RankValuesToSort_filter" + Name());
SetOption ("MaxOpenPositions", NumberHeld);
SetOption ("AllowPositionShrinking", True);
SetOption ("HoldMinDays", 1);
PositionSize = -100/NumberHeld; // Equally divide capital among the positions held
// see if rank improved since 21 bars; if not, then don't buy
RankImprove = IIf(SymRank <= Ref(SymRank, -21), 1, 0);
// rebalance monthly
rebalance = Month() != Ref(Month(), -1);
Buy = rebalance AND (SymRank <= NumberHeld AND RankImprove);
Sell = rebalance;
// Exploration output.
Filter = 1;
AddColumn(Buy, "Buy", 1.0);
AddColumn(C, "Close", 2.1);
AddColumn(SymRank, "Sym Rank", 2.0);
AddColumn(StaticVarGet("RankValuesToSort_filter" + Name()), "Rank", 2.0);
AddColumn(StaticVarGet("ValuesToSort_filter" + Name()), "filterScore", 2.2);