//10/03/2016 //EOD daily Mean Reversion Strategy long only system modified from Connors and Alvarez. The buy set up uses 3 down days and RSI(2) oversold condition. The system is meant to be run without an Index filter. Exit is on either an overbought RSI(2) condition, after a fixed number of days or an initial percentage stop. The system will be traded on IBs platform using $6 entry and exit commission and using an initial start up capital of 25k. The system is to be used on ASX stocks in the range of 25c to $25 with a minimum 7 day turnover of 250k Slippage on entry and exit has been taken as 0.5% each way and buys and sells use the open price. #include_once "Formulas\Norgate Data\Norgate Data Functions.afl" _SECTION_BEGIN( "Historical Database Testing" ); //==================================================================== //Historical Database Testing //==================================================================== ASXList = ParamList( "ASX Historical Watchlist:", "1: Off|2: ASX 20|3: ASX 50|4: ASX 100|5: ASX 200|6: ASX 300|7: ASX All Ordinaries|8: ASX Small Ordinaries|9: ASX Emerging Companies|10: Excluding ASX 300|11: In XAO but Exc ASX 100|12: Exc XAO", 0 ); HistDB = 1;//set to 1 to start if( ASXList == "1: Off" ) HistDB = 1; if( ASXList == "2: ASX 20" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XTL" ); if( ASXList == "3: ASX 50" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XFL" ); if( ASXList == "4: ASX 100" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XTO" ); if( ASXList == "5: ASX 200" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XJO" ); if( ASXList == "6: ASX 300" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XKO" ); if( ASXList == "7: ASX All Ordinaries" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XAO" ); if( ASXList == "8: ASX Small Ordinaries" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XKO" ) AND NOT NorgateIndexConstituentTimeSeries ( "$XTO" ); if( ASXList == "9: ASX Emerging Companies" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XEC" ); if( ASXList == "10: Excluding ASX 300" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XKO" ) == 0; if( ASXList == "11: In XAO but Exc ASX 100" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XAO" )AND NOT NorgateIndexConstituentTimeSeries ( "$XTO" ); if( ASXList == "12: Exc XAO" ) HistDB = NorgateIndexConstituentTimeSeries ( "$XAO" ) == 0; //--------------------------------------------------------------------------------- USList = ParamList("US Historical Watchlist:","1: Off|2: Russell 1000|3: Russell 2000|4: Russell 3000|5: NASDAQ 100|6: Dow Jones Industrial Average|7: S&P 500|8: S&P 100|9: S&P MidCap 400|10: S&P SmallCap 600|11: S&P 1500|12: Russell MicroCap|13: Russell MidCap",0); USHistDB = 1;//set to 1 to start if(USList == "1: Off") USHistDB = 1; if(USList == "2: Russell 1000") USHistDB = NorgateIndexConstituentTimeSeries ("$RUI"); if(USList == "3: Russell 2000") USHistDB = NorgateIndexConstituentTimeSeries ("$RUT"); if(USList == "4: Russell 3000") USHistDB = NorgateIndexConstituentTimeSeries ("$RUA"); if(USList == "5: NASDAQ 100") USHistDB = NorgateIndexConstituentTimeSeries ("$NDX"); if(USList == "6: Dow Jones Industrial Average") USHistDB = NorgateIndexConstituentTimeSeries ("$DJI"); if(USList == "7: S&P 500") USHistDB = NorgateIndexConstituentTimeSeries ("$SPX"); if(USList == "8: S&P 100") USHistDB = NorgateIndexConstituentTimeSeries ("$OEX"); if(USList == "9: S&P MidCap 400") USHistDB = NorgateIndexConstituentTimeSeries ("$MID"); if(USList == "10: S&P SmallCap 600") USHistDB = NorgateIndexConstituentTimeSeries ("$SML"); if(USList == "11: S&P 1500") USHistDB = NorgateIndexConstituentTimeSeries ("$SP1500"); if(USList == "12: Russell MicroCap") USHistDB = NorgateIndexConstituentTimeSeries ("$RUMIC"); if(USList == "13: Russell MidCap") USHistDB = NorgateIndexConstituentTimeSeries ("$RMC"); //--------------------------------------------------------------------------------- HDBFilter = HistDB AND USHistDB; //==================================================================== _SECTION_END(); _SECTION_BEGIN("Optional Price Filter and TO Filter"); //==================================================================== //Price, Volume and Turnover Filters //==================================================================== PriceTog = ParamToggle("Price Filter","Off|On",1); CloseToggle = ParamToggle("Close Price","Adjusted|Unadjusted",0); CP = IIf(CloseToggle,NorgateOriginalCloseTimeSeries(),Close); MinSP = Param("Minimum Share Price",0.25,0.00,1000,0.01); MaxSP = Param("Maximum Share Price",25,0.00,2000,0.01); MinMaxSP = CP >= MinSP AND CP <= MaxSP; PriceFilt = IIf(PriceTog,MinMaxSP,1); //-------------------------------------------------------------------- TOTog = ParamToggle("Turnover Filter","Off|On",1); Turnover = CP*V; MinTO = Param("Minimum Turnover - $",500000,0,10000000,1000); TOMA = Param("Turnover MA",10,1,200,1); AveTO = EMA(Turnover,TOMA); TOFilter = AveTO > MinTO AND Turnover > MinTO; TOFilt = IIf(TOTog,TOFilter,1); //-------------------------------------------------------------------- VolTog = ParamToggle("Volume Filter","Off|On",1); MinVol = Param("Minimum Volume",250000,0,10000000,1000); VolMA = Param("Volume MA",10,1,200,1); AveVol = EMA(Volume,VolMA); VolFilter = AveVol > MinVol AND Volume > MinVol; VolFilt = IIf(VolTog,VolFilter,1); //-------------------------------------------------------------------- OptFilt = PriceFilt AND TOFilt AND VolFilt; //==================================================================== _SECTION_END(); _SECTION_BEGIN("Index Filter"); //==================================================================== //Index Filter //==================================================================== IndexTog = ParamToggle("Index Filter","On|Off",1); IndexCode = ParamStr("Index Symbol","$XAO"); Index = Foreign(IndexCode,"C"); IndMA = Param("Index Filter MA",150,2,10000,1); IndexFiltUp = Index > MA(Index,IndMA); IndexUp = IIf(IndexTog,1,IndexFiltUp); //==================================================================== _SECTION_END(); //==================================================================== //Universe Filter //==================================================================== List = Paramlist("Universe Filter:","1:Off|2:Resources|3:Industrials",0); UniverseFilter = 1; If (List == "1:Off") UniverseFilter = 1; If (List == "2:Resources") UniverseFilter = InGics("10") OR InGics("151040"); If (List == "3:Industrials") UniverseFilter = InGics("10")==0 OR InGics("151040")==0; //==================================================================== _SECTION_END(); //==================================================================== //Entry and Exit Rules //==================================================================== EntryRSIlevel = Param("Entry RSI level",25,10,40,5); ExitRSIlevel = Param("Exit RSI level",75,60,100,5); EntryRSI = EntryRSIlevel; ExitRSI = ExitRSIlevel; RSIPeriod = Param("RSI Period",2,1,10,1); LowRSIBars = Param("No of RSI Lows",2,1,5,1); MultipleDownDays = Param("No of Days Down",2,1,5,1); ATRp = Param("Limit Price: ATR period",10,1,200,1); ATRmulti = Param("Limit Price: ATR Multi",0.5,0,100,.1); ATRVal = ATR(ATRp)*ATRmulti; BuyLimP = L - ATRval; TickLo = IIf(L<0.10,0.001,IIf(L<2.00,0.005,0.01)); BuyLimVal = round(BuyLimP/TickLo); BuyLim = BuyLimVal * TickLo; CurrentRSI = RSI(RSIPeriod); MultipleNDayLowRSI = Sum(CurrentRSI < EntryRSI, LowRSIBars) == LowRSIBars; //RSI(2) MA(C,MALongPeriod); //Close above long term MA CloseBelowShortTermMA = C < MA(C,MAShortPeriod); //Close below short term MA CP = Param("Close %",60,1,100,1); Spread = H-L; WeakClose = C <= L +(Spread * CP/100); //ClosePercentage = Param("Close as Percentage of Bar Range %",40,0,100,1); //IBS = Nz((Close - Low )/(High - Low) <= ClosePercentage/100); //Internal Bar Strength as percentage of daily range Volatility = abs(C-Ref(C,-1))>= 0.5*ATR(10) AND abs(C-Ref(C,-1))<= 2*ATR(10); //Limit volatility to between these limits OnLastTwoBarsOfDelistedSecurity = BarIndex() >= (LastValue(BarIndex()) -1) AND !IsNull(GetFnData("DelistingDate")); OnSecondLastBarOfDelistedSecurity = BarIndex() == (LastValue(BarIndex()) -1) AND !IsNull(GetFnData("DelistingDate")); BuySetUp = ThreeDaysDown AND WeakClose AND CloseBelowShortTermMA AND CloseAboveLongTermMA AND IndexUp AND OptFilt AND Volatility AND HDBFilter; LE = Ref(BuySetUp,-1) AND NOT OnLastTwoBarsOfDelistedSecurity; //Long Entry LE = Ref(BuySetup,-1) AND L <= Ref(BuyLim,-1) AND NOT OnLastTwoBarsOfDelistedSecurity; ; slippage = Param("slippage %",0,0,05,.5); LExPrice = IIf(O*(1-slippage/100) ExitRSI; //Exit signal LEx = Ref(SellSetup,-1); //Long Exit //-------------------------------------------------------------------- //Stops //-------------------------------------------------------------------- Short = Cover = False; ExitBars = Param("# Exit Bars",9,2,100,1); StopLossPercentage = Param("% Hard Stop Loss",20,0,100,1); //Stop Loss //==================================================================== //Looping //==================================================================== Buy = 0; Sell = 0; LPriceAtBuy = 0; LBIT = 0; FlagExit = 0; MaxLossPrice = Null; //==================================================================== for( j = 1; j < BarCount; j++ ) { if( LPriceAtBuy == 0 AND LE[j] ) { Buy[j] = True; LPriceAtBuy = LEPrice[j]; BuyPrice[j] = LEPrice[j]; MaxLossPrice[j] = LPriceAtBuy * ( 1 - StopLossPercentage / 100 ); //Craig - MaxLoss is intiialized before the looping and MaxLossPrice here? LBIT[j] = 1; if( LBIT[j] > 1 AND LEx[j] OR OnSecondLastBarOfDelistedSecurity[j] ) { Sell[j] = True; SellPrice[j] = LExPrice[j]; //Craig - missing a [j] here LPriceAtBuy = 0; } if( LBIT[j] > 1 AND L[j - 1] <= MaxLossPrice[j - 1] ) //Craig - no need to use the delisted stock exit here as it is checked on the previous exit { Sell[j] = True; SellPrice[j] = LExPrice[j]; //Craig - missing a [j] here LPriceAtBuy = 0; } } else if( LPriceAtBuy > 0 ) { LBIT[j] = LBIT[j - 1] + 1; MaxLossPrice[j] = MaxLossPrice[j - 1]; if( LBIT[j] > 1 AND L[j - 1] <= MaxLossPrice[j - 1] OR OnSecondLastBarOfDelistedSecurity[j] ) { Sell[j] = True; SellPrice[j] = LExPrice[j]; LPriceAtBuy = 0; } if( LBIT[j] == ExitBars ) //Exit trade after a certain number of bars { FlagExit[j] = True; } if( LBIT[j] > ExitBars ) { Sell[j] = True; SellPrice[j] = LExPrice[j]; LPriceAtBuy = 0; } if( LBIT[j] > 1 AND LEx[j]) //Craig - same for delisted stock exit here { Sell[j] = True; SellPrice[j] = LExPrice[j]; LPriceAtBuy = 0; } } } //==================================================================== MaxExit = FlagExit; StopLossExit = L <= MaxLossPrice; LESetup = IIf(LBIT==0 OR Sell==1,BuySetUp,0); //Long Entry Set Up LExSetup = IIf(LBIT>0 AND Sell==0,(SellSetup OR FlagExit OR StopLossExit),0); //Long Exit Set Up //Craig - there are 3 exits but this will only pick up one in the explorer //-------------------------------------------------------------------- Filter = Buy OR Sell OR LESetUp OR LExSetup OR LBIT>0; AddTextColumn(WriteIf(LESetup,"Buy Setup",""),"Entry Signal",1.2,colorDefault,IIf(LESetup,colorBrightGreen,colorDefault),130); AddTextColumn(WriteIf(LExSetup,"Exit Setup",""),"Exit Signal",1.2,colorDefault,IIf(LExSetup,colorRed,colorDefault),100); AddTextColumn(WriteIf(Buy,"Buy",""),"Today's Entries",1.2,colorDefault,IIf(Buy,colorGreen,colorDefault),130); AddTextColumn(WriteIf(Sell,"Sell",""),"Today's Exits",1.2,colorDefault,IIf(Sell,colorRed,colorDefault),130); AddColumn(LBIT,"LBIT",1.0); SetSortColumns(-3,-4,-5,-6,1); /*Filter = 1; AddColumn(BuySetUp,"Buy Setup",1); AddColumn(Buy,"Buy",1); AddColumn(StopLossExit, "Sell Setup",1); AddColumn(Sell,"Sell",1);*/ //==================================================================== //Interpretation Window //==================================================================== /*"ADX Value: "+NumToStr(ADX(ADXR),1); "3 Lower Lows: "+WriteIf(Cond2==1,"Yes","No"); "Close above MA: "+WriteIf(Cond3==1,"Yes","No"); printf( "BuyStop: " + NumToStr(BuyStop,1.3) + "n" ); ""; "Bars In Trade: "+NumToStr(LBIT,1); "Trail Stop: "+NumToStr(LStop,1.3);*/ //==================================================================== _SECTION_BEGIN("Chart Plotting"); //==================================================================== SetChartOptions(0,chartShowArrows|chartShowDates); _N(Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) )); ChartType = ParamList("Chart Type","OHLC Bar|Candle|Line",0); SetChartBkColor(ParamColor("Background Colour",colorwhite)); UpColour = ParamColor("Bar/Candle Up Colour",colorGreen); DownColour = ParamColor("Bar/Candle Down Colour",colorRed); Colour1 = IIf(C > Ref(C,-1),UpColour,DownColour); Colour2 = IIf(C > O,UpColour,DownColour); Colour3 = ParamColor("Line Chart Colour",colorRed); TextColour = ParamColor("Text Colour",colorblack); if(ChartType == "OHLC Bar") ChartOption = styleBar; if(ChartType == "Candle") ChartOption = styleCandle; if(ChartType == "Line") ChartOption = styleLine; if(ChartType == "OHLC Bar") ColourOption = Colour1; if(ChartType == "Candle") ColourOption = Colour2; if(ChartType == "Line") ColourOption = Colour3; //-------------------------------------------------------------------- Plot(Close,"Close",ColourOption,styleNoTitle|ChartOption|ParamStyle("Chart Style")); GraphXSpace = 10; Plot(MaxLossPrice,"Stop Loss",ParamColor("Stop Loss Colour",colorBlue),ParamStyle("Stop Loss Style",styleLine)); PlotShapes(shapehollowUpArrow*LESetup,colorGreen,0,L,-40); //plots an arrow for a setup bar PlotShapes(shapehollowdownArrow*LExSetup,colorRed,0,H,-40); //plots an arrow for an exit bar PlotShapes(shapeHollowSmallSquare*Buy,TextColour,0,BuyPrice,0); //plots a small hollow square right at the buy price PlotShapes(shapeHollowSmallSquare*Sell,TextColour,0,SellPrice,0); //plots a small hollow square right at the sell price //-------------------------------------------------------------------- FirstVisibleBar = Status("FirstVisibleBar"); LastVisibleBar = Status("LastVisibleBar"); for( b = Firstvisiblebar; b <= Lastvisiblebar AND b < BarCount; b++) { if(Buy[b]) PlotText("\n Buy\n "+NumToStr(BuyPrice[b],1.3),b,BuyPrice[b]*0.9985,TextColour); else if(Sell[b]) PlotText("\n Sell\n "+NumToStr(SellPrice[b],1.3),b,SellPrice[b]*0.9985, TextColour); } //==================================================================== _SECTION_END(); _SECTION_BEGIN("Backtesting"); //===================================================================== //options in automatic analysis settings //===================================================================== SetTradeDelays(0,0,0,0); SetOption("UsePrevBarEquityForPosSizing",True); Capital = Param("Initial Equity $",25000,1000,1000000,100); SetOption("InitialEquity",Capital); MaxPos = Param("Maximum Open Positions",10,1,100,1); SetOption("MaxOpenPositions",MaxPos); SetOption("AllowSameBarExit",False); SetOption("AllowPositionShrinking", False ); SetOption("InterestRate",0); SetOption("Minshares",1); SetOption("MinPosValue",1); SetOption("CommissionMode",2); //Fixed Dollar Amount SetOption("CommissionAmount",6); //Use IB commision rates SetOption("AccountMargin",100); PositionScore = mtRandom(); //--------------------------------------------------------------------- ""; "Position Sizing Method:"; PosSizeMethod = ParamList("Position Sizing Method:","Fixed Fractional Risk %|Fixed $ Risk Amount|Fixed $ Total Position Size|Fixed % of Portfolio Equity",3); //Risk Calculation /*RiskAmnt = Ref(BuyStop,-1) - Ref(TrailStop,-1); //risk amount //Fixed Fractional Risk Percentage Risk1Pcnt = Param("Fixed Fractional Risk %",1,0.01,5,0.01); Risk1 = Risk1Pcnt*(Ref(BuyStop,-1)/RiskAmnt); //Fixed $ Risk Amount Risk2Amnt = Param ("Fixed $ Risk Amount",1000,1,100000,100); Risk2 = Risk2Amnt/RiskAmnt;*/ //Fixed $ Position Size FDAmount = Param("Fixed $ Total Position Size",5000,100,100000,100); //Fixed % of Portfolio Equity FPAmount = Param("Fixed % of Portfolio Equity",10,1,100,1); /*if(PosSizeMethod == "Fixed Fractional Risk %") SetPositionSize(Risk1,spsPercentOfEquity); if(PosSizeMethod == "Fixed $ Risk Amount") SetPositionSize(Risk2,spsShares); if(PosSizeMethod == "Fixed $ Total Position Size") SetPositionSize(FDAmount,spsValue);*/ if(PosSizeMethod == "Fixed % of Portfolio Equity") SetPositionSize(FPAmount,spsPercentOfEquity); //Craig - if only using one position sizing type, I suggest you just use SetPositionSize(FPAmount,spsPercentOfEquity); and delete the rest. //==================================================================== _SECTION_END();