#include_once "Formulas\Norgate Data\Norgate Data Functions.afl" // Provided by Cesar Alvarez www.AlvarezQuantTrading.com ////////////////////////////////////////////////////////////////////// // Optimization Variables StaticVarSetText("REPORT_SYM_BUYHOLD", "DBC"); symSignal = "DBC"; idVers = Optimize("ID Version", 12, 12, 12, 1); xFactor = 1; if (Name() == "QLD") xFactor = 2; ////////////////////////////////////////////////////////////////////// // Constant Variables fPort = 1; // 1 = size is share of equity, 0 = points only Exectiming = 1; // 0 = Same Bar on Close, 1 = Next Bar on Open posqty = 1; ////////////////////////////////////////////////////////////////////////////////////////////////////// // Checking settings and setting up variables depending on database { // Making sure Pad & Align is checked if (Status("action") == actionBacktest) { // Paranoid check if (Status("stocknum") == 0) { StaticVarSet("barCountZero", BarCount); } else if (StrToUpper(Name()) != "~~~EQUITY") { barCountZero = StaticVarGet("barCountZero"); if (BarCount != barCountZero) { Error("Make sure Pad & Align is checked in Backtester Settings dialog. Set symbol to $SPX"); } } } dnLast = Status("rangetodate"); dn = DateNum(); dbName = GetDatabaseName(); if (StrFind(dbName, "Norgate") or StrFind(dbName, "Premium")) { asTradedPrice = NorgateOriginalCloseTimeSeries(); dn = DateNum(); // is the stock still trading? if(IsNull(GetFnData("DelistingDate"))) { barsRemaining = BarCount - BarIndex()-1; // extra one because on last day of data want 0 ThisIsLastBar = IIf(BarCount-BarIndex() < 4, false, barsRemaining<=0); } else { barDelist = Lookup(BarIndex(),GetFnData("DelistingDate"),1); if(IsNull(barDelist)) Error("How did we not find it"); barsRemaining = barDelist-BarIndex(); ThisIsLastBar = IIf(barDelist <= BarIndex(), true, IIf(BarCount-BarIndex() < 4, false, barsRemaining<=0)); } } else // assumes data without delisted stocks and no as traded price { asTradedPrice = Close ; ThisIsLastBar = False; Error("Assumes you have Norgate data"); } } ////////////////////////////////////////////////////////////////////// // Setting backtesting options { SetOption("InitialEquity",IIf(fPort ==1,100000,1000000)); SetOption("CommissionMode",3); SetOption("CommissionAmount",IIf(fPort == 1, 0.01, 0)); SetOption("InterestRate", 0); SetOption("AllowSameBarExit", 1); SetOption("MaxOpenPositions",IIf(fPort ==1,posqty,1500)); SetOption("MarginRequirement",100); SetOption("UsePrevBarEquityForPosSizing",IIf(exectiming==0,False,True)); SetOption("AllowPositionShrinking" , True); SetOption("MinPosValue", .00001); SetOption("MinShares", .00001); SetTradeDelays(0,0,Exectiming,Exectiming); SetOption("ActivateStopsImmediately" , False); SetPositionSize(IIf(fPort ==1,100/posqty,1),IIf(fPort ==1,spsPercentOfEquity,spsShares)); RoundLotSize = IIf(fPort ==1,10,1); SetOption("PriceBoundChecking",false); } ////////////////////////////////////////////////////////////////////// // Any foreign variables inIndex = NorgateIndexConstituentTimeSeries("$SPX"); cSPX = Foreign("$SPX", "C"); // Optimize(".",1,1,1,1); ///////////////////////////////////////////////////////////////////// // General variables HV100 = StDev(log(C/Ref(C,-1)),100)*(252^.5)*100; dn = DateNum(); sellOffset = 0; buyOffset = 1; switch(idVers) { case 1: break; case 3: break; case 2: symSignal = "$COMP"; break; case 4: pctTrail = 5; break; case 5: pctTrail = 7; break; case 6: break; case 8: pctTrail = 7; break; case 9: pctTrail = 10; break; case 10: break; case 11: break; case 12: break; case 13: buyOffset = 0; break; } SetForeign(symSignal); cSym = C; lSym = L; vSym = V; RestorePriceArrays(); Buy1 = Ref(cSym < MA(cSym,50),-1) and vSym >= MA(vSym, 50) and lSym < Ref(LLV(lSym, 5), -1) and cSym > Ref(cSym, -1) and cSym > Ref(lSym, -1) * .995 and (Year() == 1999 or MA(cSym,50) > MA(cSym, 200)) and dn != 1100521 ; Buy2 = BarsSince(lSym < Ref(LLV(lSym,5),-1)) >= 3-1 and ROC(cSym,1) > 1 and vSym > Ref(vSym, -1) and (Year() == 1999 or MA(cSym,50) > MA(cSym, 200)); Buy3 = cSym == Highest(cSym) and (Year() == 1999 or MA(cSym,50) > MA(cSym, 200)); Buy4 = cSym > MA(cSym, 50) and Cross(MA(cSym,50), MA(cSym, 200)) ; Buy5 = (idvers == 11 or idVers==12 or idVers==13) and MA(cSym,50) < MA(cSym, 200) and Cross(EMA(cSym,8), MA(cSym,50)); // Buy1 = buy4 = Buy3 = 0; Buy2=0; /////////////////////////////////////////////////////////////////////// // Buy & Sell Rules Buy = 0 ; Sell = 0; Short = False; Cover = False; BuyPrice = SellPrice = ShortPrice = CoverPrice = Open; inTrade = 0; typeBuy = 0; priceStop = 0; ma50 = MA(cSym,50); ema20 = EMA(cSym, 20); llv5 = LLV(lSym, 5); pctTarget = 5*xFactor; SellPrice = 0; dn = DateNum(); Buy1 = Ref(Buy1,-buyOffset); Buy2 = Ref(Buy2,-buyOffset); Buy3 = Ref(Buy3,-buyOffset); Buy4 = Ref(Buy4,-buyOffset); Buy5 = Ref(Buy5,-buyOffset); v10Above = cSym > EMA(cSym, 20) and EMA(cSym, 20) > MA(cSym, 50); doScale = 1; // used for testng BuyPrice = IIf(buyOffset==1, Open, Close); for(bar=1;bar 1100101) _TRACE(NumToStr(dn[bar],1.0,false)); if(!inTrade) { if(Buy1[bar]) { entryPrice = BuyPrice[bar]; stopPrice = llv5[bar-buyOffset] *(1-(.5*xFactor)/100); if(entryPrice > stopPrice) { inTrade = 1; typeBuy = 1; Buy[bar] = 1; beenAboveMA50 = false; scaleOut = false; beenAboveV10 = false; } } else if(Buy2[bar]) { entryPrice = BuyPrice[bar]; stopPrice = entryPrice * (1-(3.5*xFactor)/100); inTrade = 1; typeBuy = 2; Buy[bar] = 1; beenAboveMA50 = Csym[bar-buyOffset] > ma50[bar-buyOffset]; scaleOut = false; beenAboveV10 = false; // _TRACE(NumToStr(dn[bar],1.0,false)+":"+beenAboveMA50); } else if (Buy3[bar]) { entryPrice = BuyPrice[bar]; inTrade = 1; typeBuy = 3; Buy[bar] = 1; beenAboveMA50 = true; scaleOut = false; beenAboveV10 = false; } else if (Buy4[bar]) { entryPrice = BuyPrice[bar]; inTrade = 1; typeBuy = 4; Buy[bar] = 1; beenAboveMA50 = Csym[bar-buyOffset] > ma50[bar-buyOffset]; scaleOut = false; beenAboveV10 = false; } else if (Buy5[bar]) { entryPrice = BuyPrice[bar]; inTrade = 1; typeBuy = 5; Buy[bar] = 1; beenAboveMA50 = Csym[bar-buyOffset] > ma50[bar-buyOffset]; scaleOut = false; beenAboveV10 = false; } if(inTrade) hhvHigh = H[bar]; } if(inTrade) { if(v10Above[bar]) beenAboveV10 = true; if(typeBuy == 1) { if(beenAboveMA50) { if(Csym[bar-sellOffset] < ma50[bar-sellOffset]) { inTrade = 0; Sell[bar] = 11; SellPrice[bar] = Open[bar]; } else if (idVers == 3 and Lsym[bar] < ma50[bar-1]*(1-(1*xfactor)/100)) { inTrade = 0; Sell[bar] = 15; SellPrice[bar] = Min(Open[bar], ma50[bar-1]*(1-(1*xfactor)/100)); } } else { if (Low[bar] < stopPrice) { inTrade = 0; Sell[bar] = 12; SellPrice[bar] = Min(Open[bar], stopPrice); } if(C[bar] > ma50[bar]) beenAboveMA50 = true; } if(inTrade and H[bar] > entryPrice*(1+pctTarget/100) and !scaleOut and doScale and !(idVers == 10 and !beenAboveV10)) { // _TRACE("Scale11"); Buy[bar] = sigScaleOut; SellPrice[bar] = Max(Open[bar], entryPrice * (1+pctTarget/100)); scaleOut = true; if(idVers >= 6 and C[bar]= 6 and scaleOut and !beenAboveMA50 and intrade and L[bar] < priceStop) { inTrade = 0; Sell[bar] = 17; SellPrice[bar] = Min(Open[bar], priceStop); } } if(typeBuy == 2) { if(beenAboveMA50) { if(C[bar-sellOffset] < ma50[bar-sellOffset]) { inTrade = 0; Sell[bar] = 21; SellPrice[bar] = Open[bar]; } else if (idVers == 3 and L[bar] < ma50[bar-1]*(1-(1*xfactor)/100)) { inTrade = 0; Sell[bar] = 25; SellPrice[bar] = Min(Open[bar], ma50[bar-1]*(1-(1*xfactor)/100)); } } else { if (Low[bar] < stopPrice) { inTrade = 0; Sell[bar] = 22; SellPrice[bar] = Min(Open[bar], stopPrice); } if(C[bar] > ma50[bar]) beenAboveMA50 = true; } if(inTrade and H[bar] > entryPrice*(1+pctTarget/100) and !scaleOut and !(idVers == 10 and !beenAboveV10)) { //Buy[bar] = sigScaleOut; SellPrice[bar] = Max(Open[bar], entryPrice * (1+pctTarget/100)); scaleOut = true; if(idVers >= 6 and C[bar]= 6 and scaleOut and !beenAboveMA50 and intrade and L[bar] < priceStop) { inTrade = 0; Sell[bar] = 27; SellPrice[bar] = Min(Open[bar], priceStop); } } if(typeBuy == 3) { if(C[bar-sellOffset] < ma50[bar-sellOffset]) { inTrade = 0; Sell[bar] = 31; SellPrice[bar] = Open[bar]; } else if (idVers == 3 and L[bar] < ma50[bar-1]*(1-(1*xfactor)/100)) { inTrade = 0; Sell[bar] = 35; SellPrice[bar] = Min(Open[bar], ma50[bar-1]*(1-(1*xfactor)/100)); } if(inTrade and H[bar] > entryPrice*(1+pctTarget/100) and !scaleOut and !(idVers == 10 and !beenAboveV10)) { Buy[bar] = sigScaleOut; SellPrice[bar] = Max(Open[bar], entryPrice * (1+pctTarget/100)); scaleOut = true; if(idVers >= 6 and C[bar]= 6 and scaleOut and !beenAboveMA50 and intrade and L[bar] < priceStop) { inTrade = 0; Sell[bar] = 37; SellPrice[bar] = Min(Open[bar], priceStop); } } if(typeBuy == 4) { if(beenAboveMA50) { if(C[bar-sellOffset] < ma50[bar-sellOffset]) { inTrade = 0; Sell[bar] = 41; SellPrice[bar] = Open[bar]; } else if (idVers == 3 and L[bar] < ma50[bar-1]*(1-(1*xfactor)/100)) { inTrade = 0; Sell[bar] = 45; SellPrice[bar] = Min(Open[bar], ma50[bar-1]*(1-(1*xfactor)/100)); } } else { if(C[bar] > ma50[bar]) beenAboveMA50 = true; } if(inTrade and H[bar] > entryPrice*(1+pctTarget/100) and !scaleOut and !(idVers == 10 and !beenAboveV10)) { Buy[bar] = sigScaleOut; SellPrice[bar] = Max(Open[bar], entryPrice * (1+pctTarget/100)); scaleOut = true; if(idVers >= 6 and C[bar]= 6 and scaleOut and !beenAboveMA50 and intrade and L[bar] < priceStop) { inTrade = 0; Sell[bar] = 47; SellPrice[bar] = Min(Open[bar], priceStop); } } if(typeBuy == 5) { if(beenAboveMA50) { if(C[bar-sellOffset] < ma50[bar-sellOffset]) { inTrade = 0; Sell[bar] = 51; SellPrice[bar] = Open[bar]; } else if (idVers == 3 and L[bar] < ma50[bar-1]*(1-(1*xfactor)/100)) { inTrade = 0; Sell[bar] = 15; SellPrice[bar] = Min(Open[bar], ma50[bar-1]*(1-(1*xfactor)/100)); } } else { if(C[bar] > ma50[bar]) beenAboveMA50 = true; } if(inTrade and H[bar] > entryPrice*(1+pctTarget/100) and !scaleOut and doScale and !(idVers == 10 and !beenAboveV10)) { // _TRACE("Scale11"); Buy[bar] = sigScaleOut; SellPrice[bar] = Max(Open[bar], entryPrice * (1+pctTarget/100)); scaleOut = true; if(idVers >= 6 and C[bar]= 6 and scaleOut and !beenAboveMA50 and intrade and L[bar] < priceStop) { inTrade = 0; Sell[bar] = 57; SellPrice[bar] = Min(Open[bar], priceStop); } } hhvHigh = Max(hhvHigh, H[bar]); } } if(idVers == 7) Buy = 0; /////////////////////////////////////////////////////////////////////// // Entry/exit and ranking PositionScore = Hv100; //////////////////////////////////////////////////////////////////////////////// // Exploration code Filter = Buy5; AddColumn(BarsSince(L < Ref(LLV(L,5),-1)), "BS", 1.4); //////////////////////////////////////////////////////////////////////////////////////////////// // CBT SetBacktestMode( backtestRegularRaw ); SetCustomBacktestProc(""); if (Status("action") == actionPortfolio) { dnStart = Status("rangefromdate"); dnEnd = Status("rangetodate"); yrStart = int(dnStart/10000); yrEnd = int(dnEnd/10000); d = Day(); m = Month(); y = Year(); bo = GetBacktesterObject(); // Get backtester object bo.PreProcess(); // Do pre-processing for (bar = 0; bar < BarCount; bar++) // Loop through all bars { _TRACE("-"); _TRACE("" + m[bar] + "/" + d[bar] + "/" + y[bar] + ":"); // exit all trades for ( sig=bo.GetFirstSignal(bar); sig; sig=bo.GetNextSignal(bar) ) { // first handle exit signals if (sig.IsExit() AND sig.Price != -1 AND NOT IsNull( bo.FindOpenPos( sig.Symbol ) )) { // Exit Signal bo.ExitTrade(bar,sig.symbol,sig.Price,sig.Reason); _TRACE(""+sig.Price()); } } // Loop through all signals at this bar for (sig = bo.GetFirstSignal(bar); sig; sig = bo.GetNextSignal(bar)) { if (sig.IsEntry AND sig.Price != -1 AND IsNull( bo.FindOpenPos( sig.Symbol ) ) ) { // Entry Signal if( bo.EnterTrade(bar, sig.symbol, sig.IsLong(), sig.Price, sig.PosSize(), 1, 1) == 0 ) { bContinue = False; } } if (sig.IsScale() AND sig.Price != -1 ) { _TRACE("SO:"+sig.Price); // Entry Signal if( bo.ScaleTrade(bar, sig.symbol, false, sig.Price, -1050) == 0 ) { bContinue = False; } } } // end sig loop for ( sig=bo.GetFirstSignal(bar); sig; sig=bo.GetNextSignal(bar) ) { // first handle exit signals if (sig.IsExit() AND sig.Price != -1 AND NOT IsNull( bo.FindOpenPos( sig.Symbol ) )) { // Exit Signal bo.ExitTrade(bar,sig.symbol,sig.Price,sig.Reason); } } //bo.HandleStops(i); // Handle programmed stops at this bar bo.UpdateStats(bar, 1); // Update MAE/MFE stats for bar bo.UpdateStats(bar, 2); // Update stats at bar's end } // End of for loop over bars bo.PostProcess(); // Do post-processing if (true) { dt = DateNum(); if(Version() <= 5.4) eq = Foreign("~~~EQUITY", "C"); else eq = bo.EquityArray(); eqM = 0; // end of year equity - saving 2001 data in bar 101, 2002 in bar 102, ... for (i = 1; i < BarCount-1; i++) { // are we at the end of a year if (int(dt[i+1]/10000) > int(dt[i]/10000) ) eqM[int(dt[i]/10000)] = eq[i]; } // save the last equity eqM[yrStart-1] = GetOption("InitialEquity"); eqM[yrEnd] = eq[BarCount-1]; // output the results for (i=yrStart; i<= yrEnd; i++) { bo.addcustommetric("" + (i + 1900) + " Ret % ", 100*(eqM[i]/eqM[i-1] - 1)); } } if(1) // last N month ret - last N yr ret { CloseMonthly = TimeFrameCompress(eq, inMonthly); eqMonthly = TimeFrameCompress(eq, inMonthly); rocM = LastValue(ROC(eqMonthly, 12*3)); annual_rtn_3yr=100*(((1+rocM/100)^(1/3))-1); bo.AddCustomMetric("3 Yr ret", 100*(((1+rocM/100)^(1/3))-1)); rocM = LastValue(ROC(eqMonthly, 12*5)); annual_rtn_5yr=100*(((1+rocM/100)^(1/5))-1); bo.AddCustomMetric("5 Yr ret", 100*(((1+rocM/100)^(1/5))-1)); } if (true) // top NMDD { VarSet("DD2", 100); eq = bo.EquityArray(); drPerc = -100*(eq/Highest(eq) - 1); dd = 0; bslh = HighestBars(eq); bslh[BarCount-1] = 0; // need to catch last DD dd = IIf(bslh == 0, IIf(Ref(bslh, -1)==0, 0, HHV(drPerc, Ref(bslh, -1)+1)), 0); ddLast = LastValue(Highest(dd)); VarSet("DD1", ddLast); bo.AddCustomMetric("DD1", ddLast); for (nDD = 2; nDD <= 5; nDD++) { ddNew = LastValue(Highest(IIf(dd < ddLast, dd, 0))); VarSet("DD" + nDD, ddNew); ddLast = ddNew; bo.AddCustomMetric("DD"+nDD, ddLast); } bo.AddCustomMetric("MDD Cur", LastValue(drPerc)); } // Max DD Len if (true) { cnt = 5; ddLenS = HighestBars(bo.EquityArray()); ddLen = Nz(IIf(ddLenS!= 0 and Ref(ddlenS, 1)==0, ddLenS, 0), 0); ddLen[BarCount-1]=ddLenS[BarCount-1]; ddLen = -Sort(-ddLen); for (i=0; i< cnt;i++) { bo.AddCustomMetric("MDD Len" + (i+1), ddLen[i]); } bo.AddCustomMetric("MDD Len Cur", LastValue(ddlenS)); } if(true) { // SHARPE START if (Version() <= 5.4) eq = Foreign("~~~EQUITY", "C"); else eq = bo.EquityArray(); tbill = Foreign("%IRX", "C"); tbill = ((1+tbill/100)^(1/12))-1; // convert to monthly rate meq = TimeFrameCompress(eq, inMonthly); meq = IIf(IsNull(meq) AND !IsNull(Ref(meq,1)), GetOption("InitialEquity"), meq); // needed to get first month return mtbill = TimeFrameCompress(tbill, inMonthly); m = Cum(IsNull(meq) == 0); mret = ROC(meq, 1)/100; delta = mret - mtbill; delta = IIf(IsNull(delta) AND !IsNull(Ref(delta,1)), 0, delta); // needed to get the next 2 calcs to get the first data point avgDelta = LastValue(MA(delta, LastValue(m)-1)); annStdDevDelta = LastValue(StDev(log(1+delta), LastValue(m)-1))*12^.5; annDeltaReturn = exp(12*LastValue( MA( log(1+delta), LastValue(m)-1 ) ) ) - 1; sharpe = annDeltaReturn/annStdDevDelta; // SHARPE END stats = bo.getperformancestats(0); rtn_riskfree_monthly = TimeFrameCompress((1+Foreign("Tbill","c")/100)^(1/12)-1,inMonthly,compressLast); SetForeign("$SPX"); spx_close_0 = C[0]; TimeFrameSet(inMonthly); rtn_monthly_spx = IIf ( IsNull(ROC(C,1)), C / spx_close_0 - 1, ROC(C,1) / 100 ); TimeFrameRestore(); RestorePriceArrays(); Months_in_test = LastValue ( Cum ( monthend = Nz(TimeFrameExpand(1,inMonthly,expandPoint)) )); years_in_test = LastValue ( Cum ( yearend = Nz(TimeFrameExpand(1,inYearly,expandPoint)) )); if (Version() <= 5.4) Equity_rpt = Foreign("~~~EQUITY", "C"); else Equity_rpt = bo.equityarray; Equity_yearly = TimeFrameCompress(Equity_rpt,inYearly,compressLast); Equity_monthly = TimeFrameCompress(Equity_rpt,inMonthly,compressLast); rtn_equity_yearly = IIf ( IsNull(ROC(Equity_yearly,1)), Equity_yearly / GetOption("initialequity") - 1, ROC(Equity_yearly,1) / 100 ); rtn_equity_yearly = TimeFrameExpand(rtn_equity_yearly,inYearly,expandPoint); rtn_monthly = IIf ( IsNull(ROC(Equity_monthly,1)), Equity_monthly / GetOption("initialequity") - 1, ROC(Equity_monthly,1) / 100 ); rtn_excess_monthly = rtn_monthly - rtn_riskfree_monthly ; Volatility_rtn_equity_yearly = LastValue ( 100 * StDev ( Nz ( log ( 1 + rtn_monthly ) ), Months_in_test ) * (12^0.5) ); beta_spx = LastValue ( ( r_spx = Correlation ( rtn_monthly, rtn_monthly_spx, Months_in_test - 2 ) ) * StDev(rtn_monthly, Months_in_test - 2 ) / StDev(rtn_monthly_spx, Months_in_test - 2 ) ); bo.addcustommetric ( "Sharpe Ratio", WriteVal ( sharpe, 8.2, False ) ); bo.addcustommetric ( "Annual Volatility", WriteVal ( Volatility_rtn_equity_yearly, 8.2, False ) + "%" ); bo.addcustommetric ( "Correl. with SPX",WriteVal(LastValue(r_spx),8.3,False)); } minTrade = maxTrade = 0; maxHold = 0; for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() ) { maxTrade = Max(maxTrade, trade.GetPercentProfit()); minTrade = Min(minTrade, trade.GetPercentProfit()); maxHold = Max(maxHold, trade.BarsInTrade); } for( trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) { maxTrade = Max(maxTrade, trade.GetPercentProfit()); minTrade = Min(minTrade, trade.GetPercentProfit()); maxHold = Max(maxHold, trade.BarsInTrade); } bo.AddCustomMetric("Min % Trade", minTrade); bo.AddCustomMetric("Max % Trade", maxTrade); bo.AddCustomMetric("Max Hold", maxHold); bo.AddCustomMetric("WL", CategoryGetName( categoryWatchlist , GetOption("FilterIncludeWatchlist"))); bo.AddCustomMetric("Start Date", DateTimeToStr(DateTimeConvert(2, Status("rangefromdate")))); bo.AddCustomMetric("End Date", DateTimeToStr(DateTimeConvert(2, Status("rangetodate")))); bo.AddCustomMetric("AFL",StrExtract(GetFormulaPath(), -1, '\\')); }