TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is John Ehlers’ article in this issue, “The MAD Indicator, Enhanced.” Here, we present the November 2021 Traders’ Tips code with possible implementations in various software.
You can right-click on any chart to open it in a new tab or window and view it at it’s originally supplied size, often much larger than the version printed in the magazine.
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.
Last month in his October 2021 article in S&C, “Cycle/Trend Analytics And The MAD Indicator,” John Ehlers presented the MAD (moving average difference) indicator, an oscillator developed out of his research into understanding cycles data better. The MAD indicator effectively took the difference between two simple moving averages. While the MAD is said to offer an improvement over the classic, well-known MACD, the MADH (moving average difference with Hann) that Ehlers presents in his article in this issue, “The MADH: The MAD Indicator, Enhanced,” is said to offer an improvement over the MAD indicator. The enhancement comes from the utilization of the Hann windowing technique and takes the difference of two finite impulse response filters. Ehlers explains that excellent buy and sell indications can be seen in the indicator’s valleys and peaks, respectively.
Indicator: TASC NOV 2021 MADH // TASC NOV 2021 // MADH (Moving Average Difference - Hann) Indicator // (C) 2021 John F. Ehlers inputs: ShortLength(8), DominantCycle(27); variables: LongLength(20), Filt1(0), Filt2(0), coef(0), count(0), MADH(0); LongLength = IntPortion(ShortLength + DominantCycle / 2); Filt1 = 0; coef = 0; for count = 1 to ShortLength begin Filt1 = Filt1 + (1 - Cosine(360*count / (ShortLength + 1)))*Close[count - 1]; coef = coef + (1 - Cosine(360*count / (ShortLength + 1))); end; If coef <> 0 then Filt1 = Filt1 / coef; Filt2 = 0; coef = 0; for count = 1 to LongLength begin Filt2 = Filt2 + (1 - Cosine(360*count / (LongLength + 1)))*Close[count - 1]; coef = coef + (1 - Cosine(360*count / (LongLength + 1))); end; If coef <> 0 Then Filt2 = Filt2 / coef; // Computed as percentage of price If Filt2 <> 0 then MADH = 100*(Filt1 - Filt2) / Filt2; Plot1(MADH, "MADH"); Plot2(0, "Zero");
A sample chart is shown in Figure 1.
FIGURE 1: TRADESTATION. This TradeStation daily chart of the continuous emini S&P 500 shows the MADH indicator applied.
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.
We put together a study based on the article by John Ehlers in this issue titled “The MADH: The MAD Indicator, Enhanced.” We built the strategy referenced by using our proprietary scripting language, thinkscript. To ease the loading process, simply click on https://tos.mx/zq1AI6I or enter it into the address into setup→open shared item from within thinkorswim then choose view thinkScript study and name it “MADH” or whatever you like and can identify. You can then add the strategy to your charts from the edit studies menu from within the charts tab and then select studies.
The chart in Figure 2 shows the study added to a one-year daily chart of SPY. Please see John Ehlers’ article for more information on how to utilize the study.
FIGURE 2: THINKORSWIM. This sample chart shows the MADH study added to a one-year daily chart of SPY.
For this month’s Traders’ Tip, we’ve provided the study Moving Average Difference - Hann Indicator.efs based on the article in this issue by John Ehlers titled “The MADH: The MAD Indicator, Enhanced.” This study offers an improvement over the classic MACD study by Gerald Appel by optimizing the lengths of the moving averages.
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 3.
FIGURE 3: eSIGNAL. Here is an example of the study plotted on a daily chart of SPY.
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: Copyright 2019 Intercontinental Exchange, Inc. All Rights Reserved. eSignal is a service mark and/or a registered service mark of Intercontinental Exchange, Inc. in the United States and/or other countries. This sample eSignal Formula Script (EFS) is for educational purposes only. Intercontinental Exchange, Inc. reserves the right to modify and overwrite this EFS file with each new release. Description: The MAD Indicator, Enhanced by John F. Ehlers Version: 1.00 09/13/2021 Formula Parameters: Default: ShortLength 8 DominantCycle 27 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(); var bInit = false; function preMain() { setStudyTitle("Moving Average Difference - Hann Indicator"); setCursorLabelName("MADH", 0); setPriceStudy(false); setDefaultBarFgColor(Color.RGB(0x00,0x94,0xFF), 0); setPlotType( PLOTTYPE_LINE , 0 ); var x=0; fpArray[x] = new FunctionParameter("ShortLength", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setDefault(8); } fpArray[x] = new FunctionParameter("DominantCycle", FunctionParameter.NUMBER); with(fpArray[x++]){ setLowerLimit(1); setDefault(27); } } var bVersion = null; var xClose = null; var xMADH = null; var LongLength = null; var Filt1 = null; var coef = null; var Filt2 = null; function main(ShortLength, DominantCycle) { if (bVersion == null) bVersion = verify(); if (bVersion == false) return; if ( bInit == false ) { xClose = close(); LongLength = Math.trunc(ShortLength + DominantCycle / 2); addBand(0, PS_DASH, 1, Color.grey, 1); xMADH = efsInternal('Calc_MAD', xClose, ShortLength, LongLength); bInit = true; } if (getCurrentBarCount() < LongLength || getCurrentBarCount() < ShortLength) return; return xMADH.getValue(0); } function Calc_MAD(xClose, ShortLength, LongLength){ Filt1 = 0; coef = 0; for (var i = 1; i <= ShortLength; i++){ Filt1 = Filt1 + (1 - Math.cos(2 * Math.PI * i / (ShortLength + 1))) * xClose.getValue(-i + 1); coef = coef + (1 - Math.cos(2 * Math.PI * i / (ShortLength + 1))); } if (coef != 0) Filt1 = Filt1 / coef; Filt2 = 0; coef = 0; for (var i = 1; i <= LongLength; i++){ Filt2 = Filt2 + (1 - Math.cos(2 * Math.PI * i / (LongLength + 1))) * xClose.getValue(-i + 1); coef = coef + (1 - Math.cos(2 * Math.PI * i / (LongLength + 1))); } if (coef != 0) Filt2 = Filt2 / coef; if (Filt2 != 0) ret = 100 * (Filt1 - Filt2) / Filt2; return ret; } 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; }
We have added John Ehlers’ MADH indicator to Wealth-Lab 7 for the convenience of our users. In his article in this issue, Ehlers suggests that the valleys and peaks of the indicator can signal buy and sell opportunities. To test this trading idea, Wealth-Lab users can simply add the “indicator turns” building block or use the sample code provided below.
System rules
using WealthLab.Backtest; using System; using WealthLab.Core; using WealthLab.Indicators; using WealthLab.TASC; using System.Drawing; using System.Collections.Generic; namespace WealthScript1 { public class MyStrategy1 : UserStrategyBase { /* create indicators and other objects here, this is executed prior to the main trading loop */ public override void Initialize(BarHistory bars) { mad = MAD.Series(bars.Close, 8, 20); madh = MADH.Series(bars.Close, 8, 20); PlotIndicator(mad, Color.Red, PlotStyles.Line, false, "MAD"); PlotIndicator(madh, Color.Yellow, PlotStyles.Line, false, "MADH"); ChartDisplaySettings cds = new ChartDisplaySettings(); cds.ColorGridLines = Color.Transparent; cds.ColorWatermark = Color.White; cds.ColorUpBar = Color.Green; cds.ColorDownBar = Color.Red; cds.ColorBackground = Color.Black; SetChartDrawingOptions(cds); SetPaneDrawingOptions("MAD", 20, 50); SetPaneDrawingOptions("MADH", 20, 51); } /* execute the strategy rules here, this is executed once for each bar in the backtest history */ public override void Execute(BarHistory bars, int idx) { if(madh.TurnsUp(idx)) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); if (madh.TurnsDown(idx)) ClosePosition(LastPosition, OrderType.Market); } /* declare private variables below */ MAD mad; MADH madh; } }
Figure 4 illustrates application of the MADH to a chart of SPY.
FIGURE 4: WEALTH-LAB. This chart shows an example of trade signals produced by the system if applied to a daily chart of SPY.
John Ehlers’ MADH indicator, presented in his article in this issue, can be easily implemented in NeuroShell Trader using NeuroShell Trader’s ability to call external dynamic linked libraries. Dynamic linked libraries can be written in C, C++ and Power Basic.
After moving the code given in the article to your preferred compiler and creating a DLL, you can insert the resulting indicator as follows:
FIGURE 5: NEUROSHELL TRADER. This NeuroShell Trader chart shows the MAD and MADH indicators.
NeuroShell Trader users 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.
In his article in this issue, “The MADH: The MAD Indicator, Enhanced,” John Ehlers introduces his MADH indicator. To calculate the MADH indicator in Optuma, you can wrap the MAD formula in the prebuilt FIR() function available to all clients with our Ehlers module (www.optuma.com/ehlers).
//Calculate MAD Oscillator; //MA Inputs; #$Short = 8; #$Long = 23; //Calc MAD; S1 = MA(BARS=$Short, CALC=Close); L1 = MA(BARS=$Long, CALC=Close); MAD1 =100 * ((S1 - L1)/L1); //Wrap MAD in the FIR Hann function to calculate MADH; FIR(MAD1, SMOOTHSTYLE=Hann, BARS=20)
FIGURE 6: OPTUMA. This sample chart displays the MADH oscillator.
The importable TradersStudio files based on John Ehlers’ article in this issue, “The MADH: The MAD Indicator, Enhanced,” can be obtained on request via email to info@TradersEdgeSystems.com. The code is also available below.
Code for the author’s indicator is provided in following files:
'The MAD Indicator, Enhanced 'Author: John F. Ehlers, TASC Nov 2021 'Coded by: Richard Denning, 9/14/2021 Function EHLERS_MADH(ShortLength, DominantCycle) 'ShortLength=8, DominantCycle=27 Dim LongLength Dim Filt1 Dim Filt2 Dim coef Dim count Dim MADH As BarArray If BarNumber=FirstBar Then LongLength = 20 Filt1 = 0 Filt2 = 0 coef = 0 count = 0 MADH = 0 End If LongLength = CInt(ShortLength + DominantCycle / 2) Filt1 = 0 coef = 0 For count = 1 To ShortLength Filt1 = Filt1 + (1 - TStation_Cosine(360*count / (ShortLength + 1)))*Close[count - 1] coef = coef + (1 - TStation_Cosine(360*count / (ShortLength + 1))) Next If coef <> 0 Then Filt1 = Filt1 / coef End If Filt2 = 0 coef = 0 For count = 1 To LongLength Filt2 = Filt2 + (1 - TStation_Cosine(360*count / (LongLength + 1)))*Close[count - 1] coef = coef + (1 - TStation_Cosine(360*count / (LongLength + 1))) Next If coef <> 0 Then Filt2 = Filt2 / coef End If If Filt2 <> 0 Then MADH = 100*(Filt1 - Filt2)/Filt2 End If EHLERS_MADH = MADH End Function '------------------------------------------------------------------------------------------- 'INDICATOR PLOT Sub EHLERS_MADH_IND(ShortLength,DominantCycle) Dim MADH As BarArray MADH = EHLERS_MADH(ShortLength,DominantCycle) plot1(MADH) plot2(0) End Sub
A sample chart displaying the oscillator is shown in Figure 7.
FIGURE 7: TRADERSSTUDIO. The MADH indicator is shown on a chart of Cisco Systems Inc. (CSCO) during 2012.
Here is TradingView Pine code for implementing the MADH indicator (moving average difference with Hann windowing) as presented by John Ehlers in his article in this issue.
// TASC Issue: November 2021 // Article: "Moving Average Difference with Hann Windowing (MADH)" by John Ehlers // Language: TradingView's Pine Script // Provided by: PineCoders, for tradingview.com //@version=4 study("TASC 2021.11 - Moving Average Diff. with Hann Windowing (MADH)", "MADH") float sourceInput = input(close, "Source:") int shortLengthInput = input(8, "Short Length:", minval = 2) int dominantCycleInput = input(27, "Dominant Cycle:", minval = 4) madh(source, shortLength, dominantCycle) => var float PIx2 = math.pi * 2.0 float filt1 = 0.0 float coeffs = 0.0 for count = 1 to shortLength float hannCoeff = 1.0 - cos(PIx2 * count / (shortLength + 1)) filt1 += hannCoeff * source[count - 1] coeffs += hannCoeff filt1 := nz(filt1 / coeffs) int longLength = int(shortLength + dominantCycle * 0.5) float filt2 = 0.0 coeffs := 0.0 for count = 1 to longLength float hannCoeff = 1.0 - cos(PIx2 * count / (longLength + 1)) filt2 += hannCoeff * source[count - 1] coeffs += hannCoeff filt2 := nz(filt2 / coeffs) nz(100.0 * (filt1 - filt2) / filt2) madhSignal = madh(sourceInput, shortLengthInput, dominantCycleInput) plot(madhSignal, "MADH", color.orange, 3) hline(0, "Zero", color.gray)
The indicator is available on TradingView from the PineCodersTASC account at: tradingview.com/u/PineCodersTASC/#published-scripts
FIGURE 8: TRADINGVIEW. The MADH indicator is shown on a chart of the S&P 500 Index.
Last month in his October 2021 article in S&C, John Ehlers described his MAD indicator. In his article this month, he presents the MADH oscillator, which is the MAD enhanced with a Hann window applied. Here again is the code in C for Zorro for the MAD and Hann from previus issues.
var MAD(vars Data, int ShortPeriod, int LongPeriod) { return 100*(SMA(Data,ShortPeriod) /SMA(Data,LongPeriod)-1.); } vars hann(vars Data,int Length) { vars Out = series(0,Length); int i; for(i=0; i<Length; i++) Out[i] = Data[i] * (1-cos(2*PI*(i+1)/(Length+1))); return Out; }
For creating the MADH, we just glue both functions together:
var MADH(vars Data, int ShortPeriod, int LongPeriod) { return 100*(SMA(hann(Data,ShortPeriod),ShortPeriod) /SMA(hann(Data,LongPeriod),LongPeriod)-1.); }
Here is a script for replicating Ehlers’ chart in this issue of the MADH using SPY data from the STOOQ website:
void run() { StartDate = 20191201; EndDate = 20210701; BarPeriod = 1440; assetAdd("SPY","STOOQ:*"); asset("SPY"); plot("MAD",MAD(seriesC(),8,23),NEW,RED); plot("MADH",MADH(seriesC(),8,23),NEW,BLUE); }
The resulting chart is shown in Figure 9.
FIGURE 9: ZORRO PROJECT. Here is the MADH indicator applied to SPY data from the STOOQ website.
The MADH oscillator and scripts can be downloaded from the 2021 script repository on https://financial-hacker.com. The Zorro platform can be downloaded from https://zorro-project.com.
The MADH (moving average distance—Hann) indicator, as discussed in the article in this issue by John Ehlers, “The MAD Indicator, Enhanced,” is available for download at the following links for NinjaTrader 8 and for NinjaTrader 7:
Once the file is downloaded, you can import the indicator into NinjaTrader 8 from within the control center by selecting Tools → Import → NinjaScript Add-On and then selecting the downloaded file for NinjaTrader 8. To import into NinjaTrader 7, from within the control center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file.
You can review the source code for this indicator in NinjaTrader 8 by selecting the menu New → NinjaScript Editor → Indicators from within the control center window and selecting the MADH file. You can review the indicator’s source code in NinjaTrader 7 by selecting the menu Tools → Edit NinjaScript → Indicator from within the control center window and selecting the MADH file.
A sample chart displaying the indicator is shown in Figure 10.
FIGURE 10: NINJATRADER. The chart displays the MADH indicator on the emini S&P 500.
NinjaScript uses compiled DLLs that run native, not interpreted, which provides you with the highest performance possible.
In his article in this issue, “The MADH: The MAD Indicator, Enhanced,” John Ehlers combines two concepts presented in his previous articles in this series.
By replacing the two moving averages used in the basic MAD indicator with two Hann windowing averages, we get an indicator that is smoother as determined by looking at the rate of change (ROC) of the original MAD and the enhanced MADH (see Figure 11).
FIGURE 11: EXCEL. This shows a chart comparing MAD and MADH oscillators along with their respective ROC (rate of change) plots.
Ehlers suggests that “excellent buy and sell indications are at the valleys and peaks” of the MADH indicator. These valleys and peaks occur at the points where the MADH ROC crosses zero.
ROC crossovers are a bit easier to see in Figure 12 with the focus narrowed to approximately six months centered on the March 2020 market downturn.
FIGURE 12: EXCEL. Here is a closer look at three months on either side of the March 2020 market downturn.
To explore the ROC crossover buy and sell indications, scroll slightly to the right of the charts (shown in Figure 13) to see a one-share trading simulation.
FIGURE 13: EXCEL. This shows a trading simulation using zero crossings of the MADH ROC.
The transaction summary tab (Figure 14) provides transaction details for the trading simulation along with a few more statistics for this trading simulation.
FIGURE 14: EXCEL. Here you can see the transaction summary tab for the trading simulation.
To download this spreadsheet:
The spreadsheet file for this Traders’ Tip can be downloaded here. To successfully download it, follow these steps: