TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is Ken Calhoun’s article in the December 2016 issue, “Mean-Reversion Swing Trading.” Here, we present the January 2017 Traders’ Tips code with possible implementations in various software.
The Traders’ Tips section is provided to help the reader implement a selected technique from an article in this issue or another recent issue. The entries here are contributed by software developers or programmers for software that is capable of customization.
In “Mean-Reversion Swing Trading,” which appeared in the December 2016 issue of STOCKS & COMMODITIES, author Ken Calhoun describes a trading methodology where the trader attempts to enter an existing trend after there has been a pullback. He suggests looking for 50% pullbacks in strong trends and waiting for price to move back in the direction of the trend before entering the trade.
Here, we are providing the TradeStation EasyLanguage code for a mean-revision strategy based on the author’s concepts. We have also included a companion indicator to help visualize the trade setups.
Strategy: MeanReversion // TASC JAN 2017 // Mean-Reversion Swing Trading // Ken Calhoun inputs: ChanLength( 20 ), StopDollars( 1 ), MALength( 50 ) ; variables: UpperBand( 0 ), LowerBand( 0 ), MidBand( 0 ), LongOK( false ), ShortOK( false ), LowRef( 0 ), HighRef( 0 ), TriggerLine( 0 ), MAValue( 0 ) ; UpperBand = Highest( High, ChanLength ) ; LowerBand = Lowest( Low, ChanLength ) ; MAValue = Average( Close, MALength ) ; if Low = LowerBand then begin LowRef = Low ; LongOK = false ; ShortOK = true ; end ; if High = UpperBand then begin HighRef = High ; LongOK = true ; ShortOK = false ; end ; // 50% Pull Back Level TriggerLine = .5 * ( HighRef + LowRef ) ; if LongOK[1] and LongOK and Close crosses over TriggerLine and Close > MAValue and MarketPosition( 1 ) < 1 then begin Buy next bar at Market ; LongOK = false ; end else if ShortOK[1] and ShortOK and Close crosses under TriggerLine and Close < MAValue and MarketPosition( 1 ) > -1 then begin SellShort next bar at Market ; ShortOK = false ; end ; Sell next bar at Upperband Limit ; Sell next bar at Lowerband Stop ; Buy to Cover next bar at Lowerband Limit ; Buy to Cover next bar at UpperBand Stop ; SetStopShare ; SetStopLoss( StopDollars ) ; Indicator: MeanReversion // TASC JAN 2017 // Mean-Reversion Swing Trading // Ken Calhoun inputs: ChanLength( 20 ), MALength( 50 ) ; variables: UpperBand( 0 ), LowerBand( 0 ), MidBand( 0 ), LongOK( false ), ShortOK( false ), LowRef( 0 ), HighRef( 0 ), TriggerLine( 0 ), MAValue( 0 ) ; UpperBand = Highest( High, ChanLength ) ; LowerBand = Lowest( Low, ChanLength ) ; MAValue = Average( Close, MALength ) ; if Low = LowerBand then begin LowRef = Low ; LongOK = false ; ShortOK = true ; end ; if High = UpperBand then begin HighRef = High ; LongOK = true ; ShortOK = false ; end ; TriggerLine = .5 * ( HighRef + LowRef ) ; Plot1( UpperBand, "UpperBand" ) ; Plot2( LowerBand, "LowerBand" ) ; Plot3( TriggerLine, "Trigger" ) ; Plot4( MAValue, "Mov Avg" ) ; if LongOK then begin SetPlotColor( 1, Green ) ; SetPlotColor( 2, Green ) ; end else begin SetPlotColor( 1, Red ) ; SetPlotColor( 2, Red ) ; end ;
To download the EasyLanguage code for the strategy in this article, please visit our TradeStation and EasyLanguage support forum. The code can be found at https://community.tradestation.com/Discussions/Topic.aspx?Topic_ID=147651. The ELD filename is “TASC_JAN2017.ELD.”
For more information about EasyLanguage in general, please see https://www.tradestation.com/EL-FAQ.
A sample chart is shown in Figure 1.
FIGURE 1: TRADESTATION. Here’s an example of the MeanReversion strategy and indicator applied to a 60-minute chart of Alphabet (GOOG).
This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.
Ken Calhoun’s article in the December 2016 issue of STOCKS & COMMODITIES, “Mean-Reversion Swing Trading,” presented a visual trading system designed to take advantage of pullbacks in uptrends. The formula given here, used as a filter in the explorer, will find such trading opportunities. The first two lines of the formula allow the user to adjust the parameters of the scan:
The formula is presented using values of 10% minimum move and a 0.1% allowance on the difference at the reversion level.
Exploration filter: z:= 10; fudge:= 0.1; p1:= LastValue(TroughBars(1, C, z)); pv1:= Trough(1, C,z); p2:= LastValue( HHVBars( C, p1 )); pv2:= HHV( C, p1); meanrev:= (pv1 + pv2) / 2; pv3:= LLV(C, p2); p1 < PeakBars(1, C, z) AND pv2 >= pv1 * (1 + (z/100)) AND Ref(Abs(pv3 - meanrev) <= pv3 * (fudge/100), -1) AND C > Ref(C, -1) AND LLVBars(C, p2) = 1 AND pv1 >= 20 AND pv2 <= 70
For this month’s Traders’ Tip, we’ve provided the study Mean_Reversion_Swing.efs based on the formula described in Ken Calhoun’s article in the December 2016 issue of S&C, “Mean-Reversion Swing Trading.” In the article, Calhoun presents a strategy of trading pullbacks during a trending market.
The study contains formula parameters that may be configured through the “edit chart” window (right-click on the chart and select edit chart). A sample chart is shown in Figure 2.
FIGURE 2: eSIGNAL. Here is an example of the Mean_Reversion_Swing.efs study plotted on an intraday chart of APC.
To discuss this study or download a complete copy of the formula code, please visit the EFS Library Discussion Board forum under the forums link from the support menu at www.esignal.com or visit our EFS KnowledgeBase at https://www.esignal.com/support/kb/efs/. The eSignal formula script (EFS) is also available for copying & pasting below.
/********************************* Provided By: eSignal (Copyright c eSignal), a division of Interactive Data Corporation. 2016. All rights reserved. This sample eSignal Formula Script (EFS) is for educational purposes only and may be modified and saved under a new file name. eSignal is not responsible for the functionality once modified. eSignal reserves the right to modify and overwrite this EFS file with each new release. Description: Mean-Reversion Swing Trading by Ken Calhoun Version: 1.00 11/09/2016 Formula Parameters: Default: Slope 1 Trigger 0.5 Notes: The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com. **********************************/ var fpArray = new Array(); function preMain(){ setPriceStudy(true); setStudyTitle("MR Swing"); var x = 0; fpArray[x] = new FunctionParameter("Slope", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0.01); setUpperLimit(100); setDefault(1); setName("Slope"); } fpArray[x] = new FunctionParameter("Trigger", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0.000001); setDefault(0.5); setName("Trigger"); } } var bInit = false; var bVersion = null; var xDHigh = null; var xHigh = null; var xDLow = null; var xLow = null; var xDDateTime = null; var bInSwing = false; var bSwFinished = false; var nSwBarsBack = 0; var nMeanRev = 0; var nMeanHigh = 0; var nSwHigh = 0; var bMRFound = false; var dMeanRevD = null; var bFindBO = false; function main(Slope, Trigger){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getBarState() == BARSTATE_ALLBARS){ bInSwing = false; bSwFinished = false; nSwBarsBack = 0; nMeanRev = 0; nMeanHigh = 0; nSwHigh = 0; bMRFound = false; dMeanRevD = null; bFindBO = false; bInit = false; } if (!bInit){ xDHigh = high(inv("D")); xDLow = low(inv("D")); xDDateTime = rawtime(inv("D")); xLow = low(); xHigh = high(); } nDHigh = xDHigh.getValue(0); var slopeFinished = false; var i = 0; var swingLen = 0; while (!slopeFinished){ if (xDHigh.getValue(i) > xDHigh.getValue(i-1)){ swingLen++; i--; } else slopeFinished = true; } if (bInSwing && swingLen == 0){ nSwBarsBack = 0; bSwFinished = true; bInSwing = false; } if (bSwFinished && nMeanRev != 0){ nSwBarsBack++; if (xDLow.getValue(-1) < nMeanRev && day(0) != day(-1)){ drawLineRelative(-1, nMeanRev, -nSwBarsBack, nSwHigh, PS_SOLID, 3, Color.red, ("Mean rev. " + (rawtime(-nSwBarsBack)))); dMeanRevD = xDDateTime.getValue(-1); nMeanHigh = nSwHigh; nSwBarsBack = 0; bSwFinished = false; bMRFound = false; } } if (!bSwFinished && !bInSwing && !bMRFound && nMeanRev != 0 && xDDateTime.getValue(-1) == dMeanRevD){ if (xHigh.getValue(0) >= nMeanRev + Trigger){ drawTextRelative(0, BelowBar1, "\u00E9", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 10, "Entry"+rawtime(0)); drawTextRelative(0, BelowBar2, "MR", Color.green, null, Text.PRESET|Text.CENTER|Text.BOLD, "Arial", 8, "Entry text"+rawtime(0)); bMRFound = true; bFindBO = true; nMeanRev = 0; } } if (bFindBO && xHigh.getValue(0) >= nMeanHigh){ drawTextRelative(0, AboveBar1, "\u00E9", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 10, "2nd Entry"+rawtime(0)); drawTextRelative(0, AboveBar2, "BO", Color.green, null, Text.PRESET|Text.CENTER|Text.BOLD, "Arial", 8, "2nd Entry text"+rawtime(0)); bFindBO = false; } if (swingLen >=4){ var nSStart = getFirstBarIndexOfDay(xDDateTime.getValue(-(swingLen))); if(nSStart != null){ if (((nDHigh - xDHigh.getValue(-(swingLen)))/(xDLow.getValue(0) - xDLow.getValue(-(swingLen)))) > Slope){ nSwHigh = nDHigh; var nSwLow = xDLow.getValue(-(swingLen)); nMeanRev = (nSwHigh + nSwLow)/2; drawLineRelative(0, nDHigh, (nSStart - getCurrentBarIndex()), xDLow.getValue(-(swingLen)), PS_SOLID, 3, Color.green, "Uptrend" + xDDateTime.getValue(-(swingLen))); bInSwing = true; bSwFinished = false; nSwBarsBack = 0; } else{ bInSwing = false; bSwFinished = true; } } } } function verify(){ var b = false; if (getBuildNumber() < 779){ drawTextAbsolute(5, 35, "This study requires version 10.6 or later.", Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT, null, 13, "error"); drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT, null, 13, "upgrade"); return b; } else b = true; return b; }
In his December 2016 article, “Mean-Reversion Swing Trading,” author Ken Calhoun shows clean and concise rules for entering in a trend continuation following a pullback after a breakout. This pattern (widely known as “1-2-3 pattern”) could be programmed to apply to intraday or daily charts alike. Our rendition targets daily charts.
Since the rules didn’t put much emphasis on exits, leaving this to the trader, we added a simple profit target. Here, you can find the complete trading system’s rules:
Entry:
Exit:
Motivated readers could make various improvements to the program’s logic. For instance, to help this technique work equally well on stocks outside the $20–70 price range, make the stop-loss and profit target adaptive by replacing the fixed-dollar distance with a unit of ATR. Another suggestion is to avoid trades when a stock is not really trending, as the 1-2-3 pattern seems to perform best in established trends.
Wealth-Lab strategy code:
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WealthLab; using WealthLab.Indicators; namespace WealthLab.Strategies { public class MeanReversionSwingTrading : WealthScript { private StrategyParameter paramUptrendDays; private StrategyParameter paramReversionLasts; private StrategyParameter paramReversionExpiresAfter; private StrategyParameter paramSecondEntry; public MeanReversionSwingTrading() { paramUptrendDays = CreateParameter("Days in uptrend", 5, 2, 10, 1); paramReversionLasts = CreateParameter("Reversion lasts", 4, 2, 12, 2); paramReversionExpiresAfter = CreateParameter("Expires after", 10, 5, 20, 5); paramSecondEntry = CreateParameter("Second entry?", 1, 0, 1, 1); } protected override void Execute() { bool runup = false, reversion = false, pullback = false, secondEntry = paramSecondEntry.ValueInt == 1; int runupBar = -1, pBar = -1, reversionBar = -1, uptrendDays = paramUptrendDays.ValueInt; double runupStart = 0, runupHigh = 0, runupRange = 0, pivotPrice = 0, reversionThreshold = 1.0; Highest hi = Highest.Series(High,15); if( secondEntry ) PlotSeries(PricePane, hi, Color.Blue, LineStyle.Dashed, 1); for(int bar = GetTradingLoopStartBar(15); bar < Bars.Count; bar++) { if( ActivePositions.Count > 0 ) { if(!SellAtStop(bar+1, Position.AllPositions, pivotPrice - 1.0, "$1 stop")) SellAtLimit(bar + 1, Position.AllPositions, pivotPrice + 2.0, "Profit Target"); } if( ActivePositions.Count == 0 ) // First entry { if( !runup ) { if(CumUp.Series(Close,1)[bar] >= uptrendDays) { runup = true; runupBar = bar; runupStart = Low[bar-5]; runupHigh = High[runupBar]; runupRange = runupHigh - runupStart; pivotPrice = runupStart + (runupRange / 2d); DrawLine(PricePane,runupBar,runupHigh,bar-5,runupStart,Color.Blue,LineStyle.Solid,2); } } if( !pullback ) { if( runup ) { if( bar >= runupBar + paramReversionExpiresAfter.ValueInt ) { runup = false; //AnnotateBar("Pullback timeout exceeded",bar,false,Color.Red); } else { double runupInPercent = runupRange / (double)runupStart * 100d; double closeToExact = (reversionThreshold / 100d); if( (Low[bar] <= (pivotPrice * 1.0+closeToExact)) && (Low[bar] >= (pivotPrice * 1.0-closeToExact)) ) { pullback = true; pBar = bar; DrawLine(PricePane,runupBar,runupHigh,pBar,pivotPrice,Color.Red,LineStyle.Solid,2); } } } } // Buy at stop if price moves $0.50 the pivot price or above if( pullback ){ double entryStop = pivotPrice + 0.50; if( BuyAtStop( bar + 1, entryStop, "1st" ) != null ) { DrawLine(PricePane,bar,entryStop,pBar,entryStop,Color.DarkGreen,LineStyle.Dotted,2); runup = false; pullback = false; LastPosition.Tag = pivotPrice; } else // Invalidate entry if too many bars have passed since pullback if( bar >= pBar + paramReversionExpiresAfter.ValueInt ) { pullback = false; AnnotateBar("Rebound timeout exceeded",bar,true,Color.Orange); } } } else //Second entry if( ActivePositions.Count == 1 && secondEntry ) BuyAtStop(bar+1, hi[bar], "2nd"); } } } }
A sample chart is shown in Figure 3.
FIGURE 3: WEALTH-LAB. This chart shows a potential trade in HeidelbergCement AG (ticker HEI.DE in Wealth-Data).
An automated mean-reversion trading system as described by Ken Calhoun in his December 2016 article in STOCKS & COMMODITIES (“Mean-Reversion Swing Trading”) can be easily implemented using NeuroShell Trader’s Turning Points add-on indicators, which compute the relevant peak and valley statistics. Simply select new trading strategy from the insert menu and enter the following in the appropriate locations of the Trading Strategy Wizard:
BUY LONG CONDITIONS: [All of which must be true] A>=B(Add2(TPbars(High,Low,Close,3,2,1),1),5) A>=B(TPslope(High,Low,Close,3,2,1),45) CrossBelow(Close,FibRetr(High,Low,3,1,3,0)) STOP PRICE: Add2(FibRetr(High,Low,3,1,3,0),0.5) LONG TRAILING STOP PRICES: TrailPricePnts(Trading Strategy,1)
If you have NeuroShell Trader Professional, you can also choose whether the parameters should be optimized, including optimizing the retracement level to different Fibonacci retracements besides just 50%. After backtesting the trading strategy, use the detailed analysis button to view the backtest and trade-by-trade statistics for the strategy.
Users of NeuroShell Trader can go to the Stocks & Commodities section of the NeuroShell Trader free technical support website to download a copy of this or any previous Traders’ Tips.
A sample chart is shown in Figure 4.
FIGURE 4: NEUROSHELL TRADER. This NeuroShell Trader chart shows the reversion trading system over the past 10 years for Anadarko Petroleum Corp (APC).
The concept discussed by Ken Calhoun in his December 2016 article in S&C, “Mean-Reversion Swing Trading,” is now available as a strategy named “MRST” for download at the following links for NinjaTrader 8 and for NinjaTrader 7:
Once the file is downloaded, you can import the strategy in NinjaTader 8 from within the Control Center by selecting Tools → Import → NinjaScript Add-On and then selecting the downloaded file for NinjaTrader 8. To import in NinjaTrader 7 from within the Control Center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file. A sample chart implementing the strategy is shown in Figure 5.
FIGURE 5: NINJATRADER. The MRST strategy is displayed on the APC daily chart in NinjaTrader 8.
You can review the strategy’s source code in NinjaTrader 8 by selecting the menu New → NinjaScript Editor → Strategies from within the Control Center window and selecting the MRST file. You can review the strategy’s source code in NinjaTrader 7 by selecting the menu Tools → Edit NinjaScript → Strategy from within the Control Center window and selecting the MRST file.
Our Traders’ Tip for this month is based on the article by Ken Calhoun that appeared in the December 2016 issue of STOCKS & COMMODITIES, “Mean-Reversion Swing Trading.”
In the article, the author loosely defined a swing trading technique for entering long positions into uptrending instruments that have a 50% retracement, but that swing to make new highs. This can be easily realized with a point & figure–based system, since all the key signals for entry are simplified within this methodology.
FIGURE 6: UPDATA. Here is a point & figure (1x3) approach to mean-reversion swing trading as applied to Anadarko Petroleum Corp (APC).
The Updata code is in the Updata library and may be downloaded by clicking the custom menu and system library. Those who cannot access the library due to firewall issues may paste the code shown below into the Updata custom editor and save it.
PARAMETER "Box Size [%]" @Box=1 PARAMETER "Reversal" #Reversal=3 NAME "P&F Double Top Break" "" DISPLAYSTYLE Custom INDICATORTYPE Tool PANDFMODE @Box #Reversal PFLog PFClose @PFHigh=0 @PFLow=0 @LnPFHigh=0 @LnPFLow=0 @LongEntry=0 @LongExit=0 @PlusIncrement=0 #DateBegin=0 #Count=0 FOR #Curdate=0 To #LastDate If PANDFCOLUMN(2)>0 @PFHigh=PANDFHIGH(2) ElseIf PANDFCOLUMN(2)<0 @PFLow=PANDFLOW(2) EndIf @LnPFHigh=Ln(@PFHigh) @LnPFLow=Ln(@PFLow) @PlusIncrement=Ln(1+(@BOX/100)) @LongEntry=Exp(@LnPFHigh+@PlusIncrement) @LongExit=Exp(@LnPFLow-@PlusIncrement) If HIST(Close>@LongEntry AND @LnPFHigh>0,1) AND PANDFLOW(1)<(PANDFHIGH(2)+PANDFLOW(2)) Buy OPEN ElseIf HIST(Close<@LongExit AND @LnPFLow>0,1) Sell OPEN EndIf NEXT
The chart techniques based on after-the-fact determination of “waves” are highly subjective. In real-life trading, you don’t know that the wave has ended until the reversal is large enough.
A mechanical approach close to the one described by Ken Calhoun in his December 2016 column and in this issue on a mean-reversion technique was presented in the November 2003 STOCKS & COMMODITIES article “The Zigzag Trend Indicator” by Spyros Raftopoulos. In that article, a zigzag indicator that is appropriately delayed (to ensure its stability) was used to enter pullbacks. Users can find the AmiBroker code I had provided based on that article at the S&C website here:
https://traders.com/Documentation/FEEDbk_docs/2003/11/TradersTips/TradersTips.html#amibrokerThe code is also shown here:
LISTING 1 array = Close; amount = Param("Amount", 15.5, 1, 50, 0.5 ); zz0 = Zig( array, amount ); zz1 = Ref( zz0, -1 ); zz2 = Ref( zz0, -2 ); tr = ValueWhen(zz0 > zz1 AND zz1 < zz2, zz1); pk = ValueWhen(zz0 < zz1 AND zz1 > zz2, zz1); PU = tr + 0.01 * abs(tr)*amount; PD = pk - 0.01 * abs(pk)*amount; ZZT = IIf( array >= PU AND zz0 > zz1, 1, IIf( array <= PD AND zz0 < zz1, -1, 0 ) ); ZZT = ValueWhen( ZZT != 0, ZZT ); // plot price bar chart Plot( Close, "Price", colorBlack, styleBar ); // plot Zigzag and zigzag trend Plot( ZZT, "ZigZagTrend", colorRed, styleOwnScale ); Plot( zz0, "ZigZag line", colorBlue, styleThick ); // Plot the ribbon ribboncol= IIf( ZZT > 0, colorGreen, colorRed ); Plot( 2, "ZZT Ribbon", ribboncol, styleArea | styleOwnScale | styleNoLabel, 0, 100 ); GraphXSpace = 10; Buy = Cover = Cross( ZZT, 0 ); Sell = Short = Cross( 0, ZZT ); // plot arrows PlotShapes( Buy + 2 * Sell, ribboncol, 0, IIf( Buy, L, H ), -30 );
The code given there implements ideas similar to those described by Calhoun. The technique has the advantage of not relying on a strict “50%” pullback rule.