The following post appeared on the xltrader list on 08-08-2001. I don't know if the statements and the code are correct, but it may help you anyway.
Andreas
+++ Snip +++
I discovered, in dissecting the ADX algorithm, that Welles Wilder
isn't as smart as he thinks. Either that or he diliberately
incorporated True Range to ADX confuse or impress people.
You see, with all his talk about discovering True Range and what a
deep market truth it is, and after developing his ADX indicator that
has True Range as part of the calculation, he doesn't seem to realize
that ALL the True Range terms cancel out! Do the math, or test it
yourself. You can substitute random numbers and get identital
results.
Here's the VB source. Note the final calculation for DX uses terms
which are all ratios having ATR in the denominator, and they all
cancel out.
Enjoy.
Alex
--------------------------------------------------------------------
Option Explicit
'Exponential moving average
'Closely approximates true moving average
'Arguments:
' last_ema = previous period's ema result
' a = new value to incorporate into ema
' n = number of periods
'Return value: current ema
Function ema(ByVal last_ema As Double, ByVal a As Double, ByVal n As
Integer) As Double
Dim w As Double
w = 2# / (n + 1)
ema = (1# - w) * last_ema + w * a
End Function
'Wilder's ADX
'Arguments:
' last_adx = value of ADX from previous period
' n = number of periods to use
' init = initialize averages (set to TRUE on 1st call)
' H, L = current high and low
' Hp, Lp, Cp = previous high, low, close
'Return value: current ADX
Function ADX(last_adx As Double, n As Integer, init As Boolean, _
H As Double, L As Double, _
Hp As Double, Lp As Double, Cp As Double) As Double
Dim i As Integer, tmp1 As Double, tmp2 As Double, TR As Double
Dim PDM As Double, MDM As Double, PDI As Double, MDI As Double
Dim DX As Double
Static ATR As Double, APDM As Double, AMDM As Double 'averages
If init Then ATR = 0#: APDM = 0#: AMDM = 0#
'step 1: calculation of directional movements
' DM is the largest part of the current trading range that
' is outside the previous trading range
'
' PDM = max(0, H - Hp) (plus direction movment)
' MDM = min(0, L - Lp) (minus direction movment)
'
'For an outside day:
' if |PDM| < |MDM| then PDM=0
' if |PDM| > |MDM| then MDM=0
' else PDM=MDM=0
'
'Here's a better alternative that works just as well or better:
'tmp1 = (H - Hp + L - Lp) / 2#
'If tmp1 > 0# Then PDM = tmp1: MDM = 0# Else MDM = tmp1: PDM = 0#
'...but we'll use Wilder's way for now.
PDM = Application.WorksheetFunction.Max(0, H - Hp)
MDM = Application.WorksheetFunction.Min(0, L - Lp)
tmp1 = Abs(PDM): tmp2 = Abs(MDM)
If tmp1 < tmp2 Then
PDM = 0#
ElseIf tmp2 < tmp1 Then
MDM = 0#
Else
PDM = 0#: MDM = 0# 'inside day
End If
'step 2: calculation of true range (TR)
' TR = max(H-L, Cp-L, H-Cp) where Cp=previous close
TR = Application.WorksheetFunction.Max(H - L, Cp - L, H - Cp)
'step 3: plus and minus directional indicators PDI and MDI
'
' The average plus and minus directional movement values are
' divided by the average TR to get PDI and MDI. An exponential
' moving average closely approximates the true average.
'
' ema(|PDM|, n) ema(|MDM|, n)
' PDI = ------------- MDI = -------------
' ema(TR, n) ema(TR, n)
ATR = ema(ATR, TR, n) 'average true range
If ATR = 0# Then 'special case ATR=0
PDI = APDM: MDI = AMDM 'use previous average values
Else
APDM = ema(APDM, Abs(PDM), n)
AMDM = ema(AMDM, Abs(MDM), n)
PDI = APDM / ATR
MDI = AMDM / ATR
End If
'step 4: calculation of direction movement index DX
' Step 3 isn't necessary for calculation of DX, because the
' ATR term cancels out here. Then PDI=APDM and MDI=AMDM.
'
' PDI - MDI
' DX = ----------- '* 100
' PDI + MDI
'
' we won't multiply by 100, so our ADX will be from 0 to 1.
tmp1 = PDI + MDI
If tmp1 = 0# Then
DX = last_adx
Else
DX = Abs(PDI - MDI) / tmp1
End If
'step 5: calculation of average directional movement index ADX
' ADX = ema(|DX|, n)
ADX = ema(last_adx, Abs(DX), n)
End Function
+++ End Snip +++