How to create custom IndicatorDataSeries in different time frame?

Created at 08 Oct 2019, 09:09
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!
FI

firemyst

Joined 26.03.2019

How to create custom IndicatorDataSeries in different time frame?
08 Oct 2019, 09:09


HI all:

I have an indicator with a few custom IndicatorDataSeries objects, which I feed into EMA's.

What I am having problems with is being able to create my own custom IndicatorDataSeries object with a timeframe that's not the chart's current time frame.

How can I do this?

Example code:

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

namespace cAlgo.Indicators
{
    [Levels(-25, 0, 25)]
    [Indicator("True Strength Index (Dave Loz)", AccessRights = AccessRights.None)]
    public class TSI : Indicator
    {
        [Parameter()]
        public TimeFrame SourceTimeFrame { get; set; }

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

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

        [Parameter("SignalPeriod", DefaultValue = 8, MinValue = 1)]
        public int SignalPeriod { get; set; }
        
        [Parameter("MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MaType { get; set; }

        private MarketSeries _marketSeries;
        private IndicatorDataSeries _dataSeries;
        private IndicatorDataSeries _dataSeriesAbs;
        private MovingAverage _signal;

        protected override void Initialize()
        {
            if (SourceTimeFrame != null)
                _marketSeries = MarketData.GetSeries(Symbol.Name, SourceTimeFrame);
            else
                _marketSeries = MarketData.GetSeries(Symbol.Name, MarketSeries.TimeFrame);

	//I want to "attach" these dataseries to the _marketSeries timeframe, not the current chart's time frame.
    //I populate these data series' with custom values later in the code.
            _dataSeries = CreateDataSeries();
            _dataSeriesAbs = CreateDataSeries();
            
	//I want this MovingAverage to use the IndicatorDataSeries I created, _dataSeries,
	//based on the _marketSeries TimeFrame, which will not always be the chart's time frame.
	//How can I do this?
            _longDividend = Indicators.MovingAverage(_dataSeries, LongPeriod, MaType);
        }
        
    }
}

 


@firemyst
Replies

PanagiotisCharalampous
08 Oct 2019, 09:22

Hi FireMyst,

I am not sure what do you mean with

"I want to "attach" these dataseries to the _marketSeries timeframe, not the current chart's time frame"

DataSeries is not attached to any timeframe, it is just a series of doubles. Can you please elaborate?

Best Regards,

Panagiotis


@PanagiotisCharalampous

firemyst
09 Oct 2019, 04:38

RE:

Panagiotis Charalampous said:

Hi FireMyst,

I am not sure what do you mean with

"I want to "attach" these dataseries to the _marketSeries timeframe, not the current chart's time frame"

DataSeries is not attached to any timeframe, it is just a series of doubles. Can you please elaborate?

Best Regards,

Panagiotis

I'll try to elaborate... this indicator is going to be displayed on a 2-Hour chart.

However, I also want the indicator, based on 1 hour chart data, to also be displayed on the 2 hour chart.

So essentially I'll have two instances of this indicator on the same chart -- one showing 1-hour time frame; the other based on 2-hour time frame data.

However, there appears to be no way to attached "_dataSeries" or "_dataSeriesAbs" to a timeframe other than the charts time frame. The reason for this appears to be because when I "CreateDataSeries()", it only creates a dataseries in the current chart's timeframe that the indicator is attached to, with no way to specify that I want a data series created with a timeframe that isn't the same timeframe as the one the chart is on.

Does that help?


@firemyst

PanagiotisCharalampous
09 Oct 2019, 08:34

Hi FireMyst,

As explained above, data series are not "attached" to any timeframe. It is just a series of doubles. If you want to feed them with data from a lower timeframe, you can do it. There is no time property on anything else that relates an item of the data series to a timeframe.

Best Regards,

Panagiotis


@PanagiotisCharalampous

firemyst
09 Oct 2019, 09:57

RE:

Panagiotis Charalampous said:

Hi FireMyst,

As explained above, data series are not "attached" to any timeframe. It is just a series of doubles. If you want to feed them with data from a lower timeframe, you can do it. There is no time property on anything else that relates an item of the data series to a timeframe.

Best Regards,

Panagiotis

So with the example code below, what am I doing wrong?

 

Compile and run the code. Add the indicator on your current chart twice: the first instance, add it with the chart's timeframe. Works fine. Add it a second time with a different timeframe, and nothing shows. All the values seem to return NaN, which I think is because it's not getting the values from the secondary timeframe parameter when it differs from the chart's time frame.

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

namespace cAlgo.Indicators
{
    [Levels(-25, 0, 25)]
    [Indicator("True Strength Index (Dave Loz)", AccessRights = AccessRights.None)]
    public class TSI : Indicator
    {
        [Parameter()]
        public TimeFrame SourceTimeFrame { get; set; }

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

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

        [Parameter("SignalPeriod", DefaultValue = 8, MinValue = 1)]
        public int SignalPeriod { get; set; }
        
        [Parameter("MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MaType { get; set; }

        [Output("Tsi", LineColor = "Green", LineStyle = LineStyle.Solid, PlotType =PlotType.Line, Thickness = 2)]
        public IndicatorDataSeries Tsi { get; set; }
        [Output("Signal", LineColor = "Red", LineStyle = LineStyle.Solid, PlotType = PlotType.Line, Thickness = 2)]
        public IndicatorDataSeries Signal { get; set; }
        [Output("Diff", LineColor = "Cyan", LineStyle = LineStyle.Solid, PlotType = PlotType.Histogram, Thickness = 3)]
        public IndicatorDataSeries Diff { get; set; }

        private MovingAverage _divisor;
        private MovingAverage _longDivisor;
        private MovingAverage _dividend;
        private MovingAverage _longDividend;

        private MarketSeries _marketSeries;
        private IndicatorDataSeries _dataSeries;
        private IndicatorDataSeries _dataSeriesAbs;
        private MovingAverage _signal;
        private IndicatorDataSeries _tsiSeries;

        protected override void Initialize()
        {
            if (SourceTimeFrame != null)
                _marketSeries = MarketData.GetSeries(Symbol.Name, SourceTimeFrame);
            else
                _marketSeries = MarketData.GetSeries(Symbol.Name, MarketSeries.TimeFrame);

            _dataSeries = CreateDataSeries();
            _dataSeriesAbs = CreateDataSeries();
            
            _longDividend = Indicators.MovingAverage(_dataSeries, LongPeriod, MaType);
            _dividend = Indicators.MovingAverage(_longDividend.Result, ShortPeriod, MaType);

            _longDivisor = Indicators.MovingAverage(_dataSeriesAbs, LongPeriod, MaType);
            _divisor = Indicators.MovingAverage(_longDivisor.Result, ShortPeriod, MaType);

            _tsiSeries = CreateDataSeries();
            _signal = Indicators.MovingAverage(_tsiSeries, SignalPeriod, MaType);
           
        }
        
        public override void Calculate(int index)
        {
            if (index < 1)
            {
                Tsi[index] = 0;
                return;
            }

            int altIndex = index;
            if (MarketSeries.TimeFrame != _marketSeries.TimeFrame)
            {
                altIndex = _marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index]);
            }

            _dataSeries[index] = _marketSeries.Close[altIndex] - _marketSeries.Close[altIndex - 1];
            _dataSeriesAbs[index] = Math.Abs(_marketSeries.Close[altIndex] - _marketSeries.Close[altIndex - 1]);

            double tsiDivisor = _divisor.Result[altIndex];
            double tsiDividend = _dividend.Result[altIndex];

            if (Math.Abs(tsiDivisor) < double.Epsilon)
                _tsiSeries[index] = 0;
            else
                _tsiSeries[index] = 100.0 * tsiDividend / tsiDivisor;

            Tsi[index] = _tsiSeries[index];
            Signal[index] = _signal.Result[index];
            Diff[index] = _tsiSeries[index] - Signal[index];
        }
    }
}

 


@firemyst

PanagiotisCharalampous
09 Oct 2019, 12:31

Hi FireMyst,

First of all if you want to see any results at all you need to change

            double tsiDivisor = _divisor.Result[altIndex];
            double tsiDividend = _dividend.Result[altIndex];

to

            double tsiDivisor = _divisor.Result[index];
            double tsiDividend = _dividend.Result[index];

But then there is a major logical issue with your indicator. You are trying to do calculations for indicators using a different timeframe on the main indicator. This will result to wrong results due to different resolution of the available data.  

Example

            if (MarketSeries.TimeFrame != _marketSeries.TimeFrame)
            {
                altIndex = _marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index]);
            }

If MarketSeries.TimeFrame is h1 and _marketSeries.TimeFrame is m30 then when executing the following

_dataSeries[index] = _marketSeries.Close[altIndex] - _marketSeries.Close[altIndex - 1];

The last x values (let's say 14) of _dataSeries will contain sample data from the last 28 values of the _marketSeries.Close, since for every change of the index for h1, the index for m30 increases by 2 (print altIndex to understand what i man). Therefore this average

_longDividend = Indicators.MovingAverage(_dataSeries, 14, MaType);

will not be an average of the last 14 periods but the last 28 since it will average the last 14 sample values collected in _dataSeries.

Since you are using moving averages which are simple to code, it would be easier to do the calculations yourself rather than relying on cTrader indicators. Just loop through the custom timeframe data and calculate the MAs

Best regards,

Panagiotis


@PanagiotisCharalampous

firemyst
09 Oct 2019, 17:40

Thanks for the feedback.

Your particular point:

_longDividend = Indicators.MovingAverage(_dataSeries, 14, MaType);

is where I was getting stuck the most, wondering how I can change the time frames.

Your other points about the indexes are well taken, and was me putzing around in the code trying to get something to debug and work with.

Anyway, I appreciate your time and feedback. I'll see what I can come up with since an EMA shouldn't be too hard to program up. :-)


@firemyst