//Updated 17/05/2016 //EOD MR system using 5 day Bollinger Bands (@ 1 std deviation) that buys stocks on the dip above their 150 day MA at the lower band and sells at the middle band. Enties are based on limit orders of a minimimum of open or L-0.5ATR(10) //Best results are obtained using default parameters //ASX parameters $0.5 to $50 min TO $500,000, 10 day average //US parameters $5 to $100 min TO $1,000,000, 10 day average //ASX Commission based on IB using $6 entry and exit //US Commission based on IB using max $1 entry or $0.005 per share //IndexFilter off //******************************************************************** //Added HDBFilter to Cond3 //Deleted Cond5 = ADX(adxPeriod) >= adxMin; 24/05/16 //Added Volume Filter 30/05/16 #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",0); 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","$SPX"); Index = Foreign(IndexCode,"C"); IndMA = Param("Index Filter MA",150,2,250,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); //– this sets the drop down list UniverseFilter = 1; //– set the filter to 1 to start 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 //==================================================================== MArange = Param("MA Range",150,1,1000,1); Periods = Param("Bollinger Period",5,1,100,1); stddev = Param("Standard Deviation Bands",1,0,3,0.1); UpperBand = BBandTop( C, Periods, stddev ); LowerBand = BBandBot( C, Periods, stddev ); percentb = (C - LowerBand)/(UpperBand - LowerBand); /* %b = (Price - Lower Band)/(Upper Band - Lower Band) %b equals 1 when price is at the upper band %b equals 0 when price is at the lower band %b is above 1 when price is above the upper band %b is below 0 when price is below the lower band %b is above .50 when price is above the middle band (5-day SMA) %b is below .50 when price is below the middle band (5-day SMA) */ lowerbandlimit = Param("Lower Band Limit",0.0,-0.5,0.5,0.1); upperbandlimit = Param("Upper Band Limit",0.5,0,1.5,0.1); function ParamOptimize(pname,defaultval,minv,maxv,step) { return Optimize(pname,Param(pname,defaultval,minv,maxv,step),minv,maxv,step); } //-------------------------------------------------------------------- //Limit Entry using the stretch //-------------------------------------------------------------------- ATRp = Param("Limit Price: ATR period",10,1,20,1); ATRmulti = Param("Limit Price: ATR Multi",0.5,0,1.5,.1); ATRVal = ATR(ATRp)*ATRmulti; BuyLimP = L - ATRval; //stretch TickLo = IIf(MarketID(0) == 10 OR MarketID(0) == 14 ,IIf(L<0.10,0.001,IIf(L<2.00,0.005,0.01)),0.01); BuyLimVal = round(BuyLimP/TickLo); BuyLim = BuyLimVal * TickLo; //stretch Cond1 = LLV(percentb < lowerbandlimit,3); //3 consecutive days below lower band Cond2 = C > MA(C,MArange); Cond3 = OptFilt AND IndexUp AND UniverseFilter AND HDBFilter; Cond4 = Volatility = abs(C-Ref(C,-1))>= 0.5*ATR(10) AND abs(C-Ref(C,-1))<= 2*ATR(10); //Limit volatility to these limits OnLastTwoBarsOfDelistedSecurity = BarIndex() >= (LastValue(BarIndex()) -1) AND !IsNull(GetFnData("DelistingDate")); OnSecondLastBarOfDelistedSecurity = BarIndex() == (LastValue(BarIndex()) -1) AND !IsNull(GetFnData("DelistingDate")); BuySetUp = Cond1 AND Cond2 AND Cond3 AND Cond4; LE = Ref(BuySetUp,-1) AND L <= Ref(BuyLim,-1) AND NOT OnLastTwoBarsOfDelistedSecurity; LEPrice = Min(O,ref(BuyLim,-1)); LExPrice = O; SellSetUp = percentb > upperbandlimit; //sell at middle band LEx = Ref(SellSetUp,-1); //-------------------------------------------------------------------- //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 ); LBIT[j] = 1; if( LBIT[j] > 1 AND LEx[j] OR OnSecondLastBarOfDelistedSecurity[j] ) { Sell[j] = True; SellPrice[j] = LExPrice[j]; LPriceAtBuy = 0; } if( LBIT[j] > 1 AND L[j - 1] <= MaxLossPrice[j - 1] ) { Sell[j] = True; SellPrice[j] = LExPrice[j]; 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]) { 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 //-------------------------------------------------------------------- /*Filter = 1; AddColumn(BuySetUp,"Buy Setup",1); AddColumn(Buy,"Buy",1); AddColumn(SellSetUp, "Sell Setup",1); AddColumn(Sell,"Sell",1); */ //==================================================================== //Interpretation Window //==================================================================== //"Stop Level: "+NumToStr(StopLossExit,1.3); "3 Lower Closes and below lower band: "+WriteIf(Cond1,"Yes","No"); "Above long term MA: "+WriteIf(Cond2,"Yes","No"); "Index Filer On: "+WriteIf(IndexTog,"No","Yes"); ""; "Bars In Trade: "+NumToStr(LBIT,1); "Trail Stop: "+NumToStr(MaxLossPrice,1.3); "Trail Stop activated?: "+WriteIf(L<= MaxLossPrice,"Yes","No"); "Close above mid band: "+WriteIf(SellSetUp,"Yes","No"); //==================================================================== _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",Max(1,0.005*spsShares)); //SetOption("CommissionMode",3); //Fixed Dollar Amount SetOption("CommissionAmount",1); //Use IB commision rates 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 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); //==================================================================== _SECTION_END(); //-------------------------------------------------------------------- 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",""),"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(round(0.01*FPAmount*Capital/LEPrice),"No to Buy",1.0); AddColumn(LEPrice,"Buy Price",1.2); AddColumn(( 1 - StopLossPercentage / 100 )*LEPrice,"Stop Loss",1.2); AddColumn(LBIT,"LBIT",1.0); SetSortColumns(-3,-4,-5,-6,1);