Average Directional Movement Index ADX Formula in C#

So in my continuing series of C# implementations of common indicators, I spent all of yesterday working out the ADX. The ADX was originally developed by J. Welles Wilder and published in his book New Concepts in Technical Trading Systems, June 1978.

Various sites report implementations, but always define it in terms of indicators which have varying definitions. Namely FxStreet defines the general concept here, but only in terms of DI+/-, TR and DM+/-, each of which has varying definitions. ForexReal defines ADX here, but uses a completely different concept of TR. Stockwiz has a good overview of all of the variables used (at least they define all of their variables) here. I will be defining the ADX all the way down to price action (what you need when you are coding this…) as best I can to the original definition by J Welles Wilder.

Lets start by defining the Directional Move indicators

Directional Move Indicator

         = the Positive Direction Movement Indicator

          = the Negative Direction Movement Indicator

We use two helper variables to track the delta extreme price changes from yesterday

If today’s range is entirely within yesterday’s range, or the range is the same, then there has been no directional movement.

If the range moved up, then there has been a positive directional move.

If the range moved down, then there has been a negative directional move.

Average Directional Move Indicator

          = the Average Positive Direction Movement Indicator for N periods

          = the Average Negative Direction Movement Indicator for N periods

The Welles Wilder Average is used here to stay true to form, but this average introduces a lot of latency, so I will also implement an exponential moving average.

Alternatively, the formula for the ADM with an exponential moving average is shown below.

True Range

          is the true range for a given period. It is a measure of volatility and takes into account any overnight gap in the period’s price.

Average True Range

          is the average true range for N periods.

The Welles Wilder Average is used here again to stay true to form, but for my C# implementation I will be using the exponential moving average.

Alternatively the ATR can be calculated with the exponential moving average as shown below.

Directional Index

          is the average positive directional movement indicator normalized by the average true range.

          is the average negative directional movement indicator normalized by the average true range.

The directional index calculation is very straight forward and shown below.

Directional Movement Index

          - the directional movement index is the difference of the directional indices over the sum directional indices.

Average Directional Movement Index

          is the average directional movement calculated over N periods.

Staying true to form again, the Welles Wilder Average is used below.

Again, you can alternatively use the exponential moving average as shown below.

C# implementation

So after the laborious research to determine exactly what the ADX is and should be (exponential vs Welles Wilder Average), here is the C# implementation of this indicator.

    //The Average Directional Indicator

    if(barHistory[timePeriod].Count > 1)


        //Directional Movement Indicator

        double deltaHigh = cBar.High – barHistory[timePeriod][barHistory[timePeriod].Count – 2].High;

        double deltaLow = barHistory[timePeriod][barHistory[timePeriod].Count – 2].Low – cBar.Low;

        if((deltaHigh < 0 && deltaLow < 0) || (deltaHigh == deltaLow))


            sig.DmPlus = 0;

            sig.DmMinus = 0;


if(deltaHigh > deltaLow)


            sig.DmPlus = deltaHigh;

            sig.DmMinus = 0;


if (deltaHigh < deltaLow)


            sig.DmPlus = 0;

            sig.DmMinus = deltaLow;



        //Average Directional Movement Indicator

        if(barHistory[timePeriod].Count <= 2)


            sig.AdmPlus = sig.DmPlus;

            sig.AdmMinus = sig.DmMinus;




            double a = (double) 2/(double) (calc.DMIPeriod + 1);

            sig.AdmPlus = sig.DmPlus * a +

                symbolHistory[timePeriod][symbolHistory[timePeriod].Count-2].AdmPlus * (1-a);

            sig.AdmMinus = sig.DmMinus * a +

                symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].AdmMinus * (1 – a);



        //True Range Indicator

        sig.TrueRange = Math.Max(Math.Abs(cBar.High – cBar.Low),


                                     Math.Abs(cBar.High – barHistory[timePeriod][barHistory[timePeriod].Count – 2].Close),

                                     Math.Abs(barHistory[timePeriod][barHistory[timePeriod].Count – 2].Close – cBar.Low)));


        //Average True Range Indicator

        if(barHistory[timePeriod].Count <= 2)


            sig.AverageTrueRange = sig.TrueRange;




            double a = (double)2 / (double)(calc.ADXPeriod + 1);

            sig.AverageTrueRange = sig.TrueRange * a +

                symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].AverageTrueRange * (1 – a);



        //Directional index Indicator

        if (sig.TrueRange != 0)


            sig.DiPlus = 100 * sig.AdmPlus/sig.TrueRange;

            sig.DiMinus = 100 * sig.AdmMinus/sig.TrueRange;




            sig.DiPlus = symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].DiPlus;

            sig.DiMinus = symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].DiMinus;



        //Directional Movement Index Indicator

        if ((sig.DiPlus + sig.DiMinus) != 0)

            sig.Dx = Math.Abs((sig.DiPlus – sig.DiMinus)) / (sig.DiPlus + sig.DiMinus) * 100;


            sig.Dx = symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].Dx;


        //Average Directional Movement Indicator

        if (barHistory[timePeriod].Count <= 2)


            sig.Adx = sig.Dx;




            double a = (double)2 / (double)(calc.DMIPeriod + 1);

            sig.Adx = sig.Dx * a +

                symbolHistory[timePeriod][symbolHistory[timePeriod].Count – 2].Adx * (1 – a);



Sample Data

This is a sample data set consisting of the NYSE A/D 1-min bar data (“Close”) from August 28, 2007 and the 14 period ADX in red.

27 Responses to “Average Directional Movement Index ADX Formula in C#”

  • Magdiel Herrera Franqui

    Hola, muy buena argumentacion respaldada por su respectiva implementacion en C#, solo me queda una duda,

    puede que en esta parte:

    Average Directional Movement Index

     is the average directional movement calculated over N periods.

    Staying true to form again, the Welles Wilder Average is used below.

    se te halla pasado y colocaste el calculo del indicador True Range en Lugar de DX ?


  • Wow, this is awesome, exactly what I was looking for. Thank you.

  • At 14:24, the ADX takes off and climbs to .35 while the price barely moves. Shouldn’t the ADX be falling during this period?

  • Ah, I see now that you are comparing it w/ the NYSE A/D 1-min bar data.

  • I have not been able to get this formulas equal to the Marketscope ADX values. For some reason the first adx calculated is equal, but from the 2nd Adx it is not getting equal. Did you check the values that your formulas calculate against any other tool? It seems that it’s something about the Average Directional Movement Indicator, because the ATR i’m getting through your formulas is accurate against de Marketscope ATR.

    Anyway your post has been so helpfull, but I want to get values that match exactly compared to the marketscope tool so I can verify more clearly my strategy.

  • The ADX formula is wrong. Replace the TR with DX and it works fine for me. Besides that, GREAT work :-)

  • Great work – I’ve been struggling to build this myself due to the complexity of the indicator. This helped me a lot and is the best explanation I’ve seen on the web. Thanks!

  • I did it this way (for DMI and -DMI only). I didn’t really get ur method (iam not that good at math lol)

    Used a array for the Low,High,Close data’s named Quotes. Any feedback is welcome =)

    private void CalcDMI()
    decimal TR = 0;
    decimal DMP = 0;
    decimal DMM = 0;

    for (int x = 0; x 0 && HD > LD) ? HD : 0;
    DMM = (LD > 0 && LD > HD) ? LD : 0;

    PDI = (DMP * 100) / TR;
    MDI = (DMM * 100) / TR;

  • hmm my reply is all f*cked up =( guess i cant post that many chars, nvermind than xD. srry!

  • Hi,
    ultimately which indicator do you recomend between the exponential MA and the Welles Wilder Avergae ? It seems to me that the 14 days Wilder average is like a 27 exponential moving average … but i’d like to get a confirmation on what to use …
    TKs a lot for all the formula though !

  • @cdm: yes, the length ‘n’ wilder average is just a length ’2n-1′ EMA.

  • Thanks a bunch! This formula was just what I needed for my school report.



  • Thanks for a detailed explanation on ADX, it was quite informative. Thanks again for the good work :)

  • Great Work, I implemented the same but in a much more gruesome brute force kinda manner. Knowing the formula helps and simplifies it a lot.
    Do you have the src for this available? if not then its fine.
    Thanks anyways

  • Hello everyone… how exactly should i get the Previous parameters (t-1)? e.g. when the formula says: PlusADM(t) = PlusADM(t-1)*(1-n/n)+(PlusDM(t))*(1/n) ; when the PlusADM(t-1) should come from? if i starts by the order from the begining, and i have only current and historical data for open, close, high, and low parameters so when High(t) means the high for November 5 then high(t-1) means high of november 4, it’s makes sense for PlusDM and MinusDM since they depend only on the parameters of open and close data and we can get them, but the PlusADM(t=Nov5 for example) defined with the previous PlusADM in it [PlusADM(Nov4)] so where it’s should starts from (and if you can get the historical PlusADM (for Nov4) then what’s the point in all that formula?), i don’t have the Nov4 PlusADM so assuming i start the application on Nov5 the current PlusADM cannot be defined right? what i miss here?

    thanx alot.

  • Can you send me all source code of you class please. It’ll be very helpful.

  • what values do you take for t-1 when considering calculations for the first day? Do caclulations really only start on Day 2 since that is only when one has previouos days’ data to use in calc?

  • Very rapidly this web site will be famous amid all blogging people, due to
    it’s nice articles

  • 多くの方法前記達成夢中時計それらテレビことがなくに従うことその剛体機会スケジュール。選択することができるかどうかyoud好む朝または午後のスロット。

  • Hey

    How would you modify the code if you were to apply this indicator on some other indicator( or time series)instead of bar data.


  • Apart from all these perks that include unswerving gratitude, tax exemption
    is amongst the greatest. However, buying gold along with other precious metals takes knowledge plus
    a plan. plus a one-time account setup fee (that also includes
    commission) that is usually about 10%. Having that
    in mind, this unstable marketplace is leading us to rethink how we can still be
    at the solid investment in the midst of the market’s challenges.
    It allows you in order to save the value of the hard earned money to the future.

  • For that demonstration singularly, it’s important to be superbly schooled
    on the codes of conducts that are invoked inside the Lubbock county jail in Lubbock Texas,
    survey your sayings, ally with you’re your own genre and be informed of the arranged regiments inside the Lubbock county jail in Lubbock Texas as they sketch the priority.
    External doors and windows are double glazed with.
    1 Construction Equipment Consumption Value

    My web site :: despacho de arquitectos df

  • Awesome website you have here but I was wanting to know
    if you knew of any message boards that cover the
    same topics discussed in this article? I’d really like
    to be a part of group where I can get responses
    from other experienced people that share the same interest.
    If you have any suggestions, please let me
    know. Thanks!

  • Aw, this was a very good post. Taking the time and actual
    effort to generate a superb article… but what can I say… I
    hesitate a whole lot and never seem to get anything done.

  • Your own info with suggestions vastly within reach if you ask me.

    Thank you especially much before hand. Your current acquaintance on this area is fantastic,
    I’m glad so as to this is stumble on. Thank you once more!

  • There are a lot of people that will not complete online surveys
    because they have to give the companies too much person information,
    and for good reason. A good online research will surely help you find companies that are honestly willing to pay you for your
    valuable feedback. The landlord initially carries the
    deposit as a liability.

Leave a Reply