Multi-Time Frame Data Series

Created at 13 Jun 2021, 17:21
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!
claye.weight's avatar

claye.weight

Joined 02.08.2019

Multi-Time Frame Data Series
13 Jun 2021, 17:21


Hi,

I'm trying to build a multi time frame Moving Average indicator. I have the first 3 EMAs set and working correctly - But I want to add the 4th EMA differently. 

I want to do a custom calculation for the 4th EMA data series. But I'm not getting the correct value for the MA for that timeframe. 

Am I going about this the right way? Any help would be appreciated.

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

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class TripleEMA : Indicator
    {
        [Parameter("EMA Fast", DefaultValue = 50)]
        public int Periods { get; set; }

        [Parameter("EMA Slow", DefaultValue = 100)]
        public int Periods1 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 200)]
        public int Periods2 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 100)]
        public int Periods3 { get; set; }

        [Parameter("TimeFrame", DefaultValue = "Daily")]
        public TimeFrame EMATimeframe1 { get; set; }

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

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

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

        private Bars series1;
        private Bars barsTF1;

        private ExponentialMovingAverage Ema1;
        private ExponentialMovingAverage Ema2;
        private ExponentialMovingAverage Ema3;
        private ExponentialMovingAverage Ema4;

        private IndicatorDataSeries _iDataSeries1;

        protected override void Initialize()
        {
            //series1 = MarketData.GetSeries(EMATimeframe1);
            series1 = MarketData.GetBars(EMATimeframe1);


            Ema1 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods);
            Ema2 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods1);
            Ema3 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods2);

            barsTF1 = MarketData.GetBars(TimeFrame.Hour4);
            _iDataSeries1 = CreateDataSeries();

            Ema4 = Indicators.ExponentialMovingAverage(_iDataSeries1, Periods3);

        }

        public override void Calculate(int index)
        {

            var index1 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index1 != -1)
            {
                EMA1[index] = Ema1.Result[index1];
            }

            var index2 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index2 != -1)
            {
                EMA2[index] = Ema2.Result[index2];
            }

            var index3 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index3 != -1)
            {
                EMA3[index] = Ema3.Result[index3];
            }

            var index4 = GetIndexByDate(barsTF1, Bars.OpenTimes[index]);
            if (index4 != -1)
            {
                _iDataSeries1[index] = barsTF1.ClosePrices[index] - (High(index1) + Low(index1)) / 2;
                //EMA4[index] = Ema4.Result[index];
                Print("EMA : " + Ema4.Result[index4]);
            }

        }


        private int GetIndexByDate(Bars series, DateTime time)
        {
            for (int i = series.Count - 1; i > 0; i--)
            {
                if (time == series.OpenTimes[i])
                    return i;
            }
            return -1;
        }

        private double High(int index)
        {
            double high = Bars.HighPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.HighPrices[i] > high)
                    high = Bars.HighPrices[i];
            }

            return high;
        }

        private double Low(int index)
        {
            double low = Bars.LowPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.LowPrices[i] < low)
                    low = Bars.LowPrices[i];
            }

            return low;
        }
    }
}

 


@claye.weight
Replies

amusleh
14 Jun 2021, 12:06

Hi,

You can't use index for another time frame bars data, in your code you used the current chart bars index to get another time frame bars data: barsTF1.ClosePrices[index]

Try this:

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

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class TripleEMA : Indicator
    {
        [Parameter("EMA Fast", DefaultValue = 50)]
        public int Periods { get; set; }

        [Parameter("EMA Slow", DefaultValue = 100)]
        public int Periods1 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 200)]
        public int Periods2 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 100)]
        public int Periods3 { get; set; }

        [Parameter("TimeFrame", DefaultValue = "Daily")]
        public TimeFrame EMATimeframe1 { get; set; }

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

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

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

        private Bars series1;
        private Bars barsTF1;

        private ExponentialMovingAverage Ema1;
        private ExponentialMovingAverage Ema2;
        private ExponentialMovingAverage Ema3;
        private ExponentialMovingAverage Ema4;

        private IndicatorDataSeries _iDataSeries1;

        protected override void Initialize()
        {
            //series1 = MarketData.GetSeries(EMATimeframe1);
            series1 = MarketData.GetBars(EMATimeframe1);

            Ema1 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods);
            Ema2 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods1);
            Ema3 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods2);

            barsTF1 = MarketData.GetBars(TimeFrame.Hour4);
            _iDataSeries1 = CreateDataSeries();

            Ema4 = Indicators.ExponentialMovingAverage(_iDataSeries1, Periods3);
        }

        public override void Calculate(int index)
        {
            var index1 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index1 != -1)
            {
                EMA1[index] = Ema1.Result[index1];
            }

            var index2 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index2 != -1)
            {
                EMA2[index] = Ema2.Result[index2];
            }

            var index3 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index3 != -1)
            {
                EMA3[index] = Ema3.Result[index3];
            }

            var barsTF1Index = barsTF1.OpenTimes.GetIndexByTime(Bars[index].OpenTime);

            _iDataSeries1[index] = barsTF1.ClosePrices[barsTF1Index] - (High(index) + Low(index)) / 2;

            Print("EMA : " + Ema4.Result[index]);
        }

        private int GetIndexByDate(Bars series, DateTime time)
        {
            for (int i = series.Count - 1; i > 0; i--)
            {
                if (time == series.OpenTimes[i])
                    return i;
            }
            return -1;
        }

        private double High(int index)
        {
            double high = Bars.HighPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.HighPrices[i] > high)
                    high = Bars.HighPrices[i];
            }

            return high;
        }

        private double Low(int index)
        {
            double low = Bars.LowPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.LowPrices[i] < low)
                    low = Bars.LowPrices[i];
            }

            return low;
        }
    }
}

 


@amusleh

claye.weight
14 Jun 2021, 12:58

RE:

Hey, Thanks for the reply and your help!

unfortunately i'm still having issues. On certain time frames the EMA returns NaN - and when it does return a value. It doesn't return the correct values.

I've taken out -         (High(index) + Low(index)) / 2;

and i'm just trying to print the correct values of a regular moving average. It seems to be something with the dataseries, but i dont know what. As soon as I take it out and write

 

Ema4 = Indicators.ExponentialMovingAverage(barsTF1.ClosePrices, Periods4);

It works. But i need to use a data series so I can perform calculations on the price....

not sure what to do.

 

 

amusleh said:

Hi,

You can't use index for another time frame bars data, in your code you used the current chart bars index to get another time frame bars data: barsTF1.ClosePrices[index]

Try this:

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

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class TripleEMA : Indicator
    {
        [Parameter("EMA Fast", DefaultValue = 50)]
        public int Periods { get; set; }

        [Parameter("EMA Slow", DefaultValue = 100)]
        public int Periods1 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 200)]
        public int Periods2 { get; set; }

        [Parameter("EMA Trend", DefaultValue = 100)]
        public int Periods3 { get; set; }

        [Parameter("TimeFrame", DefaultValue = "Daily")]
        public TimeFrame EMATimeframe1 { get; set; }

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

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

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

        private Bars series1;
        private Bars barsTF1;

        private ExponentialMovingAverage Ema1;
        private ExponentialMovingAverage Ema2;
        private ExponentialMovingAverage Ema3;
        private ExponentialMovingAverage Ema4;

        private IndicatorDataSeries _iDataSeries1;

        protected override void Initialize()
        {
            //series1 = MarketData.GetSeries(EMATimeframe1);
            series1 = MarketData.GetBars(EMATimeframe1);

            Ema1 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods);
            Ema2 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods1);
            Ema3 = Indicators.ExponentialMovingAverage(series1.ClosePrices, Periods2);

            barsTF1 = MarketData.GetBars(TimeFrame.Hour4);
            _iDataSeries1 = CreateDataSeries();

            Ema4 = Indicators.ExponentialMovingAverage(_iDataSeries1, Periods3);
        }

        public override void Calculate(int index)
        {
            var index1 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index1 != -1)
            {
                EMA1[index] = Ema1.Result[index1];
            }

            var index2 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index2 != -1)
            {
                EMA2[index] = Ema2.Result[index2];
            }

            var index3 = GetIndexByDate(series1, Bars.OpenTimes[index]);
            if (index3 != -1)
            {
                EMA3[index] = Ema3.Result[index3];
            }

            var barsTF1Index = barsTF1.OpenTimes.GetIndexByTime(Bars[index].OpenTime);

            _iDataSeries1[index] = barsTF1.ClosePrices[barsTF1Index] - (High(index) + Low(index)) / 2;

            Print("EMA : " + Ema4.Result[index]);
        }

        private int GetIndexByDate(Bars series, DateTime time)
        {
            for (int i = series.Count - 1; i > 0; i--)
            {
                if (time == series.OpenTimes[i])
                    return i;
            }
            return -1;
        }

        private double High(int index)
        {
            double high = Bars.HighPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.HighPrices[i] > high)
                    high = Bars.HighPrices[i];
            }

            return high;
        }

        private double Low(int index)
        {
            double low = Bars.LowPrices[index - 10];

            for (int i = index - 10 + 1; i <= index; i++)
            {
                if (Bars.LowPrices[i] < low)
                    low = Bars.LowPrices[i];
            }

            return low;
        }
    }
}

 

 


@claye.weight

amusleh
14 Jun 2021, 13:06

Hi,

I don't know what you are trying to do, but the correct way to work with another time frame data is what I just posted.

Do you want to calculate the exponential moving average value of another time frame? 


@amusleh

claye.weight
14 Jun 2021, 13:23

RE:

My goal is to do an calculation for another time frames', close, open high, low. Then I want to turn that result into an EMA

 

Yeah, I want to calculate the EMA for another time frame. But it won't just be the close, so I can't use:

Ema4 = Indicators.ExponentialMovingAverage(barsTF1.ClosePrices, Periods4);

Which is why trying to create a data series. (if that's the right thing to do?)

 

for what you posted; I set EMA3 to the same time frame and period as EMA4 (the one with the data series) and made them both just the close (no high - low) and the results are different?

Am I missing something?

 

I appreciate all the help

amusleh said:

Hi,

I don't know what you are trying to do, but the correct way to work with another time frame data is what I just posted.

Do you want to calculate the exponential moving average value of another time frame? 

 


@claye.weight

amusleh
14 Jun 2021, 14:33

Hi,

I created a MTF exponential moving average for you:

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MTFEMA : Indicator
    {
        private Bars _baseTimeFrameBars;

        private DataSeries _baseSeries;

        private double _multiplier;

        private bool _isInitialized;

        private int _baseTimeFramePreviousIndex;

        private double _baseTimeFramePreviousEma;

        [Parameter(DefaultValue = 14)]
        public double Periods { get; set; }

        [Parameter("Source", DefaultValue = DataSource.Close)]
        public DataSource Source { get; set; }

        [Parameter("Base Time Frame")]
        public TimeFrame BaseTimeFrame { get; set; }

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

        protected override void Initialize()
        {
            _baseTimeFrameBars = MarketData.GetBars(BaseTimeFrame);

            _baseSeries = GetSeries(Source);

            _multiplier = 2 / (Periods + 1);
        }

        public override void Calculate(int index)
        {
            var baseTimeFrameIndex = _baseTimeFrameBars.OpenTimes.GetIndexByTime(Bars[index].OpenTime);

            if (baseTimeFrameIndex < Periods) return;

            if (!_isInitialized)
            {
                Result[index] = GetAverage(baseTimeFrameIndex);

                _isInitialized = true;
            }
            else
            {
                Result[index] = (_baseTimeFrameBars[baseTimeFrameIndex].Close - _baseTimeFramePreviousEma) * _multiplier + _baseTimeFramePreviousEma;
            }

            if (baseTimeFrameIndex != _baseTimeFramePreviousIndex)
            {
                _baseTimeFramePreviousIndex = baseTimeFrameIndex;
                _baseTimeFramePreviousEma = Result[index];
            }
        }

        private double GetAverage(int index)
        {
            var lastIndex = index - Periods;

            double sum = 0;

            for (var i = index; i > lastIndex; i--)
            {
                sum += _baseSeries[i];
            }

            return sum / Periods;
        }

        private DataSeries GetSeries(DataSource dataSource)
        {
            switch (dataSource)
            {
                case DataSource.Open:
                    return _baseTimeFrameBars.OpenPrices;

                case DataSource.High:
                    return _baseTimeFrameBars.HighPrices;

                case DataSource.Low:
                    return _baseTimeFrameBars.LowPrices;

                case DataSource.Close:
                    return _baseTimeFrameBars.ClosePrices;

                case DataSource.Volume:
                    return _baseTimeFrameBars.TickVolumes;

                case DataSource.Typical:
                    return _baseTimeFrameBars.TypicalPrices;

                case DataSource.Weighted:
                    return _baseTimeFrameBars.WeightedPrices;

                case DataSource.Median:
                    return _baseTimeFrameBars.MedianPrices;

                default:
                    throw new ArgumentOutOfRangeException("dataSource");
            }
        }
    }

    public enum DataSource
    {
        Open,
        High,
        Low,
        Close,
        Volume,
        Typical,
        Median,
        Weighted
    }
}

I checked it gives same result like built-in EMA, only the last bar value might be different because it waits for bar to finish then it updates the value as it should consider other time frames data, hopefully this will help you.


@amusleh

claye.weight
15 Jun 2021, 02:49

RE:

Thank you! This helps heaps!!

it works great!

 

amusleh said:

Hi,

I created a MTF exponential moving average for you:

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MTFEMA : Indicator
    {
        private Bars _baseTimeFrameBars;

        private DataSeries _baseSeries;

        private double _multiplier;

        private bool _isInitialized;

        private int _baseTimeFramePreviousIndex;

        private double _baseTimeFramePreviousEma;

        [Parameter(DefaultValue = 14)]
        public double Periods { get; set; }

        [Parameter("Source", DefaultValue = DataSource.Close)]
        public DataSource Source { get; set; }

        [Parameter("Base Time Frame")]
        public TimeFrame BaseTimeFrame { get; set; }

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

        protected override void Initialize()
        {
            _baseTimeFrameBars = MarketData.GetBars(BaseTimeFrame);

            _baseSeries = GetSeries(Source);

            _multiplier = 2 / (Periods + 1);
        }

        public override void Calculate(int index)
        {
            var baseTimeFrameIndex = _baseTimeFrameBars.OpenTimes.GetIndexByTime(Bars[index].OpenTime);

            if (baseTimeFrameIndex < Periods) return;

            if (!_isInitialized)
            {
                Result[index] = GetAverage(baseTimeFrameIndex);

                _isInitialized = true;
            }
            else
            {
                Result[index] = (_baseTimeFrameBars[baseTimeFrameIndex].Close - _baseTimeFramePreviousEma) * _multiplier + _baseTimeFramePreviousEma;
            }

            if (baseTimeFrameIndex != _baseTimeFramePreviousIndex)
            {
                _baseTimeFramePreviousIndex = baseTimeFrameIndex;
                _baseTimeFramePreviousEma = Result[index];
            }
        }

        private double GetAverage(int index)
        {
            var lastIndex = index - Periods;

            double sum = 0;

            for (var i = index; i > lastIndex; i--)
            {
                sum += _baseSeries[i];
            }

            return sum / Periods;
        }

        private DataSeries GetSeries(DataSource dataSource)
        {
            switch (dataSource)
            {
                case DataSource.Open:
                    return _baseTimeFrameBars.OpenPrices;

                case DataSource.High:
                    return _baseTimeFrameBars.HighPrices;

                case DataSource.Low:
                    return _baseTimeFrameBars.LowPrices;

                case DataSource.Close:
                    return _baseTimeFrameBars.ClosePrices;

                case DataSource.Volume:
                    return _baseTimeFrameBars.TickVolumes;

                case DataSource.Typical:
                    return _baseTimeFrameBars.TypicalPrices;

                case DataSource.Weighted:
                    return _baseTimeFrameBars.WeightedPrices;

                case DataSource.Median:
                    return _baseTimeFrameBars.MedianPrices;

                default:
                    throw new ArgumentOutOfRangeException("dataSource");
            }
        }
    }

    public enum DataSource
    {
        Open,
        High,
        Low,
        Close,
        Volume,
        Typical,
        Median,
        Weighted
    }
}

I checked it gives same result like built-in EMA, only the last bar value might be different because it waits for bar to finish then it updates the value as it should consider other time frames data, hopefully this will help you.

 


@claye.weight