Converting Indicator to .NET 6 - ArgumentOutOfRangeException

Created at 24 Jul 2022, 10:32
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!
CT

ctid3999979

Joined 05.07.2021

Converting Indicator to .NET 6 - ArgumentOutOfRangeException
24 Jul 2022, 10:32


Hi

As stated in the subject, I'm getting the following error:

24/07/2022 08:15:11.807 | Indicator instance [Moving Average Slope, GBPUSD, t250] crashed with error "Crashed in Calculate with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: y2. Actual value was: NaN."

I'm not getting any errors or warnings presented in VS2022 and it builds successfully from within VS and cTrader. I get the above error when trying to load the indicator on the chart. Works fine in cTrader 4.1. I don't have a parameter named 'y2' so I don't know what it's referring to.

I created a new Indicator in cTrader 4.3.9 and copied my cAlgo namespace from the previous indicator.

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MovingAverageSlope : Indicator
    {
        // Define parameters
        [Parameter("Type:", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType maType { get; set; }

        [Parameter("Periods", DefaultValue = 20, MinValue = 2, Step = 1)]
        public int maPeriod { get; set; }

        [Parameter("Show Slope:", DefaultValue = true)]
        public bool slopeShow { get; set; }

        [Parameter("Slope Length:", DefaultValue = 10, MinValue = 5, Step = 1)]
        public int slopePeriod { get; set; }

        [Parameter("Timeframe:", DefaultValue = "t250")]
        public TimeFrame maTimeFrame { get; set; }

        [Parameter("Smoothing:", DefaultValue = true)]
        public bool maSmoothing { get; set; }

        [Parameter("Show Info:", DefaultValue = true)]
        public bool infoShow { get; set; }

        [Output("Close", LineColor = "#FFFF00C5")]
        public IndicatorDataSeries maCloseLine { get; set; }

        [Output("Slope", LineColor = "#FFFFFF01")]
        public IndicatorDataSeries slopeLine { get; set; }

        // Instantiate indicator objects
        private Bars priceBars;
        private int maTimeIndex;
        private MovingAverage maClose;

        // Instatiate information panels
        private StackPanel maPanel;
        private StackPanel trendPanel;
        private TextBlock[] maTextBlock = new TextBlock[3];
        private TextBlock[] trendTextBlock = new TextBlock[2];

        // Define the start and end of the period of time that trades are allowed to be entered
        TimeSpan tradingStart = new TimeSpan(7, 0, 0);
        TimeSpan tradingEnd = new TimeSpan(11, 0, 0);


        protected override void Initialize()
        {

            // Get candle data and create moving average
            priceBars = MarketData.GetBars(maTimeFrame);
            maClose = Indicators.MovingAverage(priceBars.ClosePrices, maPeriod, maType);

            // Create primary slope information panel
            maPanel = new StackPanel
            {
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top,
                BackgroundColor = Color.Black,
                Margin = 5
            };

            // Add slope text blocks to primary information panel
            for (int i = 0; i < maTextBlock.Count(); i++)
            {
                maTextBlock[i] = new TextBlock
                {
                    Text = "",
                    HorizontalAlignment = HorizontalAlignment.Right,
                    ForegroundColor = Color.White
                };
                maPanel.AddChild(maTextBlock[i]);
            }
            Chart.AddControl(maPanel);

            // Create secondary information panel
            trendPanel = new StackPanel
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Bottom,
                BackgroundColor = Color.Black,
                Margin = 5
            };

            //Add text blocks to secondary information panel
            for (int i = 0; i < trendTextBlock.Count(); i++)
            {
                trendTextBlock[i] = new TextBlock
                {
                    Text = "",
                    ForegroundColor = Color.White,
                    HorizontalAlignment = HorizontalAlignment.Center
                };
                trendPanel.AddChild(trendTextBlock[i]);
            }
            Chart.AddControl(trendPanel);
        }


        public override void Calculate(int index)
        {
            // Smooth moving average
            maTimeIndex = GetIndexByDate(priceBars, Bars.OpenTimes[index]);

            if (maTimeIndex != -1)
            {
                maCloseLine[index] = maClose.Result[maTimeIndex];
            }

            if (slopeShow == true)
            {
                DisplayMASlope(maClose.Result.Last(0), maClose.Result.Last(slopePeriod), slopePeriod);
            }
        }

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

        public void DisplayMASlope(double currentMAClose, double pastMAClose, int period)
        {
            // Calculate slope
            double slope = Math.Round(((currentMAClose - pastMAClose) * 10000) / period, 5);

            if (infoShow == true)
            {
                maTextBlock[0].Text = String.Format("Slope {0}", slope);
                trendTextBlock[1].Text = "It's better to miss one than to lose one!";
                trendTextBlock[1].ForegroundColor = Color.Yellow;

                if (slope >= 0.3)
                {
                    // UPTREND information panel text
                    string directionText = "Long Positions Only!";
                    Color directionTextColor = Color.LimeGreen;

                    string strategyText = "Buy at moving average or Stochastic 'oversold'";
                    Color strategyTextColor = Color.Yellow;

                    maTextBlock[1].Text = directionText;
                    maTextBlock[1].ForegroundColor = directionTextColor;
                    trendTextBlock[0].Text = directionText;
                    trendTextBlock[0].ForegroundColor = directionTextColor;

                    maTextBlock[2].Text = strategyText;
                    maTextBlock[2].ForegroundColor = strategyTextColor;
                }
                else if (slope <= -0.3)
                {
                    // DOWNTREND information panel text
                    string directionText = "Short Positions Only!";
                    Color directionTextColor = Color.Red;

                    string strategyText = "Sell at moving average or Stochastic 'overbought'";
                    Color strategyTextColor = Color.Yellow;


                    maTextBlock[1].Text = directionText;
                    maTextBlock[1].ForegroundColor = directionTextColor;
                    trendTextBlock[0].Text = directionText;
                    trendTextBlock[0].ForegroundColor = directionTextColor;

                    maTextBlock[2].Text = strategyText;
                    maTextBlock[2].ForegroundColor = strategyTextColor;
                }
                else
                {
                    // SIDEWAYS information panel text
                    string directionText = "Sideways Range";
                    Color directionTextColor = Color.White;

                    string strategyText = "Trade Stochastic 'overbought' and 'oversold' levels";
                    Color strategyTextColor = Color.Yellow;

                    maTextBlock[1].Text = directionText;
                    maTextBlock[1].ForegroundColor = directionTextColor;
                    trendTextBlock[0].Text = directionText;
                    trendTextBlock[0].ForegroundColor = directionTextColor;

                    maTextBlock[2].Text = strategyText;
                    maTextBlock[2].ForegroundColor = strategyTextColor;
                }
            }



            // Draw slope onto moving average
            Chart.DrawTrendLine("Slope", priceBars.OpenTimes.Last(0), currentMAClose, priceBars.OpenTimes.Last(period), pastMAClose, Color.Yellow, 1, LineStyle.Lines);

        }
    }
}

EDIT:

Just did a bit more of a test. I'm getting the same result if I use my original .NET 4 project but change the Target Framework to .NET 6.

I have 2 charts open: the t250 and t50. I can add the indicator to the t50 chart without a problem. When adding it to the t250 chart I get the error. If I close my t250 chart and duplicate my t50 chart with the indicator already loaded on the chart, then change the timeframe to t250, it crashes my indicator again. However, I can add it to the t250 chart if I change my indicator to get the t500 bars.

It seems to crash if I'm using the indicator is using the same timeframe as the chart.


@ctid3999979