Issue with Indicators that require the whole Bars object as argument

Created at 19 May 2021, 11:57
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!
CA

cAlgoBuddy

Joined 12.11.2020

Issue with Indicators that require the whole Bars object as argument
19 May 2021, 11:57


Hello,

I have a custom indicator based on the ADX indicator. The ADX indicator requires the whole Bars object to be provided in it's overloaded constructor. Unfortunately, this makes it impossible to reference it correctly from a bot for the purposes of a MTF setup, as Bars is not allowed to be declared as a Parameter for an indicator.

The workaround proposed was to directly set the Bars object after creating the indicator. This works to an extend, because if you reference the same custom indicator twice, both instances will have the last assigned Bars value to them.

You can look the issue with the code below:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NewcBot : Robot
    {
        private MyDiPlus _diPlusSignal;
        private MyDiPlus _diPlusFilter;
        private DirectionalMovementSystem _adx;
        private Bars SignalBars;

        protected override void OnStart()
        {
            SignalBars = MarketData.GetBars(TimeFrame.Minute5);
            _adx = Indicators.DirectionalMovementSystem(14);

            _diPlusFilter = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusFilter.Bars = Bars;
            _diPlusFilter.Init();

            _diPlusSignal = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusSignal.Bars = SignalBars;
            _diPlusSignal.Init();
        }

        protected override void OnTick()
        {
            var index = Bars.Count - 1;
            var signalIndex = GetIndexByDate(SignalBars, Bars.OpenTimes[index]);
            _diPlusFilter.Calculate(index);
            _diPlusSignal.Calculate(signalIndex);
        }

        protected override void OnBar()
        {
            Print("_diPlusFilter.TimeFrame " + _diPlusFilter.TimeFrame);
            Print("_diPlusFilter.Bars.TimeFrame " + _diPlusFilter.Bars.TimeFrame);

            Print("_diPlusSignal.TimeFrame " + _diPlusSignal.TimeFrame);
            Print("_diPlusSignal.Bars.TimeFrame " + _diPlusSignal.Bars.TimeFrame);

            Print("DI+ Real Value: " + _adx.DIPlus.LastValue.ToString("n2"));
            Print("Filter DI+ Value: " + _diPlusFilter.DiPlus.LastValue.ToString("n2"));
            Print("Signal DI+ Value: " + _diPlusFilter.DiPlus.LastValue.ToString("n2"));
        }
        public static int GetIndexByDate(Bars series, DateTime time)
        {
            for (int i = series.ClosePrices.Count - 1; i > 0; i--)
            {
                if (time == series.OpenTimes[i])
                    return i;
            }
            return -1;
        }
    }
}
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MyDiPlus : Indicator
    {
        private DirectionalMovementSystem _adx;

        [Parameter(DefaultValue = 14)]
        public int ADXPeriods { get; set; }

        [Output("Di Plus", LineColor = "Green")]
        public IndicatorDataSeries DiPlus { get; set; }

        public Bars Bars;

        protected override void Initialize()
        {

        }

        public void Init()
        {
            _adx = Indicators.DirectionalMovementSystem(Bars, ADXPeriods);
        }

        public override void Calculate(int index)
        {
            DiPlus[index] = _adx.DIPlus[index];
        }
    }
}

My proposal for a cleaner solution is to have an extra overload for indicators that require the whole Bars object, with the (usually) required 4 DataSeries (High/Low/Open/Close).

 

With Best Regards


@cAlgoBuddy
Replies

PanagiotisCharalampous
19 May 2021, 15:05

Hi cAlgoBuddy,

This happens because in the following case, the constructor method returns the same indicator.

            _diPlusFilter = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusFilter.Bars = Bars;
            _diPlusFilter.Init();

            _diPlusSignal = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusSignal.Bars = SignalBars;
            _diPlusSignal.Init();

_diPlusFilter and _diPlusSignal are references to the same object. To force the constructor to return a new object, create a dummy parameter and pass a differrert value every time e.g.

            _diPlusFilter = Indicators.GetIndicator<MyDiPlus>("Bars", 14);
            _diPlusFilter.Bars = Bars;
            _diPlusFilter.Init();

            _diPlusSignal = Indicators.GetIndicator<MyDiPlus>("Signal", 14);
            _diPlusSignal.Bars = SignalBars;
            _diPlusSignal.Init();

Hope this helps.

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

cAlgoBuddy
19 May 2021, 15:42

RE:

PanagiotisCharalampous said:

Hi cAlgoBuddy,

This happens because in the following case, the constructor method returns the same indicator.

            _diPlusFilter = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusFilter.Bars = Bars;
            _diPlusFilter.Init();

            _diPlusSignal = Indicators.GetIndicator<MyDiPlus>(14);
            _diPlusSignal.Bars = SignalBars;
            _diPlusSignal.Init();

_diPlusFilter and _diPlusSignal are references to the same object. To force the constructor to return a new object, create a dummy parameter and pass a differrert value every time e.g.

            _diPlusFilter = Indicators.GetIndicator<MyDiPlus>("Bars", 14);
            _diPlusFilter.Bars = Bars;
            _diPlusFilter.Init();

            _diPlusSignal = Indicators.GetIndicator<MyDiPlus>("Signal", 14);
            _diPlusSignal.Bars = SignalBars;
            _diPlusSignal.Init();

Hope this helps.

Best Regards,

Panagiotis 

Join us on Telegram

Many thanks, it works fine like this!


@cAlgoBuddy