I gave up on rotational backtest mode. There are a couple of things I’m facing difficulties with. As a result, and in need of forcing individual position exits, I used the regular backtest mode, but to create a ranking with “STATICVARGENERATERANK”.
here is how it goes:
all symbols go into watchlist 0. Leave the index out of this watchlist.
if I run my analysis several times, my results vary. I have no “random()” in my code so this is a surprise.
If someone would be able to help me understand this irregularity, it’s be great.
You can provoke the error by going to line 101 and change “stocknum” to another value, say 1, 2, 3, 4, you choose.
Then you send the code to AA and click backtest a couple of times.
It can be observed that the results change. Why?
/* System Description:
As described by Clenow, Stocks on the Move, 2015
A very fine read, go n get it.
1. Market regime filter:
Snp500Index > SMA(C,200)
2. Ranking stocks:
2.1 Exponential regression slope over the past 90 days, annualized to 250 trading days - Unit [%]
2.2 Ensure smoothness of slope by calculating the coefficient of determination, R2 ("R Squared") - Value Range [0: worst fit ... 1: best fit]
2.3 Gauge for momentum: 2.1 * 2.2
3. Stock volatility consideration: ATR(20)
4. Additional Filter 1: Individual stock needs to trade above the 100 day SMA - C > SMA(C,100)
5. Additional Filter 2: Avoid stocks with gaps - no move larger than 15% over the past 90 days
6. Position Sizing: Risk Parity Approach - #ofshares = (Equity * RiskFactor) / ATR(20)
Suggested 0.1% daily impact on total equity: Equity * 0.001 / ATR(20)
7. Open position Rebalancing: Adjust position size to current ATR(20) if threshold "thr" exceeds a value. Rebalance 1 or 2 times per month.
8.1 Exits - Portfolio Rebalancing: Close below 100 day SMA - C<SMA(C,100)
8.2 Exits - Portfolio Rebalancing: Stock no longer in the top 20% ranking (SnP500 = 500 stocks, top 20% = drops out of the best performing 100 stocks)
8.3 Exits - Portfolio Rebalancing: Stock dropped out of index
Logic:
* Buy as many stocks as your Equity allows, each pos has a suggested daily impact of 0.1% of portfolio
* prefer the highest ranked until out of cash
* no buy: GAP >15%
* no buy: C<SMA(C,100)
* no buy: SnP500, close < SMA(SnP500,close, 200)
* rebalance position 1 or 2 times per month
* rebalance portfolio on Wednesday: (i.e. exit or open new pos) based on top20%, close below 100day SMA, or gap larger than 15%, or drop out of index
*/
PositionRisk = 1; // allocate 0.1% of equity on mulit*ATR(20) range
filterperiod = 200;//Optimize("Regime Filter Period",200,10,250,10);// // Market Regime Filter Period
ExpRegperiod = gapperiod = 90;//Optimize("Mom + R2 period",90,50,200,10); // Exp Reg Period
//gapperiod = Optimize("Gap Period",90,5,100,1); // lookback period for gaps
gapsize = 15;//Optimize("Gap Size %",15,5,30,1); // gapsize in %
maperiod = 100;//Optimize("MA-period",100,20,200,5);
rankheld = 100;//Optimize("Rank Held",100,20,200,5);
RiskPerContract = ATR( 20 );
PctSize = PositionRisk / RiskPerContract;
SetOption("maxopenpositions",100);//
SetPositionSize( PctSize, spsPercentOfEquity );
//SetBarsRequired(200);//
SetOption("NoDefaultColumns",True);//
SetOption("InitialEquity",100000);//
SetOption("AllowSameBarExit",True);//
SetOption("ActivateStopsImmediately",true);//TRUE when trading on OPEN
SetOption("AllowPositionShrinking",False);//
SetOption("FuturesMode",false);//
SetOption("AccountMargin",100);//
SetOption("InterestRate",0);//
SetOption("MinShares",1);//
SetOption("MinPosValue",0);//
SetOption("PriceBoundChecking", False);//
SetOption("CommissionMode",1);// 0 - use portfolio manager commission table 1 - percent of trade 2 - $ per trade 3 - $ per share/contract
SetOption("CommissionAmount",0.1);//
SetOption("ReverseSignalForcesExit", False);//
SetOption("UsePrevBarEquityForPosSizing", True);//
SetOption("PortfolioReportMode", 0);// 0: trade list 1: detailed log 2: summary 3: no output
//SetOption("UseCustomBacktestProc",true);//
SetOption("EveryBarNullCheck",False);//
SetOption("HoldMinBars",0);//
SetOption("EarlyExitBars",0);//
SetOption("EarlyExitFee",0);//
SetOption("EarlyExitDays",0);//
SetOption("DisableRuinStop",True);//
//SetOption("GenerateReport",1)
SetOption("SeparateLongShortRank",false);//
//SetOption("MaxOpenLong",100);//
//SetOption("MaxOpenShort",0);//
SetOption("RefreshWhenCompleted",False);//
SetOption("ExtraColumnsLocation",False);//
//SetOption("SettlementDelay",0);//
//OptimizerSetEngine("cmae");
SetBacktestMode( backtestRegular);
SetTradeDelays(1,1,1,1);//
BuyPrice=O;
SellPrice=O;
wlnum = 0;//GetOption( "FilterIncludeWatchlist" );
List = CategoryGetSymbols( categorywatchlist, wlnum ) ;
if( Status("stocknum")==2)
{
StaticVarRemove( "ValuesToSort*" );
for( n = 0; ( Sym = StrExtract( List, n ) ) != ""; n++ )
{
SetForeign( sym );
ann_slope = ((exp(LinRegSlope(ln(C),ExpRegperiod))^250) - 1)*100;
R2 = (Correlation(Cum(1), C, ExpRegperiod))^2;
momentum = R2 * ann_slope;//
RestorePriceArrays();
StaticVarSet( "ValuesToSort" + sym, momentum );
}
// perform ranking
StaticVarGenerateRanks( "rank", "ValuesToSort", 0, 1234 ); // normal rank mode
}
rank=StaticVarGet( "RankValuesToSort" + Name() );//
//Market Regime Filter
index_close = Foreign("^GSPC","C");
index_filter = index_close > MA(index_close,filterperiod);
//Gap Filter
upgap=IIf(L-Ref(H,-1)>0,((L-Ref(H,-1))/L)*100,0);//
downgap=IIf(H-Ref(L,-1)<0,((Ref(L,-1)-H)/H)*100,0);//
gap= Max(HHV(upgap,gapperiod),HHV(downgap,gapperiod));//
gapok=gap<gapsize;//
//Momentum Calc, based on exponential regression slope * R2;//
ann_slope = ((exp(LinRegSlope(log(C),ExpRegperiod))^250) - 1)*100;
R2 = (Correlation(Cum(1), C, ExpRegperiod))^2;
momentum = R2 * ann_slope;//
//Trade once a week only
TradingDay = DayOfWeek()==3;
//Allow positions only when C above MA
MA_exit = C<MA(C,maperiod);//
MA_entry = C>MA(C,maperiod);//
score = Null;
exit = Null;
noentry = Null;
for( i = 200; i < BarCount; i++ )
{
//Handle score = PSEUDO_scorenorotate
if (Tradingday [i] == False)
{
score [i] = 0;
}
if (Tradingday [i] == True AND index_filter [i] == false)
{
score [i] = 0;
}
// Handle score = 0 first
if (Tradingday [i] == True AND MA_exit [i] == True)
{
//exit [i] = 1;
//noentry [i] = 1;
score[i] = -1;//
}
if (Tradingday [i] == True AND momentum [i] <= 0)
{
//exit [i] = 1;
//noentry [i] = 1;
score[i] = -1;//
}
if (Tradingday [i] == True AND gapok [i] == False)
{
//exit [i] = 1;
//noentry [i] = 1;
score[i] = -1;//
}
// assign score
if (Tradingday[i] == True AND momentum [i] > 0 AND index_filter [i] == True AND MA_entry[i] == true)
{
score [i] = momentum [i];
}
else(score[i]=Null);
}
PositionScore = score;//
Buy= score>0 AND rank < rankheld;
Sell= score==-1 OR rank> rankheld;//
Plot(rank," ", colorRed,styleline);
Any ideas?
Dio