TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” Here, we present the October 2016 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 “Which Trend Indicator Wins?” in this issue, author Markos Katsanos compares the effectiveness of four popular trend-detection indicators. He tests the ADX, R-squared, vertical horizontal filter (VHF), and efficiency ratio (ER). He includes some TradeStation EasyLanguage code for the strategies in his article.
TradeStation offers a number of advanced tools and capabilities to assist traders with strategy backtesting and optimization. The TradeStation Walk-Forward Optimizer (WFO) is an advanced strategy optimization tool that automates the complex, multistep task of carrying out the statistical walk-forward testing of a trading strategy’s optimized inputs. TradeStation Portfolio Maestro is a backtesting tool that lets you evaluate the performance of a group of strategies applied to one or more symbols that make up your portfolio. As shown in Figure 1, Portfolio Maestro allows the user to analyze the performance of all of the author’s strategies simultaneously.
FIGURE 1: TRADESTATION PORTFOLIO MAESTRO. Here are sample TradeStation Portfolio Maestro results for each of the author’s four strategies applied to USO using a daily interval.
To download the EasyLanguage code for the author’s strategies as well as an example Portfolio Maestro configuration export, please visit our TradeStation and EasyLanguage support forum. It can be found here: https://community.tradestation.com/Discussions/Topic.aspx?Topic_ID=142776. The ELD filename is “TASC_OCT2016.ELD.”
For more information about EasyLanguage in general, please see https://www.tradestation.com/EL-FAQ.
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.
Markos Katsanos’ article in this issue, “Which Trend Indicator Wins,” presents formulas for four different indicators designed to identify trending markets. The MetaStock formulas for the four indicators are listed below.
This indicator is built into MetaStock. It can be used with the formula function:
ADX( periods )
For example, to use a 14-period ADX, you would write:
ADX( 14 )
The formulas for the ADX trend system are:
Buy: ( (Cross(ADX(14), 30) AND ADX(14) < 42) OR (Cross(ADX(14), 22) AND ADX(14) > 1.8*LLV(ADX(14), 12) ) ) AND C > Mov(C, 50, S) Sell: Cross( Mov(C, 50, S), C ) Sell short: ( (Cross(ADX(14), 30) AND ADX(14) < 42) OR (Cross(ADX(14), 22) AND ADX(14) > 1.8*LLV(ADX(14), 12) ) ) AND C < Mov(C, 50, S) Buy to cover: Cross( C, Mov(C, 50, S) )
This indicator is built into MetaStock. It can be used with the formula function:
VHF( data array, periods )
For example, to use a 14-period VHF on the close, you would write:
VHF( C, 14 )
The formulas for the VHF trend system are:
Buy: ( (Cross(VHF(C, 36), .38) AND VHF(C, 36) < .45 AND VHF(C, 36) > Ref(VHF(C, 36),-10)) OR (Cross(VHF(C, 36), .24) AND VHF(C, 36) > 1.5*LLV(VHF(C, 36), 10) ) ) AND C > Mov(C, 50, S) Sell: Cross( Mov(C, 50, S), C ) Sell short: ( (Cross(VHF(C, 36), .38) AND VHF(C, 36) < .45 AND VHF(C, 36) > Ref(VHF(C, 36),-10)) OR (Cross(VHF(C, 36), .24) AND VHF(C, 36) > 1.5*LLV(VHF(C, 36), 10) ) ) AND C < Mov(C, 50, S) Buy to cover: Cross( C, Mov(C, 50, S) )
Perry Kaufman’s efficiency ratio can be added to MetaStock with the following formula:
tp:= 10; {time periods} Abs( ROC( C, tp, $) ) / Sum( Abs( ROC( C, 1, $) ), tp)
As written, it is for a 10-period efficiency ratio. If you wish to use a different number of periods, just change the number on the first line.
The formulas for the efficiency ratio trend system are:
Buy: er := Mov( Abs( ROC( C, 24, $) ) / Sum( Abs( ROC( C, 1, $) ), 24), 3, S); ( (Cross( er, .36) AND er < .42 AND er > Ref(er, -3)) OR (Cross(er, .26) AND er > 2.5*LLV(er, 3) ) ) AND C > Mov(C, 50, S) Sell: Cross( Mov(C, 50, S), C ) Sell short: er := Mov( Abs( ROC( C, 24, $) ) / Sum( Abs( ROC( C, 1, $) ), 24), 3, S); ( (Cross( er, .36) AND er < .42 AND er > Ref(er, -3)) OR (Cross(er, .26) AND er > 2.5*LLV(er, 3) ) ) AND C < Mov(C, 50, S) Buy to cover: Cross( C, Mov(C, 50, S) )
This indicator is built into MetaStock. It can be used with the formula function:
RSquared( data array, periods )
For example, to use a 21-period R-squared calculated on the close, you would write:
RSquared( C, 21 )
The formulas for the R-squared trend system are:
Buy: r2 := RSquared(C, 18); lr := LinRegSlope( C, 18 ) * 100; Cross( r2, .42) AND r2 < .85 AND r2 > Ref(r2, -10) AND C > Mov(C, 50, S) AND lr > 10 Sell: Cross( Mov(C, 50, S), C ) Sell short: r2 := RSquared(C, 18); lr := LinRegSlope( C, 18 ) * 100; Cross( r2, .42) AND r2 < .85 AND r2 > Ref(r2, -10) AND AND C < Mov(C, 50, S) AND lr < -10 Buy to cover: Cross( C, Mov(C, 50, S) )
For this month’s Traders’ Tip, we’ve provided the ADX.efs (average directional index), ER.efs (efficiency ratio), R2.efs (R-squared) and VHF.efs (vertical horizontal filter) studies based on the formulas described Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” In his article, the author compares four indicators to see which one performs the best at identifying trends.
The studies contain 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. The four studies are plotted on a daily chart of USO.
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 www.esignal.com/support/kb/efs/. The code is also available as a zipped archive here:
/********************************* 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: Which Trend Indicator Wins? by Markos Katsanos Version: 1.00 08/10/2016 Formula Parameters: Default: Length 14 Trend 30 Max ADX 42 Lag 12 Crit 22 Mult 1.8 MA Length 50 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); setDefaultBarFgColor(Color.RGB(0,148,255),0); var x=0; fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(14); setName("Length"); } fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(100); setDefault(30); setName("Trend"); } fpArray[x] = new FunctionParameter("AdxMax", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(100); setDefault(42); setName("Max ADX"); } fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(12); setName("Lag"); } fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(100); setDefault(22); setName("Crit"); } fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0.01); setUpperLimit(100); setDefault(1.8); setName("Mult"); } fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(50); setName("MA Length"); } } var bInit = false; var bVersion = null; var xClose = null; var xADX = null; var xSMA = null; var xLADX = null; function main(Length, Trend, AdxMax, Lag, Crit, Mult, MALength){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= Length) return; if (getBarState() == BARSTATE_ALLBARS){ xClose = null; xADX = null; xSMA = null; xLADX = null; bInit = false; } if (!bInit){ xClose = close(); xADX = adx(Length,Length); xLADX = lowerDonchian(Lag, xADX); xSMA = sma(MALength, xClose); addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),1); bInit = true; } var nADX = xADX.getValue(0); if (getCurrentBarIndex() != 0){ var nSMA = xSMA.getValue(0); var nSMA_1 = xSMA.getValue(-1); var nADX_1 = xADX.getValue(-1); var nClose = xClose.getValue(0); var nClose_1 = xClose.getValue(-1); var nLADX = xLADX.getValue(0); if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA) Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA) Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); if (!Strategy.isInTrade()){ if (nADX_1 < Trend && nADX > Trend && nADX < AdxMax){ if (nClose > nSMA) Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA) Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (nADX > (Mult * nLADX) && nADX_1 < Crit && nADX > Crit){ if (nClose > nSMA) Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA) Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0)); else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46)) } return xADX.getValue(0); } 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; }
/********************************* 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: Which Trend Indicator Wins? by Markos Katsanos Version: 1.00 08/10/2016 Formula Parameters: Default: Length 24 ER Smooth 3 Trend 0.36 Max ER 0.42 Lag 3 Mult 2.5 Crit 0.26 MA Length 50 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); setDefaultBarFgColor(Color.RGB(0,148,255),0); var x=0; fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(24); setName("Length"); } fpArray[x] = new FunctionParameter("Smooth", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(3); setName("ER Smooth"); } fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.36); setName("Trend"); } fpArray[x] = new FunctionParameter("ERMax", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.42); setName("Max ER"); } fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(3); setName("Lag"); } fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0.01); setUpperLimit(100); setDefault(2.5); setName("Mult"); } fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.26); setName("Crit"); } fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(50); setName("MA Length"); } } var bInit = false; var bVersion = null; var xClose = null; var xER = null; var xSMA = null; var xLER = null; function main(Length, Smooth, Trend, ERMax, Lag, Mult, Crit, MALength){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= (Length + Smooth + Lag)) return; if (getBarState() == BARSTATE_ALLBARS){ xClose = null; xER = null; xSMA = null; xLER = null; bInit = false; } if (!bInit){ xClose = close(); xER = efsInternal("calc_ER",xClose,Length); xER = sma(Smooth, xER); xLER = lowerDonchian(Lag, xER); xSMA = sma(MALength, xClose); addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),1); bInit = true; } var nER = xER.getValue(0); if (getCurrentBarIndex() != 0){ var nER_1 = xER.getValue(-1); var nER_lag = xER.getValue(-Lag); var nLER = xLER.getValue(0); var nSMA = xSMA.getValue(0); var nSMA_1 = xSMA.getValue(-1); var nClose = xClose.getValue(0); var nClose_1 = xClose.getValue(-1); if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA) Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA) Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); if (!Strategy.isInTrade()){ if (nER_1 < Trend && nER > Trend && nER < ERMax && nER > nER_lag){ if (nClose > nSMA) Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA) Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (nER > (Mult * nLER) && nER_1 < Crit && nER > Crit){ if (nClose > nSMA) Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose > nSMA) Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0)); else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46)); } return nER; } function calc_ER(Close,Length){ var nNetCh = Math.abs(Close.getValue(0) - Close.getValue(-Length)); var nSum = 0; for (var i = 0; i < Length; i++){ nSum += Math.abs(Close.getValue(-i) - Close.getValue(-i-1)); } if (nSum == 0) return; ER = nNetCh / nSum; return ER; } 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; }
/********************************* 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: Which Trend Indicator Wins? by Markos Katsanos Version: 1.00 08/10/2016 Formula Parameters: Default: Length 18 R2 Smooth 3 Trend 0.42 Max R2 0.85 Lag 10 LR Crit 10 MA Length 50 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); setDefaultBarFgColor(Color.RGB(0,148,255),0); var x=0; fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(18); setName("Length"); } fpArray[x] = new FunctionParameter("Smooth", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(3); setName("R2 Smooth"); } fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.42); setName("Trend"); } fpArray[x] = new FunctionParameter("R2Max", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.85); setName("Max R2"); } fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(10); setName("Lag"); } fpArray[x] = new FunctionParameter("LRcrit", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(10); setName("LR Crit"); } fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(50); setName("MA Length"); } } var bInit = false; var bVersion = null; var xClose = null; var xR2Lin = null; var xSlope = null; var xR2 = null; var xSMA = null; function main(Length, Smooth, Trend, R2Max, Lag, LRcrit, MALength){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= (Length + Smooth + Lag)) return; if (getBarState() == BARSTATE_ALLBARS){ xClose = null; xR2Lin = null; xSlope = null; xR2 = null; xSMA = null; bInit = false; } if (!bInit){ xClose = close(); xR2Lin = efsInternal("calc_R2", xClose, Length); if (xR2Lin.getValue(0) != null){ xSlope = getSeries(xR2Lin, 0); xR2 = getSeries(xR2Lin,1); } xR2 = sma(Smooth, xR2); xSMA = sma(MALength, xClose); addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),0); bInit = true; } var nSlope = xSlope.getValue(0) * 100; var nR2 = xR2.getValue(0); if (getCurrentBarIndex() != 0){ nR2_1 = xR2.getValue(-1); nR2_lag = xR2.getValue(-Lag); nClose = xClose.getValue(0); nClose_1 = xClose.getValue(-1); nSMA = xSMA.getValue(0); nSMA_1 = xSMA.getValue(-1); if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA) Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA) Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); if ((!Strategy.isInTrade()) && nR2_1 < Trend && nR2 >= Trend && nR2 < R2Max && nR2 > nR2_lag){ if (nClose > nSMA && nSlope > LRcrit) Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA && nSlope <= LRcrit) Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0)); else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46)); } return nR2; } function calc_R2(xClose, Length){ var X = 0, Y = 0, XY = 0, X2 = 0, Y2 = 0; for (var i = 0; i < Length; i++){ var xVal = xClose.getValue(-i); X += (i + 1); Y += xVal; XY += ((i + 1) * xVal); X2 += ((i + 1) * (i + 1)); Y2 += (xVal * xVal); } var xAvg = X / Length; var yAvg = Y / Length; var aSum1 = 0, aSum2 = 0; for (var i = 0; i < Length; i++){ aSum1 += (i-xAvg) * (xClose.getValue(-i)-yAvg); aSum2 += (i-xAvg) * (i-xAvg); } var nSlope = aSum1 / aSum2; var R2 = Math.pow((Length * XY - X * Y) / Math.sqrt((Length * X2 - X * X) * (Length * Y2 - Y*Y)),2); return new Array(nSlope, R2); } 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; }
/********************************* 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: Which Trend Indicator Wins? by Markos Katsanos Version: 1.00 08/10/2016 Formula Parameters: Default: Length 14 Trend 30 Max VHF 42 Lag 12 Crit 22 Mult 1.8 MA Length 50 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); setDefaultBarFgColor(Color.RGB(0,148,255),0); var x=0; fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(36); setName("Length"); } fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.38); setName("Trend"); } fpArray[x] = new FunctionParameter("VHFMax", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0); setUpperLimit(1); setDefault(0.45); setName("Max VHF"); } fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(10); setName("Lag"); } fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1); setDefault(0.24); setName("Crit"); } fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(0.01); setUpperLimit(100); setDefault(1.5); setName("Mult"); } fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setUpperLimit(1000); setDefault(50); setName("MA Length"); } } var bInit = false; var bVersion = null; var xClose = null; var xVHF = null; var xSMA = null; var xLVHF = null; function main(Length, Trend, VHFMax, Lag, Crit, Mult, MALength){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if (getCurrentBarCount() <= Length) return; if (getBarState() == BARSTATE_ALLBARS){ xClose = null; xVHF = null; xSMA = null; xLVHF = null; xHighest = null; xLowest = null; bInit = false; } if (!bInit){ xClose = close(); xVHF = efsInternal("calc_VHF",xClose,Length); xLVHF = lowerDonchian(Lag, xVHF); xSMA = sma(MALength, xClose); addBand(0.35, PS_DASH, 1, Color.RGB(195,195,195),1); bInit = true; } nVHF = xVHF.getValue(0); if (getCurrentBarIndex() != 0){ var nSMA = xSMA.getValue(0); var nSMA_1 = xSMA.getValue(-1); var nVHF_1 = xVHF.getValue(-1); var nVHF_lag = xVHF.getValue(-Lag); var nClose = xClose.getValue(0); var nClose_1 = xClose.getValue(-1); var nLVHF = xLVHF.getValue(0); if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA) Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA) Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); if (!Strategy.isInTrade()){ if (nVHF_1 < Trend && nVHF > Trend && nVHF < VHFMax && nVHF > nVHF_lag){ if (nClose > nSMA) Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA) Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } else if (nVHF > (Mult * nLVHF) && nVHF_1 < Crit && nVHF > Crit){ if (nClose > nSMA) Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); else if (nClose < nSMA) Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT); } } if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0)); else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46)) } return nVHF; } var xHighest = null; var xLowest = null; function calc_VHF(Close,Length){ if (getBarState() == BARSTATE_ALLBARS){ xHighest = upperDonchian(Length,Close); xLowest = lowerDonchian(Length,Close); } var nHighest = xHighest.getValue(0); var nLowest = xLowest.getValue(0); if (nHighest == null || nLowest == null) return; var nSum = 0; for (var i = 0; i < Length; i++){ nSum += Math.abs(Close.getValue(-i) - Close.getValue(-i-1)); } if (nSum == 0) return; VHF = (nHighest - nLowest) / nSum; return VHF; } 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 “Which Trend Indicator Wins?” in this issue, author Markos Katsanos discusses four indicators for detecting trends. Here, we’ll present a framework that lets a Wealth-Lab user run any of the four example systems for testing the efficiency of these indicators. By dragging the respective parameter slider on the bottom left of the screen, one of the included subsystems (ADX, R2, VHF or ER) can be executed, or they can be executed all together (Figure 3).
FIGURE 3: WEALTH-LAB. This illustrates example trades taken by all four trading systems at the same time on a daily chart of USO (US Oil ETF).
While most of the indicators discussed by Katsanos in the article are prepackaged, one of the participating indicators, the efficiency ratio, has been included in our Community Indicators library. This library can be installed additionally from our website’s extensions section.
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WealthLab; using WealthLab.Indicators; using Community.Indicators; namespace WealthLab.Strategies { public class TASC201610Katsanos : WealthScript { private StrategyParameter paramStrategy2Run; public TASC201610Katsanos() { paramStrategy2Run = CreateParameter("ADX/R2/VHF/ER/All",4,0,4,1); } protected override void Execute() { var whichStrategyToRun = paramStrategy2Run.ValueInt; HideVolume(); switch (whichStrategyToRun) { case 0: SystemADX.Run(this); break; case 1: SystemR2.Run(this); break; case 2: SystemVHF.Run(this); break; case 3: SystemER.Run(this); break; case 4: SystemADX.Run(this); SystemR2.Run(this); SystemVHF.Run(this); SystemER.Run(this); break; default: break; } } } class SystemADX { public static void Run(WealthScript obj) { int periodADX = 14, adxTrend = 30, adxMax = 42, mab = 50, crit = 22, adxLag = 12; double adxMult = 1.8; var c = obj.Close; var adx = ADX.Series(obj.Bars,periodADX); var avgMAB = SMA.Series(c,mab); var adxLow = Lowest.Series(adx,adxLag); ChartPane p = obj.CreatePane(30,true,true); obj.PlotSeries(p, adx, Color.Red, LineStyle.Solid, 2 ); for(int bar = obj.GetTradingLoopStartBar(1); bar < obj.Bars.Count; bar++) { if (obj.IsLastPositionActive) { if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) ) obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" ); } else { if( obj.CrossOver(bar, adx, adxTrend) && adx[bar] < adxMax & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Strong Trend"); if( adx[bar] > adxMult * adxLow[bar] && obj.CrossOver(bar, adx, crit) & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Trend"); if( obj.CrossOver(bar, adx, adxTrend) && adx[bar] < adxMax & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Strong Dntrend"); if( adx[bar] > adxMult * adxLow[bar] && obj.CrossOver(bar, adx, crit) & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Downtrend"); } // Workaround to prevent open positions from one system to be available for the next system to close if( bar == obj.Bars.Count-1 ) obj.ExitAtClose( bar, Position.AllPositions, "Close All" ); } } } class SystemR2 { public static void Run(WealthScript obj) { int periodR2 = 18, smooth = 3, mab = 50, crit = 10, r2Lag = 10; double Trend = 0.42, r2Max = 0.85; var c = obj.Close; var r2 = RSquared.Series( c,periodR2 ); var lr = 100 * LinearRegSlope.Series( c,periodR2 ); var avgMAB = SMA.Series(c,mab); ChartPane p = obj.CreatePane(30,true,true); obj.PlotSeries(p, r2, Color.Blue, LineStyle.Solid, 2 ); for(int bar = obj.GetTradingLoopStartBar(1 + smooth + r2Lag); bar < obj.Bars.Count; bar++) { if (obj.IsLastPositionActive) { if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) ) obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" ); } else { if( obj.CrossOver(bar,r2,Trend) && r2[bar] < r2Max && r2[bar] > r2[bar-r2Lag] ) { if( c[bar] > avgMAB[bar] && lr[bar] > crit ) obj.BuyAtMarket(bar+1,"Strong Trend"); else if( c[bar] < avgMAB[bar] && lr[bar] < -crit ) obj.ShortAtMarket(bar+1,"Strong Downtrend"); } } // Workaround to prevent open positions from one system to be available for the next system to close if( bar == obj.Bars.Count-1 ) obj.ExitAtClose( bar, Position.AllPositions, "Close All" ); } } } class SystemVHF { public static void Run(WealthScript obj) { int periodVHF = 36, mab = 50, vhfLag = 10; double vhfTrend = 0.38, vhfMax = 0.45, vhfMult = 1.5, crit = 0.24; var c = obj.Close; var vhf = VHF.Series(c,periodVHF); var avgMAB = SMA.Series(c,mab); var vhfLow = Lowest.Series(vhf,vhfLag); ChartPane p = obj.CreatePane(30,true,true); obj.PlotSeries(p, vhf, Color.Magenta, LineStyle.Solid, 2 ); for(int bar = obj.GetTradingLoopStartBar(1 + mab + vhfLag); bar < obj.Bars.Count; bar++) { if (obj.IsLastPositionActive) { if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) ) obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" ); } else { if( obj.CrossOver(bar, vhf, vhfTrend) && vhf[bar] < vhfMax & vhf[bar] > vhf[bar-vhfLag] & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Strong Trend"); if( vhf[bar] > vhfMult * vhfLow[bar] && obj.CrossOver(bar, vhf, crit) & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Trend"); if( obj.CrossOver(bar, vhf, vhfTrend) && vhf[bar] < vhfMax & vhf[bar] > vhf[bar-vhfLag] & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Strong Dntrend"); if( vhf[bar] > vhfMult * vhfLow[bar] && obj.CrossOver(bar, vhf, crit) & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Downtrend"); } // Workaround to prevent open positions from one system to be available for the next system to close if( bar == obj.Bars.Count-1 ) obj.ExitAtClose( bar, Position.AllPositions, "Close All" ); } } } class SystemER { public static void Run(WealthScript obj) { int periodER = 24, mab = 50, erLag = 3; double erTrend = 0.36, erMax = 0.42, erMult = 2.5, crit = 0.26; var c = obj.Close; var er = ER.Series(c,periodER); var avgMAB = SMA.Series(c,mab); var erLow = Lowest.Series(er,erLag); ChartPane p = obj.CreatePane(30,true,true); obj.PlotSeries(p, er, Color.Black, LineStyle.Solid, 2 ); for(int bar = obj.GetTradingLoopStartBar(1 + mab + erLag); bar < obj.Bars.Count; bar++) { if (obj.IsLastPositionActive) { if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) ) obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" ); } else { if( obj.CrossOver(bar, er, erTrend) && er[bar] < erMax & er[bar] > er[bar-erLag] & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Strong Trend"); if( er[bar] > erMult * erLow[bar] && obj.CrossOver(bar, er, crit) & c[bar] > avgMAB[bar] ) obj.BuyAtMarket(bar+1,"Trend"); if( obj.CrossOver(bar, er, erTrend) && er[bar] < erMax & er[bar] > er[bar-erLag] & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Strong Dntrend"); if( er[bar] > erMult * erLow[bar] && obj.CrossOver(bar, er, crit) & c[bar] < avgMAB[bar] ) obj.ShortAtMarket(bar+1,"Downtrend"); } // Workaround to prevent open positions from one system to be available for the next system to close if( bar == obj.Bars.Count-1 ) obj.ExitAtClose( bar, Position.AllPositions, "Close All" ); } } } }
In “Which Trend Indicator Wins?” author Markos Katsanos shows how various trend strength indicators (the ADX, R-squared, VHF, and ER) can be combined with a simple moving average crossover (Figure 4). A ready-to-use formula implementing the indicators and systems presented in Katsanos’ article is shown below. You can use the parameters window to select the system/indicator you would like to test and/or display.
FIGURE 4: AMIBROKER. Here’s an example USO chart with a 50-day moving average. The R-squared, the efficiency ratio (ER), the vertical horizontal filter (VHF), and the ADX are shown in the lower panes.
function RSquared( array, period, smooth ) { bi = BarIndex(); r = Correlation( bi, array, period); rs = MA( r, smooth ); return rs ^ 2; } function VHF( period ) { nom = HHV( High, period ) - LLV( Low, period ); denom = Sum( abs( C - Ref( C, -1 ) ), period ); return nom / denom; } function EffRatio( period, smooth ) { nom = abs( C - Ref( C, -period ) ); denom = Sum( abs( C - Ref( C, -1 ) ), period ); return MA( nom/denom, smooth ); } SetOption("ReverseSignalForcesExit", False ); procedure SystemADX() { global Buy, Sell, Short, Cover; period = 14; trend = 30; axmax = 42; lag = 12; crit = 22; mult = 1.8; maperiod = 50; ax = ADX( period ); mac = MA( Close, maperiod ); strongbuy = Cross( ax, trend ) AND ax < axmax AND C > mac; regbuy = ax > mult * LLV( ax, lag ) AND Cross( ax, crit ) AND C > mac; Buy = strongbuy OR regbuy; strongshort = Cross( ax, trend ) AND ax < axmax AND C < mac; regshort = ax > mult * LLV( ax, lag ) AND Cross( ax, crit ) AND C < mac; Short = strongshort OR regshort; Sell = Cross( mac, C ); Cover = Cross( C, mac ); Plot( ax, "ADX-" + period, colorBlue ); Plot( trend, "", colorDarkYellow); } procedure SystemR2() { global Buy, Sell, Short, Cover; r2len = 18; smooth = 3; trend = 0.42; r2max = 0.85; lag = 10; lrcrit = 10; maperiod = 50; R2 = RSquared( C, r2len, smooth ); lr = 100 * LinRegSlope( C, r2len ); flt = Cross( R2, trend ) AND R2 < R2max AND R2 > Ref( R2, -lag ); mac = MA( C, maperiod ); Buy = flt AND C > mac AND lr > lrcrit; Short = flt AND C < mac AND lr < -lrcrit; Sell = Cross( mac, C ); Cover = Cross( C, mac ); Plot( R2, "Rsquared-" + r2len, colorRed ); Plot( trend, "", colorDarkYellow); } procedure SystemVHF() { global Buy, Sell, Short, Cover; period = 36; trend = 0.38; lag = 10; mult = 1.5; maperiod = 50; crit = 0.24; vfmax = 0.45; vf = VHF( period ); mac = MA( C, maperiod ); strongbuy = Cross( vf, trend ) AND vf < vfmax AND vf > Ref( vf, -lag ) AND C > mac; regbuy = vf > mult * LLV( vf, lag ) AND Cross( vf, crit ) AND C > mac; Buy = strongbuy OR regbuy; strongshort = Cross( vf, trend ) AND vf < vfmax AND vf > Ref( vf, -lag ) AND C < mac; regshort = vf > mult * LLV( vf, lag ) AND Cross( vf, crit ) AND C < mac; Short = strongshort OR regshort; Sell = Cross( mac, C ); Cover = Cross( C, mac ); Plot( vf, "VHF-" + period, colorDefault ); Plot( trend, "", colorDarkYellow); } procedure SystemER() { global Buy, Sell, Short, Cover; period = 24; smooth = 3; trend = 0.36; ermax = 0.42; erlag = 3; mult = 2.5; crit = 0.26; maperiod = 50; er = EffRatio( period, smooth ); mac = MA( C, maperiod ); strongbuy = Cross( er, trend ) AND er < ermax AND er > Ref( er, -erlag ) AND C > mac; regbuy = er > mult * LLV( er, erlag ) AND Cross( er, crit ) AND C > mac; Buy = strongbuy OR regbuy; strongshort = Cross( er, trend ) AND er < ermax AND er > Ref( er, -erlag ) AND C < mac; regshort = er > mult * LLV( er, erlag ) AND Cross( er, crit ) AND C < mac; Short = strongshort OR regshort; Sell = Cross( mac, C ); Cover = Cross( C, mac ); Plot( er, "ER-" + period, colorGreen ); Plot( trend, "", colorDarkYellow); } sys = ParamList("Select the system", "ADX|RSquared|VHF|ER" ); switch( sys ) { case "ADX": SystemADX(); break; case "RSquared": SystemR2(); break; case "VHF": SystemVHF(); break; case "ER": SystemER(); break; default: Error("Undefined system"); break; }
The trend indicators described in Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” can be easily implemented in NeuroShell Trader with a few of NeuroShell Trader’s 800+ indicators. Simply select new indicator from the insert menu and use the indicator wizard to set up the following indicators:
ADX: ADX(High,Low,Close,14,14) VHF: Divide(Range(Close,28),CumAbsMom(Close,28,1)) ER: Divide(Abs(Momentum(Close,28)),CumAbsMom(Close,28,1)) R2: LinTimeReg rsqrd(Close,21)
To implement the trend-following system for any of the indicators, simply select “new trading strategy” from the insert menu and enter the following in the appropriate locations of the Trading Strategy Wizard, substituting the appropriate test parameters for each indicator as outlined in the article’s test results:
BUY LONG CONDITIONS: [1 of which must be true] And3(CrossAbove(Indicator,TREND),A<B(Indicator,INDMAX),A>B(Close,Avg(Close,MAB))) And3(A>B(Indicator,Mul2(MULT,Min(Indicator,LAG))),CrossAbove(Indicator,CRIT),A>B(Close,Avg(Close,MAB))) SELL LONG CONDITIONS: CrossBelow(Close,Avg(Close,MAB)) SELL SHORT CONDITIONS: [1 of which must be true] And3(CrossAbove(Indicator,TREND),A<B(Indicator,INDMAX),A<B(Close,Avg(Close,MAB))) And3(A>B(Indicator,Mul2(MULT,Min(Indicator,LAG))),CrossAbove(Indicator,CRIT),A<B(Close,Avg(Close,MAB))) COVER SHORT CONDITIONS: CrossAbove(Close,Avg(Close,MAB))
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 NeuroShell chart is shown in Figure 5.
FIGURE 5: NEUROSHELL TRADER. This sample NeuroShell Trader chart shows the VHF trend-following system and the four trend-following indicators.
Strategies based off of the ADX, R-squared, VHF, and ER indicators, as discussed in “Which Trend Indicator Wins?” by Markos Katsanos in this issue, are available for download at www.ninjatrader.com/SC/October2016SC.zip.
Once downloaded, from within the NinjaTrader Control Center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file. This file is for NinjaTrader version 7.
You can review the source code of the custom VHF and ER indicators by selecting the menu Tools → Edit NinjaScript → Indicator from within the NinjaTrader Control Center window and selecting the VHF or ER file.
You can review the source code of the strategies by selecting the menu Tools → Edit NinjaScript → Strategy from within the NinjaTrader Control Center window and selecting the ADXStrategy, ERStrategy, R2Strategy, or VHFStrategy file.
A sample chart implementing the strategy is shown in Figure 6.
FIGURE 6: NINJATRADER. The R-squared, ER, VHF, and ADX are shown detecting the downtrend in USO beginning with the ER indicator on November 13, 2015.
Our Traders’ Tip for this month is based on the article by Mark Katsanos in this issue, “Which Trend Indicator Wins?” In it, the author compares four trend-following indicators to determine which is best for finding trends in real time, as opposed to in retrospect. The four indicators are: the ADX, Kaufman’s efficiency ratio, R-squared, and the vertical horizontal filter, each with standardized entry rules. A sample chart of the indicators is shown in Figure 7.
FIGURE 7: UPDATA. This chart demonstrates the ADX, VHF, R-SQ, and ER applied to the ETF USO in daily resolution.
The Updata code for this article can be found in the Updata library and may be downloaded by clicking the custom menu and system library. Those who cannot access the library due to a firewall may ask our helpdesk for a copy, or copy and paste the code shown below.
'ADX PARAMETER "ADX Period" #PERIOD=14 PARAMETER "Trend" @TREND=30 PARAMETER "Lag" #LAG=12 PARAMETER "ADX Max" @ADXMAX=42 PARAMETER "Critical" @CRIT=22 PARAMETER "Mult" @MULT=1.8 PARAMETER "Avg" #AVGPER=50 DISPLAYSTYLE 5LINES INDICATORTYPE TOOL INDICATORTYPE2 CHART COLOUR RGB(0,0,200) COLOUR2 RGB(0,0,0) COLOUR3 RGB(200,0,0) COLOUR4 RGB(0,0,200) COLOUR5 RGB(0,200,0) NAME "" "" NAME2 "ADX" "" @ADX=0 @AVG=0 FOR #CURDATE=#AVGPER TO #LASTDATE @ADX=DIRMOV(#PERIOD,ADX) @AVG=MAVE(#AVGPER) 'EXIT IF ORDERISOPEN=1 AND CLOSE<@AVG SELL CLOSE ELSEIF ORDERISOPEN=-1 AND CLOSE>@AVG COVER CLOSE ENDIF 'LONG IF @ADX>@TREND AND @ADX<@ADXMAX AND CLOSE>@AVG BUY CLOSE ENDIF IF @ADX>@MULT*PLOW(@ADX,#LAG) AND @ADX>@CRIT AND CLOSE>@AVG BUY CLOSE ENDIF 'SHORT IF @ADX>@TREND AND @ADX<@ADXMAX AND CLOSE<@AVG SHORT CLOSE ENDIF IF @ADX>@MULT*PLOW(@ADX,#LAG) AND @ADX>@CRIT AND CLOSE<@AVG SHORT CLOSE ENDIF @PLOT=@AVG @PLOT2=@ADX @PLOT3=@ADXMAX @PLOT4=@CRIT @PLOT5=@MULT*PLOW(@ADX,#LAG) NEXT 'KAUFMAN EFFICIENCY RATIO SYSTEM PARAMETER "Period" #PERIOD=20 PARAMETER "Smooth" #SMOOTH=3 PARAMETER "Mult" @MULT=2.5 PARAMETER "Lag" #LAG=3 PARAMETER "Max" @MAX=0.42 PARAMETER "Critical" @CRIT=0.26 PARAMETER "Trend" @TREND=0.36 DISPLAYSTYLE 5LINES INDICATORTYPE TOOL INDICATORTYPE2 CHART COLOUR RGB(200,0,0) COLOUR2 RGB(100,100,100) COLOUR3 RGB(200,0,0) COLOUR4 RGB(200,0,0) COLOUR5 RGB(200,0,0) @ER=0 @SIGNAL=0 @DIFF=0 @NOISE=0 NAME "" "" NAME2 "ER" "" FOR #CURDATE=#PERIOD TO #LASTDATE @DIFF=CLOSE-CLOSE(1) @SIGNAL=ABS(CLOSE-CLOSE(#PERIOD)) @NOISE=SGNL(ABS(@DIFF),#PERIOD,M)*#PERIOD @ER=SGNL(@SIGNAL/@NOISE,#SMOOTH,M) 'EXITS IF ORDERISOPEN>0 AND CLOSE<MAVE(#SMOOTH) SELL CLOSE ELSEIF ORDERISOPEN<0 AND CLOSE>MAVE(#SMOOTH) COVER CLOSE ENDIF 'LONG IF @ER>@TREND AND @ER<@MAX AND @ER>HIST(@ER,#LAG) AND CLOSE>MAVE(#SMOOTH) BUY CLOSE ELSEIF @ER>@MULT*PLOW(@ER,#LAG) AND @ER>@CRIT AND CLOSE>MAVE(#SMOOTH) BUY CLOSE ENDIF 'SHORT IF @ER>@TREND AND @ER<@MAX AND @ER>HIST(@ER,#LAG) AND CLOSE<MAVE(#SMOOTH) SHORT CLOSE ELSEIF @ER>@MULT*PLOW(@ER,#LAG) AND @ER>@CRIT AND CLOSE<MAVE(#SMOOTH) SHORT CLOSE ENDIF @PLOT=MAVE(#SMOOTH) @PLOT2=@ER @PLOT3=@CRIT @PLOT4=@MAX @PLOT5=@TREND NEXT NAME R-Squared PARAMETER "Critical" @LCrit=10 PARAMETER "Close Avg" #AVGPER=50 PARAMETER "RSQ Period" #FPeriod=20 PARAMETER "RSQ MA LENGTH" #LENGTH=3 PARAMETER "Smooth" #SMOOTH=3 PARAMETER "Trend" @TREND=0.42 PARAMETER "R2 Max" @R2MAX=0.85 PARAMETER "Lag" #LAG=10 DISPLAYSTYLE 4LINES PLOTSTYLE LINE RGB(255,0,0) PLOTSTYLE2 HISTOGRAM PLOTSTYLE3 LINE RGB(0,0,255) PLOTSTYLE4 LINE RGB(255,0,0) INDICATORTYPE CHART NEWWINDOW INDICATORTYPE2 CHART NEWWINDOW INDICATORTYPE3 CHART NEWWINDOW INDICATORTYPE4 CHART SUPERIMPOSERIGHT @sumxy=0 @sumx=0 @sumy=0 @sumXsq=0 #I=0 @b=0 @SLOPE=0 @sumYsq=0 @RSQ=0 @TOP=0 @Bottom1=0 @Bottom2=0 #RSQPeriod=#FPeriod FOR #CURDATE=#FPeriod To #LASTDATE @sumxy=0 @sumx=0 @sumy=0 @sumXsq=0 @sumYsq=0 For #I=(#FPeriod) To 1 STEP -1 @sumxy=@sumxy+(#I*CLOSE(#I)) @sumy=@sumy+close(#I) @sumx=@sumx+(#I) @sumXsq=@sumXsq+EXPBASE(#I,2) @sumYsq=@sumYsq+EXPBASE(CLOSE(#I),2) Next 'Calculates SLOPE @SLOPE=((@sumx*@sumy)-(@sumxy*#FPeriod) )/ ((@sumXsq*#FPeriod)-(EXPBASE(@sumx,2)) ) @PLOT2=@SLOPE 'Calculates RSQ-arranged so as to keep track of brackets @TOP=( (#RSQPeriod*@sumxy)-(@sumx*@sumy) ) @Bottom1=#RSQPeriod*@sumYsq-EXPBASE(@sumy,2) @Bottom2=#RSQPeriod*@sumXsq-EXPBASE(@sumx,2) @RSQ=EXPbase(@TOP/EXPBASE((@Bottom1*@Bottom2),0.5),2) @PLOT=@RSQ 'EXIT IF ORDERISOPEN=1 AND CLOSE>MAVE(#AVGPER) SELL CLOSE ENDIF 'EXIT IF ORDERISOPEN=-1 AND CLOSE<MAVE(#AVGPER) COVER CLOSE ENDIF 'ENTRIES IF @RSQ>@TREND AND @RSQ<@R2MAX AND @RSQ>HIST(@RSQ,#LAG) 'LONG IF CLOSE>MAVE(#AVGPER) AND @SLOPE>@LCrit BUY CLOSE ENDIF 'SHORTS IF CLOSE<MAVE(#AVGPER) AND @SLOPE<@LCrit SHORT CLOSE ENDIF ENDIF NEXT 'VERTICAL HORIZONTAL FILTER PARAMETER "Period" #PERIOD=28 PARAMETER "Smooth" #SMOOTH=50 PARAMETER "Trend" @TREND=0.38 PARAMETER "Mult" @MULT=1.5 PARAMETER "Crit" @CRIT=0.24 PARAMETER "Max" @MAX=0.42 PARAMETER "Lag" #LAG=10 NAME "VHF " #PERIOD "" @VHF=0 FOR #CURDATE=#PERIOD TO #LASTDATE @VHF=(PHIGH(CLOSE,#PERIOD)-PLOW(CLOSE,#PERIOD))/(SGNL(ABS(CLOSE-CLOSE(1)),#PERIOD,M)*#PERIOD) 'EXITS IF CLOSE<MAVE(#SMOOTH) AND ORDERISOPEN=1 SELL CLOSE ELSEIF CLOSE>MAVE(#SMOOTH) AND ORDERISOPEN=-1 COVER CLOSE ENDIF 'LONGS IF @VHF>@TREND AND @VHF<@MAX AND @VHF>HIST(@VHF,#LAG) IF CLOSE>MAVE(#SMOOTH) BUY CLOSE ENDIF ENDIF IF @VHF>@MULT*PLOW(@VHF,#LAG) AND @VHF>@CRIT AND CLOSE>MAVE(#PERIOD) BUY CLOSE ENDIF 'SHORTS IF @VHF>@TREND AND @VHF<@MAX AND @VHF>HIST(@VHF,#LAG) IF CLOSE<MAVE(#SMOOTH) SHORT CLOSE ENDIF ENDIF IF @VHF>@MULT*PLOW(@VHF,#LAG) AND @VHF>@CRIT AND CLOSE<MAVE(#PERIOD) SHORT CLOSE ENDIF @PLOT=@VHF NEXT
In “Which Trend Indicator Wins?” in this issue, author Markos Katsanos explores four indicators that were designed by their respective developers to assist the user in differentiating between trending markets and consolidating markets. Once the type of market has been determined, one can then apply trend-following indicators or oscillators, as appropriate, when making trading decisions.
As a way to compare these indicators, Katsanos designed a separate, simple trading system around each of the four indicators. In Figure 8 we have the basic controls for the simple moving average (on the price chart) and the four trend indicators, which are plotted as subcharts below the price chart.
FIGURE 8: EXCEL, INDICATOR COMPARISON. This chart approximates Figure 3 from Katsanos’ article in this issue, “Which Trend Indicator Wins?”
The four individual trading systems each require a fair number of columns of formulas. All four indicators done on a single worksheet would make for a very busy worksheet. Thus, this spreadsheet has a separate worksheet for each of the four indicators.
Here is my observation about the trading simulations:
While developing this workbook, I encountered a couple of interesting artifacts:
“Sell all” and “BuyToCover all” statements in his code will close out these single to possibly multiple positions on the first available exit signal.
So the logic in this workbook will only recognize the first entry signal of a given persuasion for a transaction until the transaction exits. At most, my simulation will have one trade of each persuasion active at a time.
Figure 9 shows part of one of my trend indicator worksheets. Each of the four trend indicator worksheets has the same layout, as follows:
Across the top:
Across the bottom:
FIGURE 9: EXCEL, INDICATOR CALCULATION. Here is a worksheet that can be used for indicator calculation and trading simulation.
The spreadsheet file for this Traders’ Tip can be downloaded here. To successfully download it, follow these steps: