TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is Johnny Dough’s article in this issue, “Reversing MACD: The Sequel.” Here we present the November 2013 Traders’ Tips code with possible implementations in various software.
Code for AmiBroker is already provided in Dough’s article. Subscribers will find that code at the Subscriber Area of our website. (Click on “Article Code” from the S&C menu.) Presented here is an overview of possible implementations for other software.
Traders’ Tips code is provided to help the reader implement a selected technique from an article in this issue. The entries are contributed by various software developers or programmers for software that is capable of customization.
In “Reversing MACD: The Sequel” in this issue, author Johnny Dough describes the calculation of the price during formation of the next bar where the MACD histogram crosses zero on the next bar. We are providing two functions, two indicators, and a strategy. One function (_PMACDLevel2) returns the next bar price required to achieve a particular MACD value (for example, zero, current bar MACD value, and so on). The other function (_PMACDsignal) returns the next bar price required for the MACD histogram to be zero. One indicator (_PMACD_Ind3) plots the PMACDeq, PMACDzero, and PMACDsignal values referred to in the article. These plots can be shown or not shown based on the indicator inputs.
The second indicator (sMACD) is an MACD indicator that uses simple moving averages instead of exponential moving averages. The strategy provided here issues a buy or sell-short stop order at the PMACDsignal price (that is, the price where the histogram is zero).
This strategy is for illustrative purposes only and is not intended to be traded.
To download the EasyLanguage code, please visit our TradeStation and EasyLanguage support forum. The code from this article can be found here: https://www.tradestation.com/TASC-2013. The ELD filename is “_PMACD_Sequel.ELD.”
The EasyLanguage code for the function _PMACDLevel2, indicator _PMACD_Ind3, indicator _sMACD, and strategy_ PMACD_Strategy2 can also be found below.
_PMACDLevel2 (Function) { TASC Article - November 2013 } { Reversing MACD: The Sequel } { Johnny Dough } { this function is titled _PMACDLevel2 because a prior function named _PMACDlevel was developed for the January 2012 TASC article referred to in the November 2013 article; the _PMACDlevel function can be downloaded from the TradeStation forum at the following link: https://community.tradestation.com/Discussions /Topic.aspx?Topic_ID=116668 } inputs: double MACDValue( numericseries ), { MACD value to use in calculating the price level that will result in this MACD value; for example, to determine what price level on the next bar will result in a MACD value of zero, pass in zero for this input } double MACDPrice( numericseries ), { price used in MACD calculations } double p_fast( numericsimple ), { fast moving average length for MACD } double p_slow( numericsimple ), { slow moving average length for MACD } int p_matype( numericsimple) ; { pass in 1 for EMA; any other value for SMA } variables: intrabarpersist double alphaX( 0 ), intrabarpersist double alphaY( 0 ), intrabarpersist double One_alphaX( 0 ), intrabarpersist double One_alphaY( 0 ), double FastSMA( 0 ), double SlowSMA( 0 ), double RtnPrice( 0 ), double sRtnPrice( 0 ) ; once begin { calculate smoothing factors; it is assumed that these values are constant on each bar (i.e., the lengths p_fast and p_slow are not changed) } alphaX = 2 / ( 1 + p_fast ) ; alphaY = 2 / ( 1 + p_slow ) ; One_alphaX = 1 - alphaX ; One_alphaY = 1 - alphaY ; Value99 = _PMACDLevel2[1] ; { force this function to be a series function since the EMA depends on the prior bar value } end ; { calculate the price level on the next bar where the calculated MACD Value for that price is equal to the "MACDValue" input value ( when using Exponential Moving Averages ) } RtnPrice = ( MACDValue + XAverage( MACDPrice, p_slow ) * One_alphaY - XAverage( MACDPrice, p_fast ) * One_alphaX ) / ( alphaX - alphaY ) ; { calculate the price level on the next bar where the calculated MACD Value for that price is equal to the "MACDValue" input value ( when using Simple Moving Averages ) } if p_matype <> 1 then begin FastSMA = Average( MACDPrice, p_fast ) ; SlowSMA = Average( MACDPrice, p_slow ) ; Value1 = ( FastSMA * p_fast ) - MACDPrice[p_fast-1] ; Value2 = ( SlowSMA * p_slow ) - MACDPrice[p_slow-1] ; sRtnPrice = ( p_fast * p_slow * MACDValue - p_slow * Value1 + p_fast * Value2 ) / ( p_slow - p_fast ) ; end ; { return the appropriate value based on moving average type passed in via "p_matype" input } if p_matype = 1 then { return value when using Exponential Moving Averages } _PMACDLevel2 = RtnPrice else { return value when using Simple Moving Averages } _PMACDLevel2 = sRtnPrice ; _PMACDsignal (Function) { TASC Article - November 2013 } { Reversing MACD: The Sequel } { Johnny Dough } inputs: double MACDPrice( numericseries ), { price used in MACD calculations } double p_fast( numericsimple ), { fast moving average length for MACD } double p_slow( numericsimple ), { slow moving average length for MACD } double p_signal( numericsimple ), { moving average length for MACD Signal } int p_matype( numericsimple) ; { moving average type - pass in 1 for EMA; any other value for SMA } variables: intrabarpersist double alphaX( 0 ), intrabarpersist double alphaY( 0 ), intrabarpersist double alphaZ( 0 ), intrabarpersist double XY( 0 ), intrabarpersist double XZ( 0 ), intrabarpersist double YZ( 0 ), intrabarpersist double XYZ( 0 ), intrabarpersist double One_alphaX( 0 ), intrabarpersist double One_alphaY( 0 ), intrabarpersist double One_alphaZ( 0 ), double MACDsignal( 0 ), double sMACD( 0 ), double RtnPrice( 0 ), double sRtnPrice( 0 ) ; once begin { calculate smoothing factors; it is assumed that these values are constant on each bar (i.e., p_fast, p_slow, and p_signal lengths are constant on each bar and are not changed) } alphaX = 2 / ( 1 + p_fast ) ; alphaY = 2 / ( 1 + p_slow ) ; alphaZ = 2 / ( 1 + p_signal ) ; One_alphaX = 1 - alphaX ; One_alphaY = 1 - alphaY ; One_alphaZ = 1 - alphaZ ; XY = p_fast * p_slow ; XZ = p_fast * p_signal ; YZ = p_slow * p_signal ; XYZ = p_fast * p_slow * p_signal ; Value99 = _PMACDsignal[1] ; { force function to be a series function since the EMA depends on the prior bar value } end ; { calcualte price level where MACD Value is equal to the MACD signal value ( histogram = 0 ) using Exponential Moving Averages } MACDsignal = XAverage( MACD( MACDPrice, p_fast, p_slow ), p_signal ) ; RtnPrice = ( MACDsignal - XAverage( MACDPrice, p_fast ) * One_alphaX + XAverage( MACDPrice, p_slow ) * One_alphaY ) / ( alphaX - alphaY ) ; { calculate price level on the next bar where MACD Value is equal to the MACD signal value ( histogram = 0 ) using Simple Moving Averages } if p_matype <> 1 and p_signal > 1 then begin sMACD = Average( MACDPrice, p_fast ) - Average( MACDPrice, p_slow ) ; sRtnPrice = ( sMACD * ( XYZ - XY ) - XYZ * Average( sMACD, p_signal ) - MACDPrice[p_fast - 1] * ( YZ - p_slow ) + MACDPrice[p_slow - 1] * ( XZ - p_fast ) + XY * sMACD[p_signal - 1] ) / ( XZ - YZ - p_fast + p_slow ) ; end ; { return the appropriate value based on moving average type passed in } if p_matype = 1 then { return value when using Exponential Moving Averages } _PMACDsignal = RtnPrice else { return value when using Simple Moving Averages } _PMACDsignal = sRtnPrice ; _PMACD_Ind3 (Indicator) { TASC Article - November 2013 } { Reversing MACD: The Sequel } { Johnny Dough } { this indicator is titled ...Ind3 because 2 other PMACD indicators were developed for the January 2012 TASC article referred to in the November 2013 article; these indicators can be downloaded from the TradeStation forum at the following link: https://community.tradestation.com/Discussions/ Topic.aspx?Topic_ID=116668 } inputs: double MACDPrice( Close ), { price used in MACD calculations } int FastLength( 12 ), { the shorter of the two moving average lengths used to calculate the MACD value, in bars } int SlowLength( 26 ), { the longer of the two moving average lengths used to calculate the MACD value, in bars } int MACDLength( 9 ), { the number of bars over which to average the MACD value } int MAType( 1 ), { moving average type - pass in 1 for EMA; any other value for SMA } bool ShowPMACDeq( true ), { set to true to plot the value of PMACDeq } bool ShowPMACDzero( true ), { set to true to plot the value of PMACDzero } bool ShowPMACDsignal( true ) ; { set to true to plot the value of PMACDsignal } variables: double MACDValue( 0 ), double PMACDeq( 0 ), double PMACDzero( 0 ), double PMACDsignal( 0 ) ; MACDValue = MACD( MACDPrice, FastLength, SlowLength ) ; { use SMA if MAType is not set to 1 } if MAType <> 1 then MACDValue = Average( MACDPrice, FastLength ) - Average( MACDPrice, SlowLength ) ; { the functions called below are series function and are therefore called on each bar regardless of whether they are placed behind any conditional statements or not; therefore, they are not placed behind any conditional statements } PMACDeq = _PMACDLevel2( MACDValue, MACDPrice, FastLength, SlowLength, MAType ) ; PMACDzero = _PMACDLevel2( 0, MACDPrice, FastLength, SlowLength, MAType ) ; PMACDsignal = _PMACDsignal( MACDPrice, FastLength, SlowLength, MACDLength, MAType ) ; { plots; note that the plots are displaced forward 1 bar because the values returned from the functions above are "next bar" price values } if ShowPMACDeq then Plot1[-1]( PMACDeq, "PMACDeq" ) ; if ShowPMACDzero then Plot2[-1]( PMACDzero, "PMACDzero" ) ; if ShowPMACDsignal then Plot3[-1]( PMACDsignal, "PMACDsig" ) ; _sMACD (Indicator) { TASC Article - November 2013 } { Reversing MACD: The Sequel } { Johnny Dough } { MACD indicator using Simple Moving Averages } inputs: double Price( Close ), { price used in MACD calculations } int FastLength( 12 ), { the shorter of the two simple moving average lengths used to calculate the MACD value, in bars } int SlowLength( 26 ), { the longer of the two simple moving average lengths used to calculate the MACD value, in bars } int MACDLength( 9 ), { the number of bars over which to average the MACD value } int MACDDiff_Up_Color( Green ), { color to be used to plot positive values of MACDDiff } int MACDDiff_Down_Color( Red ), { color to be used to plot negative values of MACDDiff } int BackgroundColorAlertCell( DarkGray ) ; { if alert criteria are met, this is the color used for the cell background in RadarScreen; if it is not desired for the cell background color to change when the alert criteria are met, set this input to the default cell background color } variables: intrabarpersist bool PlotCrossBarsAgo( false ), double MyMACD( 0 ), double MACDAvg( 0 ), double MACDDiff( 0 ), double HistogramColor( 0 ), int CrossBarsAgo( 0 ) ; once PlotCrossBarsAgo = GetAppInfo( aiApplicationType ) <> cChart ; MyMACD = Average( Price, FastLength ) - Average( Price, SlowLength ) ; MACDAvg = Average( MyMACD, MACDLength ) ; MACDDiff = MyMACD - MACDAvg ; HistogramColor = iff( MACDDiff > 0, MACDDiff_Up_Color, MACDDiff_Down_Color ) ; if ( MACDDiff > 0 and MACDDiff[1] <= 0 ) or ( MACDDiff < 0 and MACDDiff[1] >= 0 ) then CrossBarsAgo = 0 else CrossBarsAgo += 1 ; Plot1( MyMACD, "MACD" ) ; Plot2( MACDAvg, "MACDAvg" ) ; Plot3( MACDDiff, "MACDDiff", HistogramColor ) ; if PlotCrossBarsAgo = false then Plot4( 0, "ZeroLine" ) else Plot5( CrossBarsAgo, "CrossBarsAgo", HistogramColor ) ; { alert criteria } if MACDDiff crosses over 0 then begin SetPlotBGColor( 5, BackgroundColorAlertCell ) ; Alert( "MACD diff. crossing over 0." ) ; end else if MACDDiff crosses under 0 then begin SetPlotBGColor( 5, BackgroundColorAlertCell ) ; Alert( "MACD diff. crossing under 0." ) ; end ; _ PMACD_Strategy2 (Strategy) { TASC Article - November 2013 } { Reversing MACD: The Sequel } { Johnny Dough } { note that this strategy is for illustrative purposes only and is not meant to be traded; additionally, the _PMACD_Strategy referenced below is for illustrative purposes as well and is not meant to be traded } { this strategy is titled _PMACD_Strategy2 because a prior strategy named _PMACD_Strategy was developed for the January 2012 TASC article referred to in the November 2013 article; the _PMACD_Strategy strategy can be downloaded from the TradeStation forum at the following link: https://community.tradestation.com/Discussions/ Topic.aspx?Topic_ID=116668 } inputs: double MACDPrice( Close ), { price used in MACD calculations } int FastLength( 12 ), { the shorter of the two moving average lengths used to calculate the MACD value } int SlowLength( 26 ), { the longer of the two moving average lengths used to calculate the MACD value } int MACDLength( 9 ), { the number of bars over which to average the MACD value } int MAType( 1 ) ; { moving average type - set to 1 for EMA; set to any other value for SMA } variables: double MyMACD( 0 ), double MACDAvg( 0 ), double MACDDiff( 0 ), double PMACDsignal( 0 ) ; MyMACD = MACD( Close, FastLength, SlowLength ) ; MACDAvg = XAverage( MyMACD, MACDLength ) ; if MAType <> 1 then begin MyMACD = Average( MACDPrice, FastLength ) - Average( MACDPrice, SlowLength ) ; MACDAvg = Average( MyMACD, MACDLength ) ; end ; MACDDiff = MyMACD - MACDAvg ; PMACDsignal = _PMACDsignal( MACDPrice, FastLength, SlowLength, MACDLength, MAType ) ; { entries } { note that this strategy uses a stop order for next bar execution without using IntrabarOrderGeneration; thus, a histogram cross of zero can occur intrabar, with a corresponding strategy entry even though the cross may not be present at the close of the bar when observing the MACD indicator } if MACDDiff > 0 then { issue sell stop order for price of cross } SellShort ( "PMACDSig SE" ) next bar PMACDsignal stop else if MACDDiff < 0 then { issue buy stop order for price of cross } Buy ( "PMACDSig LE" ) next bar PMACDsignal stop ; { exits } { no exits coded; this strategy reverses }
For more information about EasyLanguage in general, please see https://www.tradestation.com/EL-FAQ.
A sample chart is shown in Figure 1.
FIGURE 1: TRADESTATION. Here is a daily chart of AAPL with the referenced indicator and strategy applied to the chart with the calculations set to use exponential moving averages (set by the indicator and strategy inputs).
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.
Johnny Dough’s article in this issue, “Reversing MACD: The Sequel,” presents additional formulas for getting the price required for the MACD to give a signal on the next bar. The article includes code for an all-in-one indicator. This indicator prompts for the MACD time periods and whether to use simple or exponential moving averages. It then plots the close required on the next bar for three different signals: 1) the MACD to equal zero, 2) the MACD to equal the value on the current bar, and 3) the MACD to cross its signal line. The MetaStock code for this indicator is shown here:
x:= Input("short term MA periods",1,50,12); y:= Input("long term MA periods",2,50,26); z:= Input("signal line periods",1,50,9); type:= Input("MA type < 1=EMA | 2=SMA >",1,2,1); ax:= 2/(x+1); ay:= 2/(y+1); az:= 2/(z+1); cmacd:= Mov(C,x,E) - Mov(C,y,E); smacd:= Mov(C,x,S) - Mov(C,y,S); peflat:= (ax*Mov(C,x,E) - ay*Mov(C,y,E))/(ax-ay); peeq0:= ((1-ay)*Mov(C,y,E) - (1-ax)*Mov(C,x,E))/(ax-ay); pesig:= (Mov(cmacd,z,E) - (1-ax)*Mov(C,x,E) + (1-ay)*Mov(C,y,E))/(ax-ay); psflat:= (x*Ref(C,-Y+1) - y*Ref(C,-x+1))/(x-y); pseq0:= (x*y*(Mov(C,x,S)-Mov(C,y,S))+x*Ref(C,-y+1)-y*Ref(C,-x+1))/(x-y); pssig:= ((x*y*z - x*y)*smacd - x*y*z*Mov(smacd,z,S) - (y*z - y)*Ref(C,-x+1) + (x*z-x)*Ref(C,-y+1) + x*y * Ref(smacd,-z+1) )/(x*z-y*z-x+y); If(type=1,peflat,psflat); If(type=1,peeq0,pseq0); If(type=1,pesig,pssig);
For this month’s Traders’ Tip, we’ve provided the formula PMACD_Signal.efs based on Johnny Dough’s article in this issue, “Reversing MACD: The Sequel.”
The study contains formula parameters to set the values for the price source, fast period, slow period, signal period, MA type, and price shift plotting option, which may be configured through the Edit Chart window (right-click on chart and select edit chart).
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: Interactive Data Corporation (Copyright © 2013) All rights reserved. This sample eSignal Formula Script (EFS) is for educational purposes only. Interactive Data Corporation reserves the right to modify and overwrite this EFS file with each new release. Description: Reversing MACD: The Sequel Version: 1.00 11/11/2013 Formula Parameters: Default: Price Source Close Period Fast 12 Period Slow 26 Period Signal 9 MA type EMA Plot computed price shift forward by 1 true 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("Reversing MACD: The Sequel"); setDefaultBarFgColor(Color.green, 0); setDefaultBarFgColor(Color.blue, 1); setDefaultBarFgColor(Color.red, 2); var x=0; fpArray[x] = new FunctionParameter("fpPriceSource", FunctionParameter.STRING); with(fpArray[x++]) { setName("Price Source"); addOption("Open"); addOption("Close"); addOption("Low"); addOption("High"); addOption("HLC/3"); setDefault("Close"); } fpArray[x] = new FunctionParameter("fpFast", FunctionParameter.NUMBER); with(fpArray[x++]) { setName("Period Fast"); setLowerLimit(1); setUpperLimit(50); setDefault(12); } fpArray[x] = new FunctionParameter("fpSlow", FunctionParameter.NUMBER); with(fpArray[x++]) { setName("Period Slow"); setLowerLimit(2); setUpperLimit(50); setDefault(26); } fpArray[x] = new FunctionParameter("fpSignal", FunctionParameter.NUMBER); with(fpArray[x++]) { setName("Period Signal"); setLowerLimit(0); setUpperLimit(50); setDefault(9); } fpArray[x] = new FunctionParameter("fpMAtype", FunctionParameter.STRING); with(fpArray[x++]) { setName("MA type"); addOption("EMA"); addOption("SMA"); setDefault("EMA"); } fpArray[x] = new FunctionParameter("fpShift", FunctionParameter.BOOLEAN); with(fpArray[x++]) { setName("Plot computed price shift forward by 1"); setDefault(true); } } var bInit = false; var bVersion = null; var xEq = null; var xZero = null; var xSignal = null; function main(fpPriceSource, fpFast, fpSlow, fpSignal, fpMAtype, fpShift) { if (bVersion == null) bVersion = verify(); if (bVersion == false) return; var nShift = null; var xSource = null; if (!fpShift) nShift = 0 else nShift = -1; if(!bInit) { switch (fpPriceSource) { case "Open": xSource = open(); break; case "Close": xSource = close(); break; case "High": xSource = high(); break; case "Low": xSource = low(); break; case "HLC/3": xSource = hlc3(); break; default : return; } switch (fpMAtype) { case "SMA": NameAverage_Eq = "PsMACDeq"; NameAverage_Zero = "PsMACDzero"; NameAverage_Signal = "PsMACDsignal"; break; case "EMA": NameAverage_Eq = "PMACDeq"; NameAverage_Zero = "PMACDzero"; NameAverage_Signal = "PMACDsignal"; break; default : return; } xEq = efsInternal(NameAverage_Eq, fpFast, fpSlow, xSource); xZero = efsInternal(NameAverage_Zero, fpFast, fpSlow, xSource); xSignal = efsInternal(NameAverage_Signal, fpFast, fpSlow, fpSignal, xSource) setCursorLabelName(NameAverage_Eq, 0); setCursorLabelName(NameAverage_Zero, 1); setCursorLabelName(NameAverage_Signal, 2); bInit = true; } var nEq = xEq.getValue(nShift); var nZero = xZero.getValue(nShift); var nSignal = xSignal.getValue(nShift); if (nEq == null || nZero == null || nSignal == null) return; return [nEq, nZero, nSignal] } var xEMAx = null; var xEMAy = null; function Calc_ValueEMA(nPeriodX, nPeriodY, xSeries) { if (getBarState() == BARSTATE_ALLBARS) { xEMAx = ema(nPeriodX, xSeries); xEMAy = ema(nPeriodY, xSeries); } var nEMAx = xEMAx.getValue(0); var nEMAy = xEMAy.getValue(0); if (nEMAx == null || nEMAy == null) return; var nAlphaX = 2 / (1 + nPeriodX); var nAlphaY = 2 / (1 + nPeriodY); return new Array(nEMAx, nEMAy, nAlphaX, nAlphaY); } function PMACDeq(nPeriodX, nPeriodY, xSeries) { var nReturnValue = null; var aValueArray = Calc_ValueEMA(nPeriodX, nPeriodY, xSeries); if (aValueArray != null) { var nEMAx = aValueArray[0]; var nEMAy = aValueArray[1]; var nAlphaX = aValueArray[2]; var nAlphaY = aValueArray[3]; nReturnValue = (nAlphaX * nEMAx - nAlphaY * nEMAy) / (nAlphaX - nAlphaY); } return nReturnValue; } function PMACDlevel(nPeriodX, nPeriodY, nLevel, xSeries) { var nReturnValue = null; var aValueArray = Calc_ValueEMA(nPeriodX, nPeriodY, xSeries); if (aValueArray != null) { var nEMAx = aValueArray[0]; var nEMAy = aValueArray[1]; var nAlphaX = aValueArray[2]; var nAlphaY = aValueArray[3]; nReturnValue = (nLevel + (1 - nAlphaY) * nEMAy - (1 - nAlphaX) * nEMAx) / (nAlphaX - nAlphaY); } return nReturnValue; } var xEMAx = null; var xEMAy = null; var xMACDValue = null; var xMACDSignal = null; function PMACDsignal(nPeriodX, nPeriodY, nPeriodZ, xSeries) { if (getBarState() == BARSTATE_ALLBARS) { xEMAx = ema(nPeriodX, xSeries); xEMAy = ema(nPeriodY, xSeries); xMACDValue = efsInternal("Calc_MACDValue", xEMAx, xEMAy); xMACDSignal = ema(nPeriodZ, xMACDValue); } var nReturnValue = null; var aValueArray = Calc_ValueEMA(nPeriodX, nPeriodY, xSeries); if (aValueArray != null) { var nEMAx = aValueArray[0]; var nEMAy = aValueArray[1]; var nAlphaX = aValueArray[2]; var nAlphaY = aValueArray[3]; var nMACDSignal = xMACDSignal.getValue(0); if (nMACDSignal == null) return; nReturnValue = (nMACDSignal - nEMAx * (1 - nAlphaX) + nEMAy * (1 - nAlphaY)) / (nAlphaX - nAlphaY); } return nReturnValue; } function Calc_MACDValue(xSeries1, xSeries2) { var nValue1 = xSeries1.getValue(0); var nValue2 = xSeries2.getValue(0); if (nValue1 == null || nValue2 == null) return; var nReturnValue = nValue1 - nValue2; return nReturnValue; } function PMACDzero(nPeriodX, nPeriodY, xSeries) { return PMACDlevel(nPeriodX, nPeriodY, 0, xSeries); } function PsMACDeq( nPeriodX, nPeriodY, xSeries) { var nOffsetX = xSeries.getValue(1-nPeriodX); var nOffsetY = xSeries.getValue(1-nPeriodY); if (nOffsetX == null || nOffsetY == null) return; var nReturnValue = (nPeriodX * nOffsetY - nPeriodY * nOffsetX) / (nPeriodX - nPeriodY); return nReturnValue; } var xSMAx_Zer = null; var xSMAy_Zer = null; function PsMACDzero( nPeriodX, nPeriodY, xSeries) { if (getBarState() == BARSTATE_ALLBARS) { xSMAx_Zer = sma(nPeriodX, xSeries); xSMAy_Zer = sma(nPeriodY, xSeries); } var nSMAx = xSMAx_Zer.getValue(0); var nSMAy = xSMAy_Zer.getValue(0); var nOffsetX = xSeries.getValue(1-nPeriodX); var nOffsetY = xSeries.getValue(1-nPeriodY); if (nSMAx == null || nSMAy == null || nOffsetX == null || nOffsetY == null) return; var nReturnValue = (nPeriodX * nPeriodY * (nSMAx - nSMAy) + nPeriodX * nOffsetY - nPeriodY * nOffsetX) / (nPeriodX - nPeriodY); return nReturnValue; } var xsMACD = null; var xSMAsMACD = null; function PsMACDsignal(nPeriodX, nPeriodY, nPeriodZ, xSeries) { var nReturnValue = null; if (nPeriodZ != 1) { if (getBarState() == BARSTATE_ALLBARS) { xsMACD = efsInternal("Calc_sMACD", nPeriodX, nPeriodY, xSeries); xSMAsMACD = sma(nPeriodZ, xsMACD); } var nsMACD = xsMACD.getValue(0); var nSMAsMACD = xSMAsMACD.getValue(0); var nOffsetX = xSeries.getValue(1-nPeriodX); var nOffsetY = xSeries.getValue(1-nPeriodY); var nOffsetZ = xsMACD.getValue(1-nPeriodZ); if (nsMACD == null || nSMAsMACD == null || nOffsetX == null || nOffsetY == null || nOffsetZ == null) return; nReturnValue = (nsMACD * (nPeriodX * nPeriodY * nPeriodZ - nPeriodX * nPeriodY) - nPeriodX * nPeriodY * nPeriodZ * nSMAsMACD - nOffsetX * (nPeriodY * nPeriodZ - nPeriodY) + nOffsetY * (nPeriodX * nPeriodZ - nPeriodX) + nPeriodX * nPeriodY * nOffsetZ) / (nPeriodX * nPeriodZ - nPeriodY * nPeriodZ - nPeriodX + nPeriodY); } return nReturnValue; } var xSMAx_Sig = null; var xSMAy_Sig = null; function Calc_sMACD(nPeriodX, nPeriodY, xSeries) { if (getBarState() == BARSTATE_ALLBARS) { xSMAx_Sig = sma(nPeriodX, xSeries); xSMAy_Sig = sma(nPeriodY, xSeries); } var nSMAx = xSMAx_Sig.getValue(0); var nSMAy = xSMAy_Sig.getValue(0); if (nSMAx == null || nSMAy == null) return; var nReturnValue = nSMAx - nSMAy; return nReturnValue; } function verify() { var b = false; if (getBuildNumber() < 779) { drawTextAbsolute(5, 35, "This study requires version 8.0 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; }
A sample chart is shown in Figure 2.
FIGURE 2: eSIGNAL. The study contains formula parameters to set the values for the price source, fast period, slow period, signal period, MA type, and price shift plotting option.
In this issue, author Johnny Dough, in his article “Reversing MACD: The Sequel,” shares a new tool: an indicator that anticipates the price when MACD crosses above or below its signal line one bar ahead. It complements his January 2012 article in S&C on the reverse MACD indicator.
We’re including this reverse MACD signal indicator in the latest version of our TASCIndicators library so that Wealth-Lab users can apply it to their charts and strategies, either coded in C# directly or built with the help of the GUI-based strategy wizard.
In addition, Dough provides the formulas for reverse functions for the MACD indicator based on simple moving averages (SMA) rather than the traditional exponential ones (EMA). These can also be found in the TASCIndicators library.
To start using them, please install the “TASC Magazine Indicators” library from the Extensions section of our website if you haven’t done so already, or apply the update directly using Wealth-Lab’s extension manager. In both cases, don’t forget to restart Wealth-Lab.
The strategy code shown here illustrates its application in Wealth-Lab, buying at stop order (or above) at the reverse-engineered price when a bullish MACD is crossing above its signal line, and exiting if the opposite criteria have been met after the MACD has turned bearish. Applying the idea to the short side is equally easy: Simply reverse the rules. Copy/paste the included strategy’s C# code or simply let Wealth-Lab do the job: In the open strategy dialog, click “download” to get this strategy’s code as well as other strategies contributed by the Wealth-Lab community.
A sample chart is shown in Figure 3.
FIGURE 3: WEALTH-LAB. This sample Wealth-Lab 6 chart illustrates the application of the RevEngMACDSignal indicator. Crossovers and crossunders of MACD and its signal line are highlighted in green and red, respectively.
C# Code using System; using System.Drawing; using WealthLab; using WealthLab.Indicators; using TASCIndicators; using Community.Indicators; namespace WealthLab.Strategies { public class ReversingMACDSequel : WealthScript { protected override void Execute() { // Reversing MACD HideVolume(); RevEngMACDSignal r = RevEngMACDSignal.Series( Close,12,26,9,false ); PlotSeries( PricePane, r, Color.Blue, LineStyle.Dashed, 2 ); // MACD and MACD signal DataSeries macd = MACD.Series( Close ); DataSeries macdSignal = MACDEx_Signal3.Series( Close,12,26,9 ); DataSeries macdHist = MACDEx_Histogram.Series( Close,12,26 ); ChartPane MACDPane = CreatePane( 50, true, true ); PlotSeries( MACDPane, macd, Color.Blue, LineStyle.Solid, 2 ); PlotSeries( MACDPane, macdSignal, Color.Red, LineStyle.Solid, 2 ); PlotSeries( MACDPane, macdHist, Color.Teal, LineStyle.Histogram, 2 ); for(int bar = r.FirstValidValue; bar < Bars.Count; bar++) { if( CrossOver( bar, macdSignal, macd ) ) SetBackgroundColor( bar, Color.FromArgb( 30, Color.Green ) ); if( CrossUnder( bar, macdSignal, macd ) ) SetBackgroundColor( bar, Color.FromArgb( 30, Color.Red ) ); if (IsLastPositionActive) { if( macd[bar] > 0 ) SellAtStop( bar+1, LastPosition, r[bar] ); } else { if( macd[bar] < 0 ) BuyAtStop( bar+1, r[bar] ); } } } } }
The reverse MACD indicators described by Johnny Dough in his article in this issue, “Reversing MACD: The Sequel,” can be easily implemented with a few of NeuroShell Trader’s 800+ indicators. Simply select “New indicator” from the Insert menu and use the Indicator Wizard to recreate the following indicators:
PMACDeq: Divide( Sub( Mul2( ExpAvg(Close,12), Divide(2,Add2(1,12))), Mul2( ExpAvg(Close,26), Divide(2,Add2(1,26)))), Sub( Divide(2,Add2(1,12)), Divide(2,Add2(1,26)))) PMACDlevel: Divide( Add2(LEVEL, Sub( Mul2( ExpAvg(Close,26), Sub(1, Divide(2, Add2(1,26)))), Mul2( ExpAvg(Close,12), Sub(1, Divide(2, Add2(1,12)))))), Sub( Divide(2, Add2(1,12)), Divide(2, Add2(1,26)))) PMACDzero: PMACDlevel(0) PMACDsignal: Divide( Add2( Sub( MACD Signal(Close,9,12,26), Mul2( ExpAvg(Close,12), Sub(1, Divide(2, Add2(1,12))))), Mul2( ExpAvg(Close,26), Sub(1, Divide(2, Add2(1,26))))),Sub( Divide(2, Add2(1,12)), Divide(2, Add2(1,26)))) sMACD: Sub( Avg( Close, 12), Avg( Close, 26)) PsMACDeq: Divide( Sub( Mul2(12, Lag(Close,25)), Mul2(26, Lag(Close,11))), Sub(12,26)) PsMACDzero: Divide( Add2( Mul3(12,26, Sub( Avg(Close,12), Avg(Close,26))), Sub( Mul2(12, Lag(Close,25)), Mul2(26, Lag(Close,11)))), Sub(12,26)) PsMACDSignal: Divide( Add3( Sub( Sub( Mul2( SMACD, Sub( Mul3(12,26,9), Mul2(12,26))), Mul4(12,26,9, Avg(SMACD,9))), Mul2( Lag( Close,11), Sub( Mul2(26,9),26))), Mul2( Lag(Close,25), Sub( Mul2(12,9),12)), Mul3(12,26, Lag(SMACD,8))), Add2( Sub( Sub( Mul2(12,9), Mul2(26,9)),12),26))
Users of NeuroShell Trader can go to the STOCKS & COMMODITIES section of the NeuroShell Trader free technical support website to download a copy of this or any previous Traders’ Tips.
A sample chart is shown in Figure 4.
FIGURE 4: NEUROSHELL TRADER, REVERSE MACD. This NeuroShell Trader chart displays the reverse MACD indicators.
In “Reversing MACD: The Sequel” in this issue, author Johnny Dough presents functions that return price values for the MACD indicators. I am providing AIQ code for the following functions based on the AmiBroker code given in Dough’s article:
I created some additional rules to show reports that display the price values of the functions:
The NearXO report is useful if you want to enter a position on a buy or sell stop-on-close when the MACD oscillator crosses zero. This report sorts by the MACD oscillator. The ones with a positive MACD oscillator amounts calculate the price that would be used to go short at the close for a cross-down on the oscillator. The ones with negative MACD oscillator amounts calculate the price that would be used to go long at the close for a cross-up on the oscillator.
I ran the NearXO report on September 10, 2013 using the NASDAQ 100 list of stocks. There were 12 that showed up on the report, meaning they were near a cross-up or cross-down of the MACD oscillator.
In Figure 5, I show a chart of Qiagen NV (QGEN), which appeared on the report. The report showed that a next day’s close price of 20.72 or higher would cause the MACD oscillator to go from negative to positive. On September 11, 2013, QGEN closed at 20.98 and the MACD oscillator moves to a positive number.
FIGURE 5: AIQ. Here’s a sample chart of QGEN with the MACD oscillator, looking for closing price (white line) and price to make a cross up or cross down on the oscillator (dark green line).
The AIQ code and EDS file can be downloaded from www.TradersEdgeSystems.com/traderstips.htm.
!REVERSING MACD: THE SEQUEL !Author: Johnny Dough, TASC November 2013 !Coded by: Richard Denning 9/9/2013 !www.TradersEdgeSystems.com !INPUTS: price is [close]. period_X is 12. period_Y is 25. period_Z is 9. level is 0. minPrice is 10. rangeFactor is 1. !PMACDsignal returns price where MACD crosses signal line ! or MACD histogram cross of 0: alphaX is 2 / ( 1 + period_X ). alphaY is 2 / ( 1 + period_Y ). alphaZ is 2 / ( 1 + period_Z ). One_alphaX is 1 - alphaX. One_alphaY is 1 - alphaY. One_alphaZ is 1 - alphaZ. MACDvalue is expavg( price, period_X ) - expavg( price, period_Y ). MACDvalue_1 is valresult(MACDvalue,1). MACDsignal is expavg( MACDvalue, period_Z ). PMACDsignal is ( MACDsignal - expavg( price, period_X ) * one_alphaX + expavg( price, period_Y ) * one_alphaY ) / ( alphaX - alphaY ). !PMACDlevel returns price where MACD is equal to level value ! e.g. PMACDlevel(0, C, 12, 16) would return the series ! where next price would make MACD=0 PMACDlevel is (Level + expavg( price, period_Y ) * one_alphaY - expavg( price, period_X )* one_alphaX ) / ( alphaX - alphaY ). !PMACDeq returns price where MACD is equal to previous bar MACD PMACDeq is ( expavg( price, period_X ) * alphaX - expavg( price, period_Y )* alphaY ) / ( alphaX - alphaY ). !ADDITIONAL CODE NOT PROVIDED BY AUTHOR: PMACDsignal_1 is valresult(PMACDsignal,1). !PLOT-OFFSET BY ONE DAY MACDosc_1 is val([MACD osc],1). !Prior day's MACD oscillator value !RULES FOR GENERATING REPORTS: PMACxup if price > PMACDsignal_1 and valrule(price <= PMACDsignal_1,1) and price > minPrice. PMACxdn if price < PMACDsignal_1 and valrule(price >= PMACDsignal_1,1) and price > minPrice. PMACDsigPct is (PMACDsignal / [close] - 1) * 100. AvgRangePct is simpleavg(([high]/[low]-1)*100,200). NearXO if price > minPrice and abs(PMACDsigPct) <= AvgRangePct*rangeFactor. ShowValues if price > minPrice.
In “Reversing MACD: The Sequel” in this issue, author Johnny Dough presents functions that return price values for the MACD indicators. The TradersStudio code for these functions is provided at the following websites:
The following code files are contained in the download:
Due to time constraints, I did not write any code that uses the functions, such as an indicator plot or a trading system.The code for the functions themselves is shown here:
'REVERSING MACD: THE SEQUEL 'Author: Johnny Dough, TASC November 2013 'Coded by: Richard Denning 9/12/2013 'wwwTradersEdgeSystemscom function PMACDsignal(price as bararray,period_X,period_Y,period_Z) 'PMACDsignal returns price where MACD crosses signal line ' or MACD h=togram cross of 0: Dim alphaX, alphaY, alphaZ,One_alphaX,One_alphaY,One_alphaZ,den Dim MACDvalue As BarArray Dim MACDsignal As BarArray alphaX = 2 / ( 1 + period_X ) alphaY = 2 / ( 1 + period_Y ) alphaZ = 2 / ( 1 + period_Z ) One_alphaX = 1 - alphaX One_alphaY = 1 - alphaY One_alphaZ = 1 - alphaZ MACDvalue = XAverage( price, period_X ) - XAverage( price, period_Y ) MACDsignal = XAverage( MACDvalue, period_Z ) den = iif( (alphaX - alphaY) <> 0,alphaX - alphaY,0.00001) PMACDsignal=(MACDsignal-XAverage(price,period_X)*One_alphaX+XAverage(price,period_Y)*One_alphaY)/den End Function '------------------------------------------------------------------------------------------------------- function PMACDlevel(level,price as bararray,period_X,period_Y) 'PMACDlevel returns price where MACD = equal To level value ' e.g. PMACDlevel(0, C, 12, 16) would return the series ' where Next price would make MACD=0 Dim alphaX, alphaY, One_alphaX, One_alphaY, den alphaX = 2 / ( 1 + period_X ) alphaY = 2 / ( 1 + period_Y ) One_alphaX = 1 - alphaX One_alphaY = 1 - alphaY den = iif( (alphaX - alphaY) <> 0,alphaX - alphaY,0.00001) PMACDlevel=(level+XAverage(price,period_Y)*One_alphaY-XAverage(price,period_X)*One_alphaX)/den End Function '-------------------------------------------------------------------------------------------------------- function PMACDeq(price as bararray,period_X,period_Y) 'PMACDeq returns price where MACD is equal to previous bar MACD Dim alphaX, alphaY, One_alphaX, One_alphaY, den alphaX = 2 / ( 1 + period_X ) alphaY = 2 / ( 1 + period_Y ) den = iif( (alphaX - alphaY) <> 0,alphaX - alphaY,0.00001) PMACDeq=(XAverage(price,period_X)*alphaX-XAverage(price,period_Y)*alphaY)/den End Function '--------------------------------------------------------------------------------------------------------
ReversingMACD2, based on Johnny Dough’s article in this issue, “Reversing MACD: The Sequel,” has been implemented as an indicator available for download at www.ninjatrader.com/SC/November2013SC.zip.
Once you have downloaded it, 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 or greater.
You can review the indicator source code by selecting the menu Tools → Edit NinjaScript → Indicator from within the NinjaTrader Control Center window and selecting the “ReversingMACD2” file.
A sample chart implementing the strategy is shown in Figure 6.
FIGURE 6: NINJATRADER, REVERSE MACD. This screenshot shows the ReversingMACD2 applied to a 777-volume bars intraday chart of the December 2013 ZB futures contract.
The Traders’ Tip for this month is based on “Reversing MACD: The Sequel” in this issue by Johnny Dough. In the article, Dough builds on January 2012 S&C article, “Reversing MACD,” which uses the indicator PMACDsignal to indicate the price necessary to cause two indicator value levels to meet. In his article in this issue, Dough discusses the underlying price necessary to cause the MACD line to meet its signal line.
Dough also changes the type of moving averages used earlier in his January 2012 article from exponential to simple arithmetic. The moving average indicates the price necessary for the averages to cross each other; to cross a set level; and to cross a signal line. See Figure 7.
FIGURE 7: UPDATA. Here, the PMACD is applied to Apple (AAPL) in daily resolution.
The Updata code for this technique has been added to the Updata library and may be downloaded by clicking the custom menu and indicator library. Those who cannot access the library due to a firewall may paste the code shown here into the Updata custom editor and save it.
'PMACDsignal PARAMETER "MACD Period 1" #Period1=12 PARAMETER "MACD Period 2" #Period2=26 PARAMETER "Signal Period" #Signal=5 NAME "PMACDEq" "" DISPLAYSTYLE 2LINES INDICATORTYPE TOOL PLOTSTYLE THICK2 RGB(255,50,50) @AlphaX=0 @AlphaY=0 @AlphaZ=0 @expAvgX=0 @expAvgY=0 @expAvgZ=0 @MACDSignal=0 @PMACDSignalPrev=0 @PMACDSignal=0 FOR #CURDATE=0 TO #LASTDATE If #CURDATE>MAX(#Period1,#Period2) @AlphaX=2/(1+#Period1) @AlphaY=2/(1+#Period2) @AlphaZ=2/(1+#Signal) @expAvgX=EAVE(#Period1) @expAvgY=EAVE(#Period2) @expAvgZ=EAVE(#Signal) @MACDSignal=SGNL(MACD(#Period1,#Period2,E),#Signal,E) @PMACDSignal=(@MACDSignal-(1-@AlphaX)*@expAvgX+(1-@AlphaY)*@expAvgY)/(@AlphaX-@AlphaY) @Plot=@PMACDSignal EndIf NEXT 'PsPMACDzero PARAMETER "MACD Period 1" #Period1=12 PARAMETER "MACD Period 2" #Period2=26 NAME "PsPMACDzero" "" DISPLAYSTYLE LINE INDICATORTYPE TOOL PLOTSTYLE THICK2 RGB(0,0,200) @PsPMACDzero=0 FOR #CURDATE=0 TO #LASTDATE If #CURDATE>MAX(#Period1,#Period2) @PsPMACDzero=(#Period1*#Period2*(MAVE(#Period1)-MAVE(#Period2))+#Period1*Close(#Period2)-#Period2*Close(#Period1))/(#Period1-#Period2) @PLOT=@PsPMACDzero EndIf NEXT 'PsPMACDeq PARAMETER "MACD Period 1" #Period1=12 PARAMETER "MACD Period 2" #Period2=26 NAME "PsPMACDeq" "" DISPLAYSTYLE LINE INDICATORTYPE TOOL PLOTSTYLE THICK2 RGB(0,200,0) @PsPMACDeq=0 FOR #CURDATE=0 TO #LASTDATE If #CURDATE>MAX(#Period1,#Period2) @PsPMACDeq=(#Period1*CLOSE(#Period2)-#Period2*CLOSE(#Period1))/(#Period1-#Period2) @PLOT=@PsPMACDeq EndIf NEXT 'PsMACDsignal PARAMETER "MACD Period 1" #Period1=12 PARAMETER "MACD Period 2" #Period2=26 PARAMETER "Signal Period" #Signal=9 NAME "PsPMACDsignal" "" DISPLAYSTYLE LINE INDICATORTYPE TOOL PLOTSTYLE THICK2 RGB(200,0,0) @sMACD=0 @sMACDsignal=0 @PsPMACDsignal=0 @A=0 @B=0 @C=0 @D=0 @E=0 FOR #CURDATE=0 TO #LASTDATE If #CURDATE>MAX(#Period1,#Period2) 'MACD type changed with 'M' for simple type. @sMACD=MACD(#Period1,#Period2,M) @sMACDsignal=SGNL(@sMACD,#Signal,M) 'Code split into 5 parts for easy print in magazine @A=@sMACD*(#Period1*#Period2*#Signal-#Period1*#Signal) @B=(#Period1*#Period2*#Signal)*@PsPMACDsignal @C=Close(#Period1)*(#Period2*#Signal-#Period2) @D=Close(#Period1)*(#Period1*#Signal-#Period1) @E=#Period1*#Period2*Hist(@sMACD,#Signal) @PsPMACDsignal=(@A-@B-@C+@D+@E)/((#Period1*#Signal)-(#Period2*#Signal)-#Period1+#Period2) @PLOT=@PsPMACDsignal EndIf NEXT
In “Reversing MACD: The Sequel” in this issue, author Johnny Dough takes a look at the MACD indicator and presents multiple functions that calculate what the price of the next bar would need to be for the following actions to occur: a cross of the MACD and its signal line, the MACD crossing the zero level, and the MACD changing direction.
FIGURE 8: TRADING BLOX. Here is a sample plot of the MACD, MACDSignal, PMACDeq, PMACDsignal, and PMACDzero functions.
The functions are given for both the standard exponential moving average (EMA) version of the MACD and the simple moving average (SMA) version. See Figure 8. The functions have been coded in the update indicators script as instrument permanent variables.
Since each function calculates the price of the next bar, the plots of each instrument permanent variable must be offset ahead one bar (Figure 9).
FIGURE 9: TRADING BLOX, CODE SETUP. Plots must be offset ahead one bar.
The computations needed in Microsoft Excel spreadsheet software to recreate Johnny Dough’s indicator lines that he describes in his article in this issue, “Reversing MACD: The Sequel,” are straightforward translations of the AmiBroker functions given in his article to Excel column formulas.
This spreadsheet uses one set of columns to calculate the EMA-based values and a second adjacent set of columns to calculate the SMA-based values.
The toggle button to display moving averages in the upper right of the chart controls which set of calculations actually appear on the chart (that is, the EMA or SMA). See Figure 10.
FIGURE 10: EXCEL, MACD & PMACD. This sample chart of AAPL displays the MACD and the PMACD indicator lines as calculated using the EMA.
Because the vertical range of the price indicator lines changes rather dramatically between the EMA setting and the SMA setting, this button also rescales the vertical axis as necessary.
The 1 day signal shift button is a simple on/off toggle that moves the indicator lines forward (to the right) one day when on.
Here are two changes to this user-control interface (Figure 11) to note that are departures from previous Excel Traders’ Tips:
FIGURE 11: EXCEL, USER CONTROLS. User controls (in blue) for this Traders’ Tip are located under the “reversing” heading. The intermediate values are computed from the period values. Computing the intermediate values here greatly simplifies the indicator line formulas.
The spreadsheet file for this Traders’ Tip can be downloaded here. To successfully download it, follow these steps: