Indicator difficulty: Index was outside the bounds of the array

Created at 13 Sep 2023, 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!
WM

wmclennan77

Joined 29.06.2021

Indicator difficulty: Index was outside the bounds of the array
13 Sep 2023, 17:21


Wondering if someone can help me, I'm a bit stuck.

I am trying to port a TV indicator to cTrader, I mostly finished but received a Crashed in Calculate with IndexOutOfRangeException: Index was outside the bounds of the array error.

I know that the error is somehow a result of trying to access data not in the array scope, but I am limited in my skill and cannot find where on my own.

Current code below (The original indicator is a bit messy in terms of code layout, so I tried to refactor and tidy up as much as I could).

Any help would be greatly appreciated!

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

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class FareidMRC : Indicator
    {
        //Parameters
        [Parameter("Price Source", Group = "MRC Parameters", DefaultValue = SourceEnum.HLC3)]
        public SourceEnum sourceParameter { get; set; }

        [Parameter("Filter Type", Group = "MRC Parameters", DefaultValue = IndicatorTypeEnum.SuperSmoother)]
        public IndicatorTypeEnum typeParameter { get; set; }

        [Parameter("Lookback Period", Group = "MRC Parameters", DefaultValue = 200, MinValue = 1)]
        public int lengthParameter { get; set; }

        [Parameter("Inner Channel Size Multiplier", Group = "MRC Parameters", DefaultValue = 1.0, MinValue = 0.1, Step = 0.005)]
        public double innermultParameter { get; set; }

        [Parameter("Inner Channel Size Multiplier", Group = "MRC Parameters", DefaultValue = 2.415, MinValue = 0.1, Step = 0.005)]
        public double outermultParameter { get; set; }

        [Parameter("Draw Mean Reversion Channels", Group = "MRC Parameters", DefaultValue = true)]
        public bool drawchannelParameter { get; set; }

        [Parameter("Draw Mean Reversion Zones (MRC Must Be Yes)", Group = "MRC Parameters", DefaultValue = true)]
        public bool displayzoneParameter { get; set; }

        [Output("Mean Output", LineStyle = LineStyle.Solid, Thickness = 1, LineColor = "#FFCD00")]
        public IndicatorDataSeries z { get; set; }

        [Output("R1", LineStyle = LineStyle.Solid, Thickness = 1, LineColor = "Green")]
        public IndicatorDataSeries x1 { get; set; }

        [Output("S1", LineStyle = LineStyle.Solid, Thickness = 1, LineColor = "Green")]
        public IndicatorDataSeries x2 { get; set; }

        [Output("R2", LineStyle = LineStyle.Solid, Thickness = 1, LineColor = "Red")]
        public IndicatorDataSeries y1 { get; set; }

        [Output("S2", LineStyle = LineStyle.Solid, Thickness = 1, LineColor = "Red")]
        public IndicatorDataSeries y2 { get; set; }

        //Enumerators
        public enum SourceEnum
        {
            Open,
            High,
            Low,
            Close,
            HL2,
            HLC3,
            OHLC4,
            HLCC4
        }

        public enum IndicatorTypeEnum
        {
            SuperSmoother,
            Ehlers_EMA,
            Gaussian,
            Butterworth,
            BandStop,
            SMA,
            EMA,
            RMA
        }

        //Global Variables
        double pi, mult, mult2;
        double[] sourceValue, ss, _Output, _Input;

        protected override void Initialize()
        {
            pi = 2 * Math.Asin(1);
            mult = pi * innermultParameter;
            mult2 = pi * outermultParameter;
        }

        public override void Calculate(int index)
        {
            if (index < lengthParameter)
            {
                return;
            }

            //Initialize source array
            if (sourceValue == null)
            {
                sourceValue = new double[index];
            }

            //Create index for source
            sourceValue[index] = GetIndiSet(index);

            GetMRC(index);
        }

        #region Methods

        public double GetIndiSet(int methodIndex)
        {
            double Result;
            switch (sourceParameter)
            {
                case SourceEnum.Open:
                    Result = Bars.OpenPrices[methodIndex];
                    break;
                case SourceEnum.High:
                    Result = Bars.HighPrices[methodIndex];
                    break;
                case SourceEnum.Low:
                    Result = Bars.LowPrices[methodIndex];
                    break;
                case SourceEnum.Close:
                    Result = Bars.ClosePrices[methodIndex];
                    break;
                case SourceEnum.HL2:
                    Result = Math.Round((Bars.HighPrices[methodIndex] + Bars.LowPrices[methodIndex]) / 2, Symbol.Digits);
                    break;
                case SourceEnum.HLC3:
                    Result = Math.Round((Bars.HighPrices[methodIndex] + Bars.LowPrices[methodIndex] + Bars.ClosePrices[methodIndex]) / 3, Symbol.Digits);
                    break;
                case SourceEnum.OHLC4:
                    Result = Math.Round((Bars.OpenPrices[methodIndex] + Bars.HighPrices[methodIndex] + Bars.LowPrices[methodIndex] + Bars.ClosePrices[methodIndex]) / 4, Symbol.Digits);
                    break;
                case SourceEnum.HLCC4:
                    Result = Math.Round((Bars.HighPrices[methodIndex] + Bars.LowPrices[methodIndex] + Bars.ClosePrices[methodIndex] + Bars.ClosePrices[methodIndex]) / 4, Symbol.Digits);
                    break;
                default:
                    Result = double.NaN;
                    break;
            }
            return Result;
        }

        private void GetMRC(int methodIndex)
        {
            //Mean values
            double v_meanrange = GetSmoothing(methodIndex, IndicatorTypeEnum.SuperSmoother, sourceValue[methodIndex], lengthParameter);
            double v_meanline = GetSmoothing(methodIndex, typeParameter, sourceValue[methodIndex], lengthParameter);

            //Deviation values
            double v_upband1 = v_meanline + (v_meanrange * mult);
            double v_loband1 = v_meanline - (v_meanrange * mult);
            double v_upband2 = v_meanline + (v_meanrange * mult2);
            double v_loband2 = v_meanline - (v_meanrange * mult2);

            //Draw Outputs
            z[methodIndex] = drawchannelParameter ? v_meanline : 0;
            x1[methodIndex] = drawchannelParameter ? v_upband1 : 0;
            x2[methodIndex] = drawchannelParameter ? v_loband1 : 0;
            y1[methodIndex] = drawchannelParameter ? v_upband2 : 0;
            y2[methodIndex] = drawchannelParameter ? v_loband2 : 0;
        }

        private double GetSmoothing(int methodIndex, IndicatorTypeEnum typeIndex, double _src, int _length)
        {
            double c0 = 1.0;
            double c1 = 0.0;
            double b0 = 1.0;
            double b1 = 0.0;
            double b2 = 0.0;
            double a2 = 0.0;
            double alpha;
            double beta;
            double gamma;
            double cycle = 2 * pi / _length;

            if (_Input == null)
            {
                _Input = new double[methodIndex];
            }
            for (int i = methodIndex - 1; i > 0; i--)
            {
                _Input[i] = _Input[i - 1];
            }
            _Input[0] = _src;
            if (_Output == null)
            {
                _Output = new double[methodIndex];
            }
            for (int i = methodIndex - 1; i > 0; i--)
            {
                _Output[i] = _Output[i - 1];
            }

            switch (typeIndex)
            {
                case IndicatorTypeEnum.SuperSmoother:
                    double s_a1 = Math.Exp(-Math.Sqrt(2) * pi / _length);
                    double s_b1 = 2 * s_a1 * Math.Cos(Math.Sqrt(2) * pi / _length);
                    double s_c3 = -Math.Pow(s_a1, 2);
                    double s_c2 = s_b1;
                    double s_c1 = 1 - s_c2 - s_c3;

                    if (ss == null)
                    {
                        ss = new double[methodIndex];
                    }
                    for (int i = methodIndex - 1; i > 0; i--)
                    {
                        ss[i] = ss[i - 1];
                    }
                    ss[0] = s_c1 * _src + s_c2 * nz(ss[1], _src) + s_c3 * nz(ss[2], _src);
                    return ss[0];

                case IndicatorTypeEnum.Ehlers_EMA:
                    alpha = (Math.Cos(cycle) + Math.Sin(cycle) - 1) / Math.Cos(cycle);
                    b0 = alpha;

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.Gaussian:
                    beta = 2.415 * (1 - Math.Cos(cycle));
                    alpha = -beta + Math.Sqrt((beta * beta) + (2 * beta));
                    c0 = alpha * alpha;
                    a2 = -(1 - alpha) * (1 - alpha);

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.Butterworth:
                    beta = 2.415 * (1 - Math.Cos(cycle));
                    alpha = -beta + Math.Sqrt((beta * beta) + (2 * beta));
                    c0 = alpha * alpha / 4;
                    b1 = 2;
                    b2 = 1;
                    a2 = -(1 - alpha) * (1 - alpha);

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.BandStop:
                    beta = Math.Cos(cycle);
                    gamma = 1 / Math.Cos(cycle * 2 * 0.1);
                    alpha = gamma - Math.Sqrt((gamma * gamma) - 1);
                    c0 = (1 + alpha) / 2;
                    b1 = -2 * beta;
                    b2 = 1;
                    a2 = -alpha;

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.SMA:
                    c1 = 1 / _length;
                    b0 = 1 / _length;

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.EMA:
                    alpha = 2 / (_length + 1);
                    b0 = alpha;

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                case IndicatorTypeEnum.RMA:
                    alpha = 1 / _length;
                    b0 = alpha;

                    _Output[0] = (c0 * ((b0 * _Input[0]) + (b1 * nz(sourceValue[methodIndex - 1], 0)) + (b2 * nz(sourceValue[methodIndex - 2], 0)))) + (a2 * nz(_Output[2], 0)) - (c1 * nz(_Input[_length], 0));
                    return _Output[0];

                default:
                    return 0.0;
            }
        }

        private double nz(double source, double replacement)
        {
            return double.IsNaN(source) ? replacement : source;
        }

        #endregion
    }
}

 


@wmclennan77
Replies

PanagiotisChar
14 Sep 2023, 06:32 ( Updated at: 14 Sep 2023, 06:34 )

Hi there,

Your problem is probably here

            //Initialize source array
            if (sourceValue == null)
            {
                sourceValue = new double[index];
            }

            //Create index for source
            sourceValue[index] = GetIndiSet(index);

In C# indexing starts from 0. So the max index for an array with a length of index is index - 1.


@PanagiotisChar

wmclennan77
14 Sep 2023, 08:04

RE: Indicator difficulty: Index was outside the bounds of the array

PanagiotisChar said: 

Hi there,

Your problem is probably here

            //Initialize source array            if (sourceValue == null)            {                sourceValue = new double[index];            }            //Create index for source            sourceValue[index] = GetIndiSet(index);

In C# indexing starts from 0. So the max index for an array with a length of index is index - 1.

Hi,

Thanks for the response and assistance.

I updated the method as follows:

public override void Calculate(int index)
        {
            if (index < lengthParameter)
            {
                return;
            }

            //Initialize source array
            if (sourceValue == null)
            {
                sourceValue = new double[index];
            }

            //Create index for source - updated to index - 1
            sourceValue[index - 1] = GetIndiSet(index);

            GetMRC(index);
        }

I am still receiving the same error, could it be one of the other arrays in the code or did I make the wrong alteration?


@wmclennan77

PanagiotisChar
15 Sep 2023, 06:16

The problem is at the same point. When the array is initialized and the index increases, then the array is again shorter than the index. Your solution is not appropriate, use a list instead


@PanagiotisChar