Referencing data from custom indicator

Created at 27 Mar 2021, 20:37
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

findsameh

Joined 21.06.2015

Referencing data from custom indicator
27 Mar 2021, 20:37


Dear all,

I'm using a custom indicator (Connor's RSI). The plotted indicator is correct. But when I retrieve the indicator's (Result[index]) previous bar values through crsi.Result.Last(n) I never get the same value as that for the plotted indicator. Am I missing something? I can share code if necessary.

Thanks,

Sameh


@findsameh
Replies

amusleh
28 Mar 2021, 11:13

Hi,

To get the previous completed value on a data series you must use index -1 or Last(1).

If that didn't solved the issue please post the code and we will help you.


@amusleh

findsameh
28 Mar 2021, 14:51

RE:

Dear Ahmad, 

Neither index-1 nor Last(1) work, both return numbers not matching any of the correct results. Below is the CRSI indicator code, followed by a simple bot to retrieve the results.

using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using System.Linq;
using System.Collections.Generic;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ConnorsCRSI11 : Indicator
    {
        [Output("Main", Color = Colors.Orange)]
        public IndicatorDataSeries Result { get; set; }
        [Parameter(DefaultValue = 3.0)]
        public int rsiPeriods { get; set; }
        [Parameter(DefaultValue = 2.0)]
        public int streakPeriods { get; set; }
        [Parameter(DefaultValue = 100.0)]
        public int rocPeriods { get; set; }

        public RelativeStrengthIndex rsi;
        public int streak;
        public double avgGain1;
        public double avgLoss1;
        public double avgGain;
        public double avgLoss;
        public double rsiStreakValue = 0;
        public List<double> listOfAvgGain = new List<double>();
        public List<double> listOfAvgLoss = new List<double>();
        public List<double> listOfGain = new List<double>();
        public List<double> listOfLoss = new List<double>();
        public List<int> listOfStreaks = new List<int>();
        public int startIndex;
        public double percentRank = 0.0;

        protected override void Initialize()
        {
            rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, rsiPeriods);
            startIndex = 1;
        }
        public override void Calculate(int index)
        {
            UpDownStreak(streakPeriods, index + 1);
            RsiStreak(streakPeriods, index);
            Roc(rocPeriods, index);
            Result[index] = (rsiStreakValue + rsi.Result.LastValue + percentRank) / 3;

        }
        // crsi calculations
        public void Roc(double periods, int index)
        {
            var countRocDn = 0;
            var prevDayRoc = 0.0;
            var todayRoc = (Bars.ClosePrices[index] - Bars.ClosePrices[index - 1]) / Bars.ClosePrices[index - 1];
            for (int i = 1; i <= periods; i++)
            {
                prevDayRoc = (Bars.ClosePrices[index - i] - Bars.ClosePrices[index - i - 1]) / Bars.ClosePrices[index - i - 1];
                if (todayRoc > prevDayRoc)
                    countRocDn++;
            }
            percentRank = (countRocDn / periods) * 100;
        }
        public void UpDownStreak(int periods, int index)
        {
            int countUp = 0;
            int countDn = 0;
            int countEqual = 0;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] > Bars.ClosePrices[index - i - 1])
                {
                    countUp++;
                    continue;
                }
                else
                    break;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] < Bars.ClosePrices[index - i - 1])
                {
                    countDn++;
                    continue;
                }
                else
                    break;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] == Bars.ClosePrices[index - i - 1])
                {
                    countEqual++;
                    continue;
                }
                else
                    break;
            if (countUp > countDn && countUp > countEqual)
            {
                streak = countUp;
                listOfStreaks.Add(countUp);
            }
            else if (countDn > countUp && countDn > countEqual)
            {
                streak = -countDn;
                listOfStreaks.Add(-countDn);
            }
            else if (countEqual > countUp && countEqual > countDn)
            {
                streak = 0;
                listOfStreaks.Add(0);
            }
            if (listOfStreaks.Count > periods)
                listOfStreaks.RemoveAt(0);
        }
        public void RsiStreak(int periods, int index)
        {
            if (listOfStreaks.Count < periods)
                return;
            double changeValue = 0;
            double rs = 0;
            List<double> listOfChangeValues = new List<double>();
            for (int i = 0; i < periods - 1; i++)
            {
                changeValue = listOfStreaks[i + 1] - listOfStreaks[i];
                if (changeValue > 0)
                {
                    listOfGain.Add(changeValue);
                    listOfLoss.Add(0);
                }
                else
                {
                    listOfLoss.Add(Math.Abs(changeValue));
                    listOfGain.Add(0);
                }
            }
            if (1 == startIndex)
            {
                avgGain1 = listOfGain.Average();
                avgLoss1 = listOfLoss.Average();
                listOfAvgGain.Add(avgGain1);
                listOfAvgLoss.Add(avgLoss1);
            }
            else
            {
                avgGain = ((listOfAvgGain[listOfAvgGain.Count - 1] * (periods - 1)) + listOfGain[listOfGain.Count - 1]) / periods;
                avgLoss = ((listOfAvgLoss[listOfAvgLoss.Count - 1] * (periods - 1)) + listOfLoss[listOfLoss.Count - 1]) / periods;
                listOfAvgGain.Add(avgGain);
                listOfAvgLoss.Add(avgLoss);
            }
            if (1 == startIndex)
            {
                startIndex = 2;
                rs = avgGain1 / avgLoss1;
                if (avgLoss1 == 0)
                    rsiStreakValue = 100;
                else
                    rsiStreakValue = 100 - (100 / (1 + rs));
            }
            else
            {
                rs = avgGain / avgLoss;
                if (avgLoss == 0)
                    rsiStreakValue = 100;
                else
                    rsiStreakValue = 100 - (100 / (1 + rs));
            }
            if (listOfLoss.Count >= periods)
                listOfLoss.RemoveAt(0);
            if (listOfGain.Count >= periods)
                listOfGain.RemoveAt(0);
            if (listOfAvgGain.Count >= periods)
                listOfAvgGain.RemoveAt(0);
            if (listOfAvgLoss.Count >= periods)
                listOfAvgLoss.RemoveAt(0);
            if (listOfStreaks.Count >= periods)
                listOfStreaks.RemoveAt(0);
        }
    }
}
===========

Bot to retrieve results:

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 cBotNew : Robot
    {
        [Parameter("RSI periods", Group = "CRSI", DefaultValue = 3.0, MinValue = 1)]
        public int rsiPeriods { get; set; }
        [Parameter("Up/down streak periods", Group = "CRSI", DefaultValue = 2.0, MinValue = 1)]
        public int streakPeriods { get; set; }
        [Parameter("Rate of change periods", Group = "CRSI", DefaultValue = 100.0, MinValue = 2)]
        public int rocPeriods { get; set; }

        public ConnorsCRSI11 crsi;

        protected override void OnStart()
        {
            crsi = Indicators.GetIndicator<ConnorsCRSI11>(rsiPeriods, streakPeriods, rocPeriods);
        }
        protected override void OnBar()
        {
            Print(crsi.Result.Last(1));
        }
    }
}
===========

Thanks,

Sameh


@findsameh

amusleh
29 Mar 2021, 11:45

I changed the lists to IndicatorDataSeries and now the results are matching, the indicator might be changed so please modify it if something is changed related to indicator formula.

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

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ConnorsCRSI11 : Indicator
    {
        private IndicatorDataSeries listOfAvgGain;
        private IndicatorDataSeries listOfAvgLoss;
        private IndicatorDataSeries listOfGain;
        private IndicatorDataSeries listOfLoss;
        private IndicatorDataSeries listOfStreaks;

        [Output("Main", Color = Colors.Orange, IsHistogram = true, PlotType = PlotType.Histogram)]
        public IndicatorDataSeries Result { get; set; }

        [Parameter(DefaultValue = 3.0)]
        public int rsiPeriods { get; set; }

        [Parameter(DefaultValue = 2.0)]
        public int streakPeriods { get; set; }

        [Parameter(DefaultValue = 100.0)]
        public int rocPeriods { get; set; }

        public RelativeStrengthIndex rsi;
        public int streak;
        public double avgGain1;
        public double avgLoss1;
        public double avgGain;
        public double avgLoss;

        public int startIndex;
        public double percentRank = 0.0;

        protected override void Initialize()
        {
            listOfAvgGain = CreateDataSeries();
            listOfAvgLoss = CreateDataSeries();
            listOfGain = CreateDataSeries();
            listOfLoss = CreateDataSeries();
            listOfStreaks = CreateDataSeries();

            rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, rsiPeriods);
            startIndex = 1;
        }

        public override void Calculate(int index)
        {
            if (index <= rocPeriods) return;

            UpDownStreak(streakPeriods, index);
            var rsiStreakValue = GetRsiStreak(streakPeriods, index);
            Roc(rocPeriods, index);

            Result[index] = (rsiStreakValue + rsi.Result[index] + percentRank) / 3;
        }

        // crsi calculations
        public void Roc(double periods, int index)
        {
            var countRocDn = 0;
            var prevDayRoc = 0.0;
            var todayRoc = (Bars.ClosePrices[index] - Bars.ClosePrices[index - 1]) / Bars.ClosePrices[index - 1];
            for (int i = 1; i <= periods; i++)
            {
                prevDayRoc = (Bars.ClosePrices[index - i] - Bars.ClosePrices[index - i - 1]) / Bars.ClosePrices[index - i - 1];
                if (todayRoc > prevDayRoc)
                    countRocDn++;
            }
            percentRank = (countRocDn / periods) * 100;
        }

        public void UpDownStreak(int periods, int index)
        {
            int countUp = 0;
            int countDn = 0;
            int countEqual = 0;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] > Bars.ClosePrices[index - i - 1])
                {
                    countUp++;
                    continue;
                }
                else
                    break;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] < Bars.ClosePrices[index - i - 1])
                {
                    countDn++;
                    continue;
                }
                else
                    break;
            for (int i = 1; i < 100; i++)
                if (Bars.ClosePrices[index - i] == Bars.ClosePrices[index - i - 1])
                {
                    countEqual++;
                    continue;
                }
                else
                    break;
            if (countUp > countDn && countUp > countEqual)
            {
                streak = countUp;
                listOfStreaks[index] = countUp;
            }
            else if (countDn > countUp && countDn > countEqual)
            {
                streak = -countDn;
                listOfStreaks[index] = -countDn;
            }
            else if (countEqual > countUp && countEqual > countDn)
            {
                streak = 0;
                listOfStreaks[index] = 0;
            }
        }

        public double GetRsiStreak(int periods, int index)
        {
            if (listOfStreaks.Count < periods)
                return 0;

            double changeValue;
            double rs;
            double result;

            for (int i = index; i > index - periods; i--)
            {
                changeValue = listOfStreaks[i - 1] - listOfStreaks[i];

                listOfLoss[index] = 0;
                listOfGain[index] = 0;

                if (double.IsNaN(changeValue)) continue;

                if (changeValue > 0)
                {
                    listOfGain[index] = changeValue;
                }
                else
                {
                    listOfLoss[index] = Math.Abs(changeValue);
                }
            }

            if (1 == startIndex)
            {
                avgGain1 = listOfGain.Average();
                avgLoss1 = listOfLoss.Average();

                listOfAvgGain[index] = double.IsNaN(avgGain1) ? 0 : avgGain1;
                listOfAvgLoss[index] = double.IsNaN(avgLoss1) ? 0 : avgLoss1;
            }
            else
            {
                avgGain = ((listOfAvgGain[index - 1] * (periods - 1)) + listOfGain[index]) / periods;
                avgLoss = ((listOfAvgLoss[index - 1] * (periods - 1)) + listOfLoss[index]) / periods;

                listOfAvgGain[index] = avgGain;
                listOfAvgLoss[index] = avgLoss;
            }

            if (1 == startIndex)
            {
                startIndex = 2;
                rs = avgGain1 / avgLoss1;
                if (avgLoss1 == 0)
                    result = 100;
                else
                    result = 100 - (100 / (1 + rs));
            }
            else
            {
                rs = avgGain / avgLoss;
                if (avgLoss == 0)
                    result = 100;
                else
                    result = 100 - (100 / (1 + rs));
            }

            return result;
        }
    }
}
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class cBotNew : Robot
    {
        [Parameter("RSI periods", Group = "CRSI", DefaultValue = 3.0, MinValue = 1)]
        public int rsiPeriods { get; set; }

        [Parameter("Up/down streak periods", Group = "CRSI", DefaultValue = 2.0, MinValue = 1)]
        public int streakPeriods { get; set; }

        [Parameter("Rate of change periods", Group = "CRSI", DefaultValue = 100.0, MinValue = 2)]
        public int rocPeriods { get; set; }

        public ConnorsCRSI11 crsi;

        protected override void OnStart()
        {
            crsi = Indicators.GetIndicator<ConnorsCRSI11>(rsiPeriods, streakPeriods, rocPeriods);
        }

        protected override void OnBar()
        {
            var index = Bars.Count - 2;

            Print(crsi.Result[index], " | ", Bars.OpenTimes[index].ToString("R"));
        }
    }
}

 


@amusleh

findsameh
30 Mar 2021, 22:05

RE:

Hello Ahmad,

Thanks for the suggestion. But with your suggested solution if you compare the result through the indicator code (Result[index]) and compare it to the result through the bot (crsi.Result[index]), you'd find that both values still don't match.

And I'm not sure why I'd need to change the indicator code to retrieve a value from indicator to bot, knowing the indicator already produces the correct values for CRSI.

Sameh


@findsameh

amusleh
31 Mar 2021, 09:16

RE: RE:

findsameh said:

Hello Ahmad,

Thanks for the suggestion. But with your suggested solution if you compare the result through the indicator code (Result[index]) and compare it to the result through the bot (crsi.Result[index]), you'd find that both values still don't match.

And I'm not sure why I'd need to change the indicator code to retrieve a value from indicator to bot, knowing the indicator already produces the correct values for CRSI.

Sameh

There was some issues in your indicator code, that causes issues when you use it on a cBot, like using invalid bar index.

I checked the indicator and cBot I posted and the results does match on backtest for EURUSD on Spotware cTrader beta.


@amusleh

findsameh
31 Mar 2021, 12:14

RE: RE: RE:

amusleh said:

findsameh said:

Hello Ahmad,

Thanks for the suggestion. But with your suggested solution if you compare the result through the indicator code (Result[index]) and compare it to the result through the bot (crsi.Result[index]), you'd find that both values still don't match.

And I'm not sure why I'd need to change the indicator code to retrieve a value from indicator to bot, knowing the indicator already produces the correct values for CRSI.

Sameh

There was some issues in your indicator code, that causes issues when you use it on a cBot, like using invalid bar index.

I checked the indicator and cBot I posted and the results does match on backtest for EURUSD on Spotware cTrader beta.

Apologies, my mistake ... they do match. Thanks a lot.

Sameh


@findsameh