Help with a modified Hull MA

Created at 07 Feb 2022, 07:50
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
sirinath's avatar

sirinath

Joined 25.11.2021

Help with a modified Hull MA
07 Feb 2022, 07:50


I did 2 implementations of the a modified HMA and some other MAs.

using System;

using cAlgo.API;

namespace cAlgo {
  [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
  public class Ex2MovingAverage : Indicator {
    [Parameter("Source")]
    public DataSeries Source { get; set; }

    [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
    public int Periods { get; set; }

    [Output("MA1", LineColor = "Magenta", LineStyle = LineStyle.DotsVeryRare)]
    public IndicatorDataSeries MA1 { get; set; }

    [Output("MA2", LineColor = "Cyan", LineStyle = LineStyle.DotsVeryRare)]
    public IndicatorDataSeries MA2 { get; set; }

    [Output("MA3", LineColor = "Yellow", LineStyle = LineStyle.DotsVeryRare)]
    public IndicatorDataSeries MA3 { get; set; }

    [Output("DEMA", LineColor = "Pink")]
    public IndicatorDataSeries DEMA { get; set; }

    [Output("TEMA", LineColor = "Turquoise")]
    public IndicatorDataSeries TEMA { get; set; }

    [Output("MA4", LineColor = "Orange", LineStyle = LineStyle.DotsVeryRare)]
    public IndicatorDataSeries MA4 { get; set; }

    [Output("MA5", LineColor = "Brown", LineStyle = LineStyle.DotsVeryRare)]
    public IndicatorDataSeries MA5 { get; set; }

    [Output("HMA", LineColor = "Red")]
    public IndicatorDataSeries HMA { get; set; }

    private double exp;
    private double expH;
    private double expSR;

    protected override void Initialize() {
      exp = 2.0 / (Periods + 1.0);
      expH = 2.0 / (Periods / 2.0 + 1.0);
      expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
    }

    public override void Calculate(int index) {
      double p = Source[index];

      if (index <= 0) {
        MA1[0] = p;
        MA2[0] = p;
        MA3[0] = p;
        DEMA[0] = p;
        TEMA[0] = p;

        MA4[0] = p;
        MA5[0] = p;
        HMA[0] = p;

        return;
      }

      int pi = index - 1;

      double pma1 = MA1[pi];
      double d1 = p - pma1;
      double ma1 = pma1 + exp * d1;
      MA1[index] = ma1;

      double pma2 = MA2[pi];
      double d2 = ma1 - pma2;
      double ma2 = pma2 + exp * d2;
      MA2[index] = ma2;

      double pma3 = MA3[pi];
      double d3 = ma2 - pma3;
      double ma3 = pma3 + exp * d3;
      MA3[index] = ma3;

      double dema = 2 * ma1 - ma2;
      DEMA[index] = dema;

      double tema = 3 * (ma1 - ma2) + ma3;
      TEMA[index] = tema;

      double pma4 = MA4[pi];
      double d4 = p - pma4;
      double ma4 = pma4 + expH * d4;
      MA4[index] = ma4;

      double ma5 = 2 * ma4 - ma1;
      MA5[index] = ma5;

      double phma = HMA[pi];
      double dh = ma5 - phma;
      double hma = ma5 + expSR * dh;
      HMA[index] = hma;
    }
  }
}

and

using System;

using cAlgo.API;

namespace cAlgo {
  [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
  public class EHMAIndicator : Indicator {
    [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
    public int Periods { get; set; }

    [Output("Avg. Price")]
    public IndicatorDataSeries AP { get; set; }

    [Output("EMA")]
    public IndicatorDataSeries EMA { get; set; }

    [Output("EMAH")]
    public IndicatorDataSeries EMAH { get; set; }

    [Output("EMAI")]
    public IndicatorDataSeries EMAI { get; set; }

    [Output("EHMA")]
    public IndicatorDataSeries EHMA { get; set; }

    public IndicatorDataSeries EV { get; private set; }
    public IndicatorDataSeries EVH { get; private set; }
    public IndicatorDataSeries EVI { get; private set; }
    public IndicatorDataSeries EHV { get; private set; }

    [Output("EHUB")]
    public IndicatorDataSeries EHUB { get; set; }

    [Output("EHLB")]
    public IndicatorDataSeries EHLB { get; set; }

    private double exp;
    private double expH;
    private double expSR;

    protected override void Initialize() {
      exp = 2.0 / (Periods + 1.0);
      expH = 2.0 / (Periods / 2.0 + 1.0);
      expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);

      EV = CreateDataSeries();
      EVH = CreateDataSeries();
      EVI = CreateDataSeries();
      EHV = CreateDataSeries();
    }

    public override void Calculate(int index) {
      double o = Bars.OpenPrices[index];
      double h = Bars.HighPrices[index];
      double l = Bars.LowPrices[index];
      double c = Bars.ClosePrices[index];
      double p = (o + 2 * h + 2 * l + 3 * c) / 8;

      AP[index] = p;

      if (index <= 0) {
        EMA[0] = p;
        EMAH[0] = p;
        EMAI[0] = p;
        EHMA[0] = p;

        EV[0] = 0;
        EVH[0] = 0;
        EVI[0] = 0;
        EHV[0] = 0;

        EHUB[0] = p;
        EHLB[0] = p;

        return;
      }

      int pi = index - 1;

      double pema = EMA[pi];
      double e = p - pema;
      double ema = pema + exp * e;
      EMA[index] = ema;

      double pemah = EMAH[pi];
      double eh = p - pemah;
      double emah = pemah + expH * eh;
      EMAH[index] = emah;

      double emai = 2 * emah - ema;
      EMAI[index] = emai;

      double pehma = EHMA[pi];
      double eeh = emai - pehma;
      double ehma = pehma + expSR * eeh;
      EHMA[index] = ehma;

      double v = Math.Pow(p - ehma, 2);

      double pev = EV[pi];
      double d = v - pev;
      double ev = pev + exp * d;
      EV[index] = ev;

      double pevh = EVH[pi];
      double dh = v - pevh;
      double evh = pevh + expH * dh;
      EVH[index] = evh;

      double evi = 2 * evh - ev;
      EVI[index] = evi;

      double pehv = EHV[pi];
      double deh = evi - pehv;
      double ehv = pehv + expSR * deh;
      EHV[index] = ehv;

      EHUB[index] = ehma + 2 * Math.Sqrt(ehv);
      EHLB[index] = ehma - 2 * Math.Sqrt(ehv);
    }
  }
}

The HMA part is the same but they give different results. I am trying to spot my mistake but I cannot find it.

At lower MA lengths the 1st implementation gives erratic results.

This is the same settings for the other implimentation

Also I am trying to do a modified smoothed out BB in the 2nd implementation but this is not coming out correctly.

I would really appreciate if some one can help in figuring this out. 

When settings are the same:

This also has a 20 period HMA for comparison. In previous chart Modified HMA is above and in the latter it is below. The price series used slightly differs through.


@sirinath
Replies

sirinath
07 Feb 2022, 12:26

Can this difference be due to a bug in cTrader?


@sirinath

amusleh
08 Feb 2022, 08:15

RE:

sirinath said:

Can this difference be due to a bug in cTrader?

Hi,

I checked your code but it's not clear what's the exact issue you are looking to solve?

Please make clear what's the issue and then I will be able to help you.

If there is something wrong in your code first remove the other things and just post the code that has issue, that way it would be much easier for us to help you.


@amusleh

sirinath
08 Feb 2022, 18:20 ( Updated at: 21 Dec 2023, 09:22 )

RE: RE:

This is a modified version of Hull MA. The code is the same but the results are different.

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MATest1 : Indicator
    {
        [Parameter("Source")]
        public DataSeries Source { get; set; }

        [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
        public int Periods { get; set; }

        [Output("MA1", LineColor = "Magenta", LineStyle = LineStyle.DotsVeryRare)]
        public IndicatorDataSeries MA1 { get; set; }

        [Output("MA4", LineColor = "Orange", LineStyle = LineStyle.DotsVeryRare)]
        public IndicatorDataSeries MA4 { get; set; }

        [Output("MA5", LineColor = "Brown", LineStyle = LineStyle.DotsVeryRare)]
        public IndicatorDataSeries MA5 { get; set; }

        [Output("HMA", LineColor = "Red")]
        public IndicatorDataSeries HMA { get; set; }

        private double exp;
        private double expH;
        private double expSR;

        protected override void Initialize()
        {
            exp = 2.0 / (Periods + 1.0);
            expH = 2.0 / (Periods / 2.0 + 1.0);
            expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
        }

        public override void Calculate(int index)
        {
            double p = Source[index];

            if (index <= 0)
            {
                MA1[0] = p;

                MA4[0] = p;
                MA5[0] = p;
                HMA[0] = p;

                return;
            }

            int pi = index - 1;

            double pma1 = MA1[pi];
            double d1 = p - pma1;
            double ma1 = pma1 + exp * d1;
            MA1[index] = ma1;

            double pma4 = MA4[pi];
            double d4 = p - pma4;
            double ma4 = pma4 + expH * d4;
            MA4[index] = ma4;

            double ma5 = 2 * ma4 - ma1;
            MA5[index] = ma5;

            double phma = HMA[pi];
            double dh = ma5 - phma;
            double hma = ma5 + expSR * dh;
            HMA[index] = hma;
        }
    }
}
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MATest2 : Indicator
    {
        [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
        public int Periods { get; set; }

        [Output("Avg. Price")]
        public IndicatorDataSeries AP { get; set; }

        [Output("EMA")]
        public IndicatorDataSeries EMA { get; set; }

        [Output("EMAH")]
        public IndicatorDataSeries EMAH { get; set; }

        [Output("EMAI")]
        public IndicatorDataSeries EMAI { get; set; }

        [Output("EHMA", LineColor = "Red")]
        public IndicatorDataSeries EHMA { get; set; }

        private double exp;
        private double expH;
        private double expSR;

        protected override void Initialize()
        {
            exp = 2.0 / (Periods + 1.0);
            expH = 2.0 / (Periods / 2.0 + 1.0);
            expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
        }

        public override void Calculate(int index)
        {
            double p = Bars.ClosePrices[index];

            AP[index] = p;

            if (index <= 0)
            {
                EMA[0] = p;
                EMAH[0] = p;
                EMAI[0] = p;
                EHMA[0] = p;

                return;
            }

            int pi = index - 1;

            double pema = EMA[pi];
            double e = p - pema;
            double ema = pema + exp * e;
            EMA[index] = ema;

            double pemah = EMAH[pi];
            double eh = p - pemah;
            double emah = pemah + expH * eh;
            EMAH[index] = emah;

            double emai = 2 * emah - ema;
            EMAI[index] = emai;

            double pehma = EHMA[pi];
            double eeh = emai - pehma;
            double ehma = pehma + expSR * eeh;
            EHMA[index] = ehma;
        }
    }
}


@sirinath

amusleh
09 Feb 2022, 08:47

Hi,

Your calculation doesn't match, if you copy the first one calculation part to the second then the results will match.

This one EHMA output matches with first one HMA output:

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MATest2 : Indicator
    {
        [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
        public int Periods { get; set; }

        [Output("Avg. Price")]
        public IndicatorDataSeries AP { get; set; }

        [Output("EMA")]
        public IndicatorDataSeries EMA { get; set; }

        [Output("EMAH")]
        public IndicatorDataSeries EMAH { get; set; }

        [Output("EMAI")]
        public IndicatorDataSeries EMAI { get; set; }

        [Output("EHMA", LineColor = "Red")]
        public IndicatorDataSeries EHMA { get; set; }

        private double exp;
        private double expH;
        private double expSR;

        protected override void Initialize()
        {
            exp = 2.0 / (Periods + 1.0);
            expH = 2.0 / (Periods / 2.0 + 1.0);
            expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
        }

        public override void Calculate(int index)
        {
            double p = Bars.ClosePrices[index];

            AP[index] = p;

            if (index <= 0)
            {
                EMA[0] = p;
                EMAH[0] = p;
                EMAI[0] = p;
                EHMA[0] = p;

                return;
            }

            int pi = index - 1;

            double pma1 = EMA[pi];
            double d1 = p - pma1;
            double ma1 = pma1 + exp * d1;
            EMA[index] = ma1;

            double pma4 = EMAH[pi];
            double d4 = p - pma4;
            double ma4 = pma4 + expH * d4;
            EMAH[index] = ma4;

            double ma5 = 2 * ma4 - ma1;
            EMAI[index] = ma5;

            double phma = EHMA[pi];
            double dh = ma5 - phma;
            double hma = ma5 + expSR * dh;
            EHMA[index] = hma;
        }
    }
}

 


@amusleh

sirinath
09 Feb 2022, 15:25 ( Updated at: 09 Feb 2022, 15:29 )

RE:

The logic is the same. Only variable names, use of line colour, and DataSeries change between the files. Then the results change.

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MATest2 : Indicator
    {
        // [Parameter("Source")]
        // public DataSeries Source { get; set; }

        [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
        public int Periods { get; set; }

        // [Parameter("Periods", DefaultValue = 14, MinValue = 1)]
        // public int Periods { get; set; }

        [Output("Avg. Price")]
        public IndicatorDataSeries AP { get; set; }

        [Output("EMA")]
        public IndicatorDataSeries EMA { get; set; }

        // [Output("MA1", LineColor = "Magenta", LineStyle = LineStyle.DotsVeryRare)]
        // public IndicatorDataSeries MA1 { get; set; }

        [Output("EMAH")]
        public IndicatorDataSeries EMAH { get; set; }

        // [Output("MA4", LineColor = "Orange", LineStyle = LineStyle.DotsVeryRare)]
        // public IndicatorDataSeries MA4 { get; set; }

        [Output("EMAI")]
        public IndicatorDataSeries EMAI { get; set; }

        // [Output("MA5", LineColor = "Brown", LineStyle = LineStyle.DotsVeryRare)]
        // public IndicatorDataSeries MA5 { get; set; }

        [Output("EHMA", LineColor = "Red")]
        public IndicatorDataSeries EHMA { get; set; }

        // [Output("HMA", LineColor = "Red")]
        // public IndicatorDataSeries HMA { get; set; }

        private double exp;
        // private double exp;
        private double expH;
        // private double expH;
        private double expSR;
        // private double expSR;

        protected override void Initialize()
        {
            exp = 2.0 / (Periods + 1.0);
            // exp = 2.0 / (Periods + 1.0);
            expH = 2.0 / (Periods / 2.0 + 1.0);
            // expH = 2.0 / (Periods / 2.0 + 1.0);
            expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
            // expSR = 2.0 / (Math.Sqrt(Periods) + 1.0);
        }

        public override void Calculate(int index)
        {
            double p = Bars.ClosePrices[index];
            // double p = Source[index];
            AP[index] = p;
            // NA - not there in the other file

            if (index <= 0)
            {
                EMA[0] = p;
                // MA1[0] = p;
                EMAH[0] = p;
                // MA4[0] = p;
                EMAI[0] = p;
                // MA5[0] = p;
                EHMA[0] = p;
                // HMA[0] = p;

                return;
            }

            int pi = index - 1;
            // int pi = index - 1;

            double pema = EMA[pi];
            // double pma1 = MA1[pi];
            double e = p - pema;
            // double d1 = p - pma1;
            double ema = pema + exp * e;
            // double ma1 = pma1 + exp * d1;
            EMA[index] = ema;
            // MA1[index] = ma1;

            double pemah = EMAH[pi];
            // double pma4 = MA4[pi];
            double eh = p - pemah;
            // double d4 = p - pma4;
            double emah = pemah + expH * eh;
            // double ma4 = pma4 + expH * d4;
            EMAH[index] = emah;
            // MA4[index] = ma4;

            double emai = 2 * emah - ema;
            // double ma5 = 2 * ma4 - ma1;
            EMAI[index] = emai;
            // MA5[index] = ma5;

            double pehma = EHMA[pi];
            // double phma = HMA[pi];
            double eeh = emai - pehma;
            // double dh = ma5 - phma;
            double ehma = pehma + expSR * eeh;
            // double hma = ma5 + expSR * dh;
            EHMA[index] = ehma;
            // HMA[index] = hma;
        }
    }
}

 


@sirinath

amusleh
10 Feb 2022, 08:44

Hi,

The calculation code differs, at line (first indicator):

double hma = ma5 + expSR * dh;

And for second indicator the same line is:

double ehma = pehma + expSR * eeh;

The "ma5" variable is different from "pechma".

You should change the calculation code of second indicator to:

            double pema = EMA[pi];
            double e = p - pema;
            double ema = pema + exp * e;
            EMA[index] = ema;

            double pemah = EMAH[pi];
            double eh = p - pemah;
            double emah = pemah + expH * eh;
            EMAH[index] = emah;

            double emai = 2 * emah - ema;
            EMAI[index] = emai;

            double pehma = EHMA[pi];
            double eeh = emai - pehma;
            double ehma = emai + expSR * eeh;
            EHMA[index] = ehma;

 


@amusleh

sirinath
14 Feb 2022, 19:10

RE:

Many thanks for your help in this. I managed to get it to work as intended.

But I ran into another problem with another indicator but based on edits to the initial 2 files I shared above. I will post this as another question.

 


@sirinath