TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is Domenico D’Errico’s article in this issue, “Detecting Swings.” Here, we present the May 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 “Detecting Swings” in this issue, author Domenico D’Errico presents four methods for identifying price swings in charts. He describes these swings as being the result of the actions of what he deems big players and tells us that understanding these should be one of the main goals of technical traders. The author’s methods for detecting swings include using pivots, Bollinger Bands, and the relative strength indicator.
In a sidebar to the article he provides some TradeStation EasyLanguage code for a strategy to test the four swing methods. Here, we are providing the EasyLanguage code for an additional version that allows the strategy to be fully optimized in the TradeStation platform.
Strategy: Detecting Swings // TASC May 2017 // Detecting Swings // Author Domenico D'Errico inputs: Strategy( 1 ), PivotStrength( 2 ), RSILength( 5 ), RSIOverBought( 60 ), RSIOverSold( 40 ), BollingerBandLength( 12 ), BollingerBandNumDevs( 2 ), BeginTradingDate( 1000101 ), NumberOfBarsToExit( 4 ), ExitOnDate( 1171230 ), Capital( 10000 ) ; variables: PivotLow( false ) , PivotHigh( false ) , RSIValue( 0 ), UpperBollingerBand( 0 ), LowerBollingerBand( 0 ), Qty( 0 ), MP( 0 ); MP = MarketPosition ; Qty = Capital / Close ; PivotLow = PivotLowVSBar( 1, Low, PivotStrength, PivotStrength, PivotStrength + 1 ) <> -1 ; PivotLow = PivotHighVSBar( 1, High, PivotStrength, PivotStrength, PivotStrength + 1 ) <> -1 ; RSIValue = RSI( Close, RSILength ) ; UpperBollingerBand = BollingerBand( Close, BollingerBandLength, BollingerBandNumDevs ) ; LowerBollingerBand = BollingerBand( Close, BollingerBandLength, -BollingerBandNumDevs ) ; if Date > BeginTradingDate and Date < ExitOnDate then begin // Pivot Entry if Strategy = 1 and MP = 0 then begin If PivotLow then Buy Qty Shares next bar at Market else If PivotHigh then SellShort Qty Shares next bar at Market ; end ; //Bollinger Bands if Strategy = 2 and MP = 0 then begin If Close crosses over LowerBollingerBand then Buy Qty Shares next bar at Market else if Close crosses under UpperBollingerBand then SellShort Qty Shares next bar at Market ; end ; //RSI Cross If Strategy = 3 and MP = 0 then begin If RSIValue crosses over RSIOverSold then Buy Qty Shares next bar at Market else if RSIValue crosses under RSIOverBought then SellShort Qty Shares next bar at Market ; end ; //RSI + higher Low / lower high If Strategy = 4 and MP = 0 then begin If RSIValue < RSIOverSold and Low > Low[1] then Buy Qty Shares next bar at Market else if RSIValue > RSIOverBought and High < High[1] then SellShort Qty Shares next bar at Market ; end ; end ; //Exit If BarsSinceEntry = NumberOfBarsToExit or Date >= ExitOnDate then begin Sell next bar at Market ; Buytocover next bar at Market ; End ;
To download the EasyLanguage code for this indicator and function, 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_MAY2017.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. The “detecting swings” strategy is applied to a weekly chart of the Materials Select Sector SPDR (XLB).
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.
Domenico D’Errico’s article in this issue, “Detecting Swings,” presents four trading strategies for swing trading. Here are the MetaStock formulas for these strategies:
Strategy 1 (Pivot High-Low): Buy Long formula: el:= Ref(H, -1) > Max( H, Ref(H, -2)); es:= Ref(L, -1) < Min( L, Ref(L, -2)); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = 1 and Ref( ltrade <= 0, -1) Sell formula: el:= Ref(H, -1) > Max( H, Ref(H, -2)); es:= Ref(L, -1) < Min( L, Ref(L, -2)); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = -1 Sell Short formula: el:= Ref(H, -1) > Max( H, Ref(H, -2)); es:= Ref(L, -1) < Min( L, Ref(L, -2)); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = 1 and Ref( strade <= 0, -1) Buy to Cover formula: el:= Ref(H, -1) > Max( H, Ref(H, -2)); es:= Ref(L, -1) < Min( L, Ref(L, -2)); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = -1 Strategy 2 (Bollinger Bands): Buy Long formula: el:= Cross( C, BBandBot(C, 20, S, 2)); es:= Cross( BBandTop(C, 20, S, 2), C); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = 1 and Ref( ltrade <= 0, -1) Sell formula: el:= Cross( C, BBandBot(C, 20, S, 2)); es:= Cross( BBandTop(C, 20, S, 2), C); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = -1 Sell Short formula: el:= Cross( C, BBandBot(C, 20, S, 2)); es:= Cross( BBandTop(C, 20, S, 2), C); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = 1 and Ref( strade <= 0, -1) Buy to Cover formula: el:= Cross( C, BBandBot(C, 20, S, 2)); es:= Cross( BBandTop(C, 20, S, 2), C); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = -1 Strategy 3 (RSI Cross): Buy Long formula: el:= Cross( RSI(5), 40); es:= Cross( 60, RSI(5)); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = 1 and Ref( ltrade <= 0, -1) Sell formula: el:= Cross( RSI(5), 40); es:= Cross( 60, RSI(5)); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = -1 Sell Short formula: el:= Cross( RSI(5), 40); es:= Cross( 60, RSI(5)); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = 1 and Ref( strade <= 0, -1) Buy to Cover formula: el:= Cross( RSI(5), 40); es:= Cross( 60, RSI(5)); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = -1 Strategy 4 (RSI & higher Low / lower High): Buy Long formula: el:= RSI(5) < 40 AND L > Ref(L, -1); es:= RSI(5) > 60 AND H < Ref(H, -1); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = 1 and Ref( ltrade <= 0, -1) Sell formula: el:= RSI(5) < 40 AND L > Ref(L, -1); es:= RSI(5) > 60 AND H < Ref(H, -1); ltrade:= If(PREV<=0, If(el, 1, 0), If(es OR (BarsSince(PREV<=0)>=4), -1, PREV)); ltrade = -1 Sell Short formula: el:= RSI(5) < 40 AND L > Ref(L, -1); es:= RSI(5) > 60 AND H < Ref(H, -1); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = 1 and Ref( strade <= 0, -1) Buy to Cover formula: el:= RSI(5) < 40 AND L > Ref(L, -1); es:= RSI(5) > 60 AND H < Ref(H, -1); strade:= If(PREV<=0, If(es, 1, 0), If(el OR (BarsSince(PREV<=0)>=4), -1, PREV)); strade = -1
For this month’s Traders’ Tip, we’re providing four studies: BollingerBands.efs, Pivot_HighLow.efs, RSI_Cross.efs, and RSI_Cross_HLLH.efs based on the formulas described in Domenico D’Errico’s article in this issue, “Detecting Swings.” In the article, the author presents four ways to analyze price swings.
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 studies plotted on a weekly chart of XLI.
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 scripts (EFS) are 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: Up & Down Detecting Swings by Domenico D'Errico Version: 1.00 03/14/2017 Formula Parameters: Default: Period (W) 4 Length 12 StdDev 2 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("BollingerBands"); setCursorLabelName("Upper", 0); setCursorLabelName("Lower",1); var x=0; fpArray[x] = new FunctionParameter("Period", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Period (W)"); setDefault(4); setLowerLimit(1); } fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Length"); setDefault(12); setLowerLimit(1); } fpArray[x] = new FunctionParameter("StdDev", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("StdDev"); setDefault(2); setLowerLimit(1); } } var bInit = false; var bVersion = null; var xHigh = null; var xLow = null; var xUpper = null; var xLower = null; var positionDate = null; var rawDay = 86400; function main(Period, Length, StdDev){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= Length) return; if (getBarState() == BARSTATE_ALLBARS){ bInit = false; } if (!bInit){ xHigh = high(); xLow = low(); xUpper = upperBB(Length, StdDev); xLower = lowerBB(Length, StdDev); positionDate = null; bInit = true; } if (Strategy.isInTrade()){ if (rawtime(0) >= (positionDate + rawDay * (Period * 7))){ if (Strategy.isLong()){ Strategy.doSell("BB CROSS OVER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (Strategy.isShort()){ Strategy.doCover("BB CROSS UNDER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } } if (getCurrentBarIndex() != 0){ if (!Strategy.isInTrade()){ if (xLow.getValue(-1) < xLower.getValue(-1) && xLow.getValue(0) > xLower.getValue(0)){ Strategy.doLong("BB CROSS OVER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0,BelowBar2, "\u00E9", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } else if (xHigh.getValue(-1) > xUpper.getValue(-1) && xHigh.getValue(0) < xUpper.getValue(0)){ Strategy.doShort("BB CROSS UNDER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0, AboveBar2, "\u00EA", Color.red, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } } } return [xUpper.getValue(0), xLower.getValue(0)]; } function verify(){ var b = false; if (getBuildNumber() < 3742){ drawTextAbsolute(5, 35, "This study requires version 12.1 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; }
/********************************* 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: Up & Down Detecting Swings by Domenico D'Errico Version: 1.00 03/14/2017 Formula Parameters: Default: Period 4 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("Pivot H/L"); var x=0; fpArray[x] = new FunctionParameter("Period", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Period"); setDefault(4); setLowerLimit(1); } } var bInit = false; var bVersion = null; var xHigh = null; var xLow = null; var positionDate = null; var rawDay = 86400; function main(Period){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getBarState() == BARSTATE_ALLBARS){ bInit = false; } if (!bInit){ xHigh = high(); xLow = low(); positionDate = null; bInit = true; } if (Strategy.isInTrade()){ if (rawtime(0) >= (positionDate + (rawDay * Period * 7))){ if (Strategy.isLong()){ Strategy.doSell("PIVOT LOW Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (Strategy.isShort()){ Strategy.doCover("PIVOT HIGH Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } } if (getCurrentBarIndex() != 0){ if (!Strategy.isInTrade()){ if (IsPivotLow(xLow)){ Strategy.doLong("PIVOT LOW", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(-1,BelowBar1, "\u009F", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 11, positionDate); } else if (IsPivotHigh(xHigh)){ Strategy.doShort("PIVOT HIGH", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(-1, AboveBar1, "\u009F", Color.red, null, Text.PRESET|Text.CENTER, "Wingdings", 11, positionDate); } } } return; } function IsPivotLow(xLow){ var res = false; if (xLow != null){ if (xLow.getValue(-1) < xLow.getValue(0) && xLow.getValue(-1) < xLow.getValue(-2)) res = true; } return res; } function IsPivotHigh(xHigh){ var res = false; if (xHigh != null){ if (xHigh.getValue(-1) > xHigh.getValue(0) && xHigh.getValue(-1) > xHigh.getValue(-2)) res = true; } return res; } function verify(){ var b = false; if (getBuildNumber() < 3742){ drawTextAbsolute(5, 35, "This study requires version 12.1 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; }
/********************************* 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: Up & Down Detecting Swings by Domenico D'Errico Version: 1.00 03/14/2017 Formula Parameters: Default: Period (W) 4 Length 5 Overbought Level 60 Oversold Level 40 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(false); setStudyTitle("RSI Cross"); setDefaultBarFgColor(Color.grey); setCursorLabelName("RSI"); var x=0; fpArray[x] = new FunctionParameter("Period", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Period (W)"); setDefault(4); setLowerLimit(1); } fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Length"); setDefault(5); setLowerLimit(1); } fpArray[x] = new FunctionParameter("UpLevel", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Overbought level"); setDefault(60); } fpArray[x] = new FunctionParameter("LwLevel", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Oversold Level"); setDefault(40); } } var bInit = false; var bVersion = null; var xRSI = null; var positionDate = null; var rawDay = 86400; function main(Period, Length, UpLevel, LwLevel){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= Length) return; if (getBarState() == BARSTATE_ALLBARS){ bInit = false; } if (!bInit){ xRSI = rsi(Length); addBand(UpLevel, PS_DASH, 1, Color.grey, "Overbought"); addBand(LwLevel, PS_DASH, 1, Color.grey, "Oversold"); positionDate = null; bInit = true; } if (Strategy.isInTrade()){ if (rawtime(0) >= (positionDate + rawDay * (Period * 7))){ if (Strategy.isLong()){ Strategy.doSell("RSI CROSS OVER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (Strategy.isShort()){ Strategy.doCover("RSI CROSS UNDER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } } if (getCurrentBarIndex() != 0){ if (!Strategy.isInTrade()){ if (xRSI.getValue(-1) < LwLevel && xRSI.getValue(0) > LwLevel){ Strategy.doLong("RSI CROSS OVER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0, BottomRow1, "\u00E9", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } else if (xRSI.getValue(-1) > UpLevel && xRSI.getValue(0) < UpLevel){ Strategy.doShort("RSI CROSS UNDER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0, TopRow1, "\u00EA", Color.red, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } } } if (xRSI.getValue(0) > UpLevel) setBarFgColor(Color.red); else if (xRSI.getValue(0) < LwLevel) setBarFgColor(Color.green); return xRSI.getValue(0); } function verify(){ var b = false; if (getBuildNumber() < 3742){ drawTextAbsolute(5, 35, "This study requires version 12.1 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; }
/********************************* 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: Up & Down Detecting Swings by Domenico D'Errico Version: 1.00 03/14/2017 Formula Parameters: Default: Period (W) 4 Length 5 Overbought Level 60 Oversold Level 40 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(false); setStudyTitle("RSI Cross HLLH"); setDefaultBarFgColor(Color.grey); setCursorLabelName("RSI"); var x=0; fpArray[x] = new FunctionParameter("Period", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Period (W)"); setDefault(4); setLowerLimit(1); } fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Length"); setDefault(5); setLowerLimit(1); } fpArray[x] = new FunctionParameter("UpLevel", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Overbought level"); setDefault(60); } fpArray[x] = new FunctionParameter("LwLevel", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Oversold Level"); setDefault(40); } } var bInit = false; var bVersion = null; var xRSI = null; var xHigh = null; var xLow = null; var positionDate = null; var rawDay = 86400; function main(Period, Length, UpLevel, LwLevel){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= Length) return; if (getBarState() == BARSTATE_ALLBARS){ bInit = false; } if (!bInit){ xRSI = rsi(Length); xHigh = high(); xLow = low(); addBand(UpLevel, PS_DASH, 1, Color.grey, "Overbought"); addBand(LwLevel, PS_DASH, 1, Color.grey, "Oversold"); positionDate = null; bInit = true; } if (Strategy.isInTrade()){ if (rawtime(0) >= (positionDate + rawDay * (Period * 7))){ if (Strategy.isLong()){ Strategy.doSell("RSI CROSS OVER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (Strategy.isShort()){ Strategy.doCover("RSI CROSS UNDER Exit", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } } if (getCurrentBarIndex() != 0){ if (!Strategy.isInTrade()){ if (xRSI.getValue(0) < LwLevel && xLow.getValue(0) > xLow.getValue(-1)){ Strategy.doLong("RSI CROSS OVER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0, BottomRow1, "\u00E9", Color.green, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } else if (xRSI.getValue(0) > UpLevel && xHigh.getValue(0) < xHigh.getValue(-1)){ Strategy.doShort("RSI CROSS UNDER", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); positionDate = rawtime(0); drawTextRelative(0, TopRow1, "\u00EA", Color.red, null, Text.PRESET|Text.CENTER, "Wingdings", 10, positionDate); } } } if (xRSI.getValue(0) > UpLevel) setBarFgColor(Color.red); else if (xRSI.getValue(0) < LwLevel) setBarFgColor(Color.green); return xRSI.getValue(0); } function verify(){ var b = false; if (getBuildNumber() < 3742){ drawTextAbsolute(5, 35, "This study requires version 12.1 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 “Detecting Swings” in this issue, author Domenico D’Errico lays out an idea about why stock prices swing the way they do, and he lays out a clear way to detect these swings. D’Errico’s strategy uses pivot points, RSI, and Bollinger Bands to determine price swings. We took this strategy and recreated it using our proprietary scripting language, thinkscript. We have made the loading process extremely easy—simply click on the link https://tos.mx/dGNlkr, then choose to view thinkScript strategy.
In Figure 3 you can see the PriceSwing strategy added to a six-month daily chart of Citigroup. The image displays the Bollinger Band version of strategy, but D’Errico built his strategy to allow the chartist to choose the strategy to use on the fly. The choices for strategies are: “Pivot High-Low,” “Bollinger Bands Crossover,” “RSI Crossover,” or “RSI + Higher Low/Lower High.”
FIGURE 3: THINKORSWIM. Here you can see the PriceSwing Strategy added to a six-month daily chart of Citigroup.
Users can find more detail about the PriceSwings strategy in D’Errico’s article in this issue.
There are a range of popular techniques that traders use to detect price swings, and Domenico D’Errico’s article in this issue, “Detecting Swings,” throws light on some more creative approaches to the problem. But first, let’s start with the definition of one commonly used technique, the pivot high/low. The traditional definition of a high/low pivot is the occurrence of one to two consecutive lower lows followed by one to two consecutive higher lows. In his article text, D’Errico gives a different definition, where “the previous bar’s low is lower than the previous two bars and the current bar’s low.” However, the illustration shown in the author’s Figure 2 of the article as well as the code given in his sidebar leans more toward the traditional definition.
Compare that to the zigzag swing chart shown in the author’s Figure 3. (Yes, that’s actually another “undocumented” fifth way to build a swing chart based on absolute or relative [percentage] movements from an established top or bottom.) As can be seen in that figure, several actual pivot highs and lows in the middle of the “legs” are ignored as price noise by this technique.
Next, the author shows that the usage of RSI or Bollinger Band (BB) crossovers can be as multifaceted as to define pivot points. For example, I heard about some research some traders did that went as far as building synthetic price bars (such as renko) based on the RSI crossovers. A new renko brick is drawn each time the close price exceeds a fixed amount, and a new bar is created when an event like an RSI crossover/crossunder takes place.
While this sort of implementation may sound intriguing at first, a look into the code in the author’s sidebar puts everything back in its place. In its present state, the code is simply presenting tried and true crossovers and crossunders. However, the off-the-shelf RSI crossovers can be misguiding when detecting price swings. For example, the author’s Figure 2 shows the successive swings in a trading range, but this is what typically happens in strong, lasting trends. Even a series of correctional movements can take place without affecting the price significantly, yet the author’s system would repeatedly signal an “RSI swing high” and would sell short an established uptrend. A possible way to alleviate this might be to ignore successive RSI swings in the same direction.
That’s why various adaptive RSI thresholds have been introduced by several technical analysts. A well-known idea of dynamic RSI zones, which was described in David Sepiashvili’s February 2006 article in Technical Analysis of STOCKS & COMMODITIES, “The Self-Adjusting RSI,” provides a solution to this problem by creating thresholds that change depending on the standard deviation of the RSI. At Wealth-Lab, I designed my own rendition of RSI that uses an adaptive lookback period built on the frequency of pivot point swings. Long story short, if someone wishes to build an RSI swing chart, an improvement effort is required.
On a closing note, swing trading typically implies that positions are closed within days, rarely up to two weeks. While closing out a position after four weeks may be a universal approach to benchmark the performance of some strategies, a more adaptive way might be to exit, for example, as soon as the opposite pivot point is formed. However, this point alone, as well as other aspects of these techniques, could on their own make for a whole other article, so I’ll stop here for now and save more discussion and suggestions for another time.
Most of the techniques described in the article are already available in WealthScript and can be downloaded within Wealth-Lab or can be built interactively. Simply install the community indicators library to enjoy the range of pivot indicators:
Any Wealth-Lab user can backtest RSI or Bollinger Band crossovers without having to code. A crossover of any suitable indicator can be easily added to interactive rule-based strategies created from the building blocks called conditions.
Similarly, the RSI + higher low/lower high can be easily built in a drag & drop manner, as illustrated in Figure 4. Figure 5 shows some long and short trades taken by the pivot high/low approach.
FIGURE 4: WEALTH-LAB. This shows how the RSI + higher low/lower high can be built in a drag & drop manner as a rule-based strategy.
FIGURE 5: WEALTH-LAB. This shows some example long and short trades taken by the pivot high/low approach.
If you’re interested in evaluating the performance of swings as D’Errico defines them in his article, give the code shown here a try!
Wealth-Lab 6 strategy code (C#): using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WealthLab; using WealthLab.Indicators; namespace WealthLab.Strategies { public class MyStrategy : WealthScript { protected override void Execute() { for(int bar = GetTradingLoopStartBar(2); bar < Bars.Count; bar++) { bool PivotLow = (Low[bar-1] < Low[bar-2]) && (Low[bar-1] < Low[bar]); bool PivotHigh = (High[bar-1] > High[bar-2]) && (High[bar-1] > High[bar]); if (IsLastPositionActive) { Position p = LastPosition; if (bar+1 - p.EntryBar >= 20) ExitAtMarket( bar+1, p, "After 4 weeks" ); } else { if (PivotLow) BuyAtMarket(bar+1); else if (PivotHigh) ShortAtMarket(bar+1); } } } } }
In “Detecting Swings” in this issue, author Domenico D’Errico performs a comparison of backtests of four price swing identification methods. This backtest comparison can be easily implemented in NeuroShell Trader. Simply select new trading strategy from the insert menu for each of the different systems and enter the following in the appropriate locations of the trading strategy wizard:
Pivot High/Low Trading Strategy BUY LONG CONDITIONS: Pivot Point: Top Flag(High) SELL LONG CONDITIONS: BarsSinceFill>=X(Pivot High/Low Trading Strategy,20) SELL SHORT CONDITIONS: Pivot Point: Bottom Flag(Low) COVER SHORT CONDITIONS: BarsSinceFill>=X(Pivot High/Low Trading Strategy,20) Bollinger Bands Trading Strategy BUY LONG CONDITIONS: CrossAbove(Close,BB Low(Close,20,2)) SELL LONG CONDITIONS: BarsSinceFill>=X(Bollinger Bands Trading Strategy,20) SELL SHORT CONDITIONS: CrossBelow(Close,BB High(Close,20,2)) COVER SHORT CONDITIONS: BarsSinceFill>=X(Bollinger Bands Trading Strategy,20) RSI Cross Trading Strategy BUY LONG CONDITIONS: CrossAbove(RSI(Close,5),30) SELL LONG CONDITIONS: BarsSinceFill>=X(RSI Cross Trading Strategy,20) SELL SHORT CONDITIONS: CrossBelow(RSI(Close,5),70) COVER SHORT CONDITIONS: BarsSinceFill>=X(RSI Cross Trading Strategy,20) RSI + Higher low/Lower High Trading Strategy BUY LONG CONDITIONS: [All of which must be true] A<=B(RSI(Close,5),30) A>B(Low,Lag(Low,1)) SELL LONG CONDITIONS: BarsSinceFill>=X(RSI + Higher low/Lower High TS,20) SELL SHORT CONDITIONS: [All of which must be true] A>=B(RSI(Close,5),70) A<B(High,Lag(High,1)) COVER SHORT CONDITIONS: BarsSinceFill>=X(RSI + Higher low/Lower High TS,20)
If you have NeuroShell Trader Professional, you can also choose whether the parameters should be optimized. 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 6.
FIGURE 6: NEUROSHELL TRADER. This NeuroShell Trader chart shows the four different price swing systems and a comparison of their system equity lines for the SPY.
The AIQ code based on Domenico D’Errico’s article in this issue, “Detecting Swings,” is provided at www.TradersEdgeSystems.com/traderstips.htm.
I tested the author’s four systems using the NASDAQ 100 list of stocks on weekly bars, as did the author, from 3/16/2005 through 3/14/2017. Figure 7 shows the comparative metrics of the four systems using the four-week exit. The results were quite different than the author’s, probably due to a different test portfolio and also a 10-year test period rather than the author’s 20-year period. In addition, my test results show longs only, whereas the author’s results are the average of both the longs and shorts.
FIGURE 7: AIQ. As coded in EDS, this shows the metrics for the author’s four systems run on NASDAQ 100 stocks (weekly bar data) over the period 3/16/2005 to 3/14/2007.
The Bollinger Band (Buy2) system showed the worst results, whereas the author’s results showed the Bollinger Band system as the best. The pivot system (Buy1) showed the best results, whereas the author’s results showed the pivot system as the worst. I am not showing here the comparative test results for the Sell1 thru Sell4 rules, as all showed an average loss over this test period.
!DECTECTING SWINGS !Author: Domenico D'Errico, TASC May 2017 !Coded by: Richard Denning, 3/15/17 !www.TradersEdgeSystems.com !Set to WEEKLY in properties Low is [low]. Low1 is valresult(Low,1). Low2 is valresult(Low,2). High is [high]. High1 is valresult(High,1). High2 is valresult(High,2). PivotLow if Low1 < Low2 and Low1 < Low. PivotHigh if High1 > High2 and High1 > High. Buy1 if PivotLow. Sell1 if PivotHigh. !Set parameter for bollinger bands to 12 with 2 sigma (weekly) in charts: Buy2 if [close] > [Lower BB] and valrule([close] <= [Lower BB],1). Sell2 if [close] < [Upper BB] and valrule([close] >= [Upper BB],1). !Set parameter for Wilder RSI to 5 (weekly) in charts: Buy3 if [RSI Wilder] > 40 and valrule([RSI Wilder] <= 40,1). Sell3 if [RSI Wilder] < 60 and valrule([RSI Wilder] >= 60,1). Buy4 if [RSI Wilder] < 40 And Low > Low1. Sell4 if [RSI Wilder] > 60 And High < High1. Exit if {position days} >= 4.
The code and EDS file can be downloaded from www.TradersEdgeSystems.com/traderstips.htm.
The TradersStudio code based on Domenico D’Errico’s article in this issue, “Detecting Swings,” can be found at www.TradersEdgeSystems.com/traderstips.htm.
I tested the author’s four systems using 23 futures contracts on weekly bars from 11/27/2002 through 4/5/2013. Figure 8 shows the comparative metrics of the four systems using the four-week exit. The results were quite different than the author’s, probably due to a different test portfolio and also a 10-year test period rather than the author’s 20-year period.
FIGURE 8: TRADERSSTUDIO. Here is a table showing the metrics for the author’s four systems run on a portfolio of futures contracts (weekly bar data) over the period 11/27/2002 through 4/5/2003.
The following code file is provided in the download:
All of the systems showed losses on the short side. On the long side, the pivot system (parameter=1) showed the best results followed by the RSI crossover system (parameter=3). The RSI with higher low/lower high (parameter=4) lost money on both longs and shorts and performed the worst of the four.
The TradersStudio code is shown here:
'DECTECTING SWINGS 'Author: Domenico D'Errico, TASC May 2017 'Coded by: Richard Denning, 3/15/17 'www.TradersEdgeSystems.com Sub MULTI(Strategy) Dim PivotLow As Boolean Dim PivotHigh As Boolean PivotLow = False PivotLow = Low[1] < Low[2] And Low[1] < Low PivotHigh = False PivotHigh = High[1] > High[2] And High[1] > High 'Qty=10000/Close If Date > MigrateDate(1000101) Then If Strategy=1 And GetMarketPosBack(0)=0 Then If PivotLow Then Buy("", 1, 0, Market, Day) End If If PivotHigh Then Sell("", 1, 0, Market, Day) End If End If If Strategy=2 And GetMarketPosBack(0)=0 Then If CrossesOver(Close, bollingerband(Close,12,-2)) Then Buy("", 1, 0, Market, Day) End If If CrossesUnder(Close, bollingerband(Close,12, 2)) Then Sell("", 1, 0, Market, Day) End If End If If Strategy=3 And GetMarketPosBack(0)=0 Then If CrossesOver(RSI(Close, 5, 0), 40) Then Buy("", 1, 0, Market, Day) End If If CrossesUnder(RSI(Close, 5, 0), 60) Then Sell("", 1, 0, Market, Day) End If End If If Strategy=4 And GetMarketPosBack(0)=0 Then If RSI(Close, 5, 0)< 40 And L>L[1] Then Buy("", 1, 0, Market, Day) End If If RSI(Close, 5, 0)> 60 And H<H[1] Then Sell("", 1, 0, Market, Day) End If End If End If If BarsSinceEntry-1=4 Or Date >= MigrateDate(1161230) Then ExitLong("", "", 1, 0, Market, Day) ExitShort("", "", 1, 0, Market, Day) End If End Sub
The DetectingSwings strategy, as discussed in the article “Detecting Swings” in this issue by Domenico D’Errico, is available for download at the following links for NinjaTrader 8 and 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.
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 DetectingSwings 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 DetectingSwings file.
NinjaScript uses compiled DLLs that run native, not interpreted, which provides you with the highest performance possible.
A sample chart implementing the strategy is shown in Figure 9.
FIGURE 9: NINJATRADER. The DetectingSwings strategy displays the short entry for the close price crossing below the Bollinger Upper Band and the long entry for the close price crossing above the Bollinger Lower Band on a ES 06-17 hourly chart for March 10, 2016.
Our Traders’ Tip for this month is based on the article by Domenico D’Errico in this issue, “Detecting Swings.”
In the article, the author proposes four systems to be used alternately to better capture swings in the market. The systems are based on RSIs, pivots, and Bollinger Band types of entries. All parameters are optimizable within Updata to better select past values with a positive expectancy going forward.
The Updata code for this article 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.
DISPLAYSTYLE 2LINES PARAMETER "RSI Period" #RSIPeriod=14 PARAMETER "Bollinger Period" #BollPeriod=20 PARAMETER "Bollinger Deviation" @DEV=2 PARAMETER "Pivot Length" #PivotLength=5 PARAMETER "Strategy" #Strategy=4 NAME DETECTING SWINGS @RSI=0 @BOLLUPPER=0 @BOLLLOWER=0 @PIVOTUP=0 @PIVOTDOWN=0 FOR #CURDATE=50 TO #LASTDATE @RSI=RSI(#RSIPeriod) @BOLLUPPER=BOLL(#BollPeriod,@DEV,U) @BOLLLOWER=BOLL(#BollPeriod,@DEV,L) 'Measure HigherHighs/LowerLows of any length @PIVOTUP=SGNL(HIGH(1)>HIGH(2),#PivotLength,M)*#PivotLength @PIVOTDOWN=SGNL(LOW(1)<LOW(2),#PivotLength,M)*#PivotLength IF #Strategy=1 IF @RSI<30 AND LOW>PLOW(LOW(1),#RSIPeriod,1) BUY CLOSE ENDIF IF @RSI>70 AND HIGH<PHIGH(HIGH(1),#RSIPeriod,1) SHORT CLOSE ENDIF 'INDICATORTYPE CHART @PLOT=@RSI ELSEIF #Strategy=2 IF HASX(@RSI,30,UP) BUY CLOSE ENDIF IF HASX(@RSI,70,DOWN) SHORT CLOSE ENDIF 'INDICATORTYPE CHART @PLOT=@RSI ELSEIF #Strategy=3 IF @PIVOTDOWN=1 AND LOW>LOW(1) BUY CLOSE ENDIF IF @PIVOTUP=1 AND HIGH<HIGH(1) SHORT CLOSE ENDIF 'INDICATORTYPE TOOL @PLOT=@PIVOTUP @PLOT2=@PIVOTDOWN ELSEIF #Strategy=4 IF HASX(CLOSE,@BOLLLOWER,UP) BUY CLOSE ENDIF IF HASX(CLOSE,@BOLLUPPER,DOWN) SHORT CLOSE ENDIF 'INDICATORTYPE TOOL @PLOT=@BOLLUPPER @PLOT2=@BOLLLOWER ENDIF 'Cover any open positions after 4wks/30 days IF ORDEROPENFOR>30 SELL CLOSE COVER CLOSE ENDIF NEXT
A sample chart is shown in Figure 10.
FIGURE 10: UPDATA. Here, the Bollinger Band strategy is applied to the material sector ETF XLB of daily resolution.
In his article “Detecting Swings” in this issue, Domenico D’Errico shows four simple methods to detect swings. A ready-to-use AmiBroker formula that implements a simple trading system based on those methods is presented here.
// one bar delay (trade next bar) SetTradeDelays( 1, 1, 1, 1 ); // trade on open BuyPrice = SellPrice = ShortPrice = CoverPrice = Open; L1 = Ref( Low, -1 ); L2 = Ref( Low, -2 ); H1 = Ref( High, -1 ); H2 = Ref( High, -2 ); strategy = Param("Strategy", 1, 1, 4 ); strategy = Optimize( "Strategy", strategy, 1, 4, 1 ); switch( strategy ) { case 1: /* pivot */ PivotLow = L1 < L2 AND L1 < L; PivotHigh = H1 > H2 AND H1 > H; Buy = PivotLow; Short = PivotHigh; break; case 2: /* Bollinger band */ Buy = Cross( Close, BBandBot( Close, 15, 2 ) ); Short = Cross( BBandTop( Close, 15, 2 ), Close ); break; case 3: /* rsi */ Buy = Cross( RSI( 5 ), 40 ); Short = Cross( 60, RSI( 5 ) ); break; case 4: /* rsi + higher low / lower high */ Buy = RSI( 5 ) < 40 AND L > L1; Short = RSI( 5 ) > 60 AND H < H1; break; default: Buy = Short = 0; break; } Sell = Cover = 0; // exit by timed exit only // assumes we work in weekly interval ApplyStop( stopTypeNBar, stopModeBars, 5 );
A sample chart is shown in Figure 11.
FIGURE 11: AMIBROKER. Shown here are sample test results from four different strategies applied to nine ETFs on weekly intervals covering the years 1998–2017. Less than one-third of the cases were profitable.