Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index

Created at 18 Apr 2021, 03:19
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!
TH

thenewlearner

Joined 18.04.2021

Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index
18 Apr 2021, 03:19


 

Hi All

I am trying to use cTrader Automate bot function, strange issue i am facing

I am getting the error

"Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index"

if i use it for backtesting on gbpusd 15min or more

if i use the backtesting on m1 or m5 gbpusd, it does not give error and it works. Any idea?

 

Below is the code

....

using System;
using System.Linq;
using System.Net;
using System.Text;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MLBot : Robot
    {
        private const string Name = "MLBot";

        [Parameter("Base Url", DefaultValue = "http://localhost:5000/predict")]
        public string BaseUrl { get; set; }

        [Parameter("Batch Size", MinValue = 1, DefaultValue = 128)]
        public int BatchSize { get; set; }

        [Parameter("Window Size", MinValue = 1, DefaultValue = 256)]
        public int WindowSize { get; set; }

        [Parameter("MA Periods", MinValue = 1, DefaultValue = 14)]
        public int MAPeriods { get; set; }

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

        [Parameter("Prediction Size", MinValue = 1, DefaultValue = 4)]
        public int PredSize { get; set; }

        [Parameter("Volume", Group = "Standard", DefaultValue = 20000)]
        public double Volume { get; set; }

        [Parameter("Max Spread Limit (Pips)", Group = "Standard", DefaultValue = 1.5)]
        public double MaxSpreadLimitInPips { get; set; }

        private WebClient _webClient;
        private double _maxSpreadLimitAbsolute = 0;
        private string _baseUrlParam;

        private string GetUrl()
        {
            StringBuilder sb = new StringBuilder(_baseUrlParam);
            sb.Append("/");
            sb.Append(Time.ToString("yyyyMMddHHmmss"));
            sb.Append("/");

            for (int i = WindowSize + MAPeriods - 1; i >= 0; i--)
            {
                sb.Append(((Bars.Last(i).High + Bars.Last(i).Low) / 2).ToString());
                sb.Append(",");
            }
            sb.Length = sb.Length - 1;
            // removing the last comma
            return sb.ToString();
        }

        private TradeType? GetMLPrediction()
        {
            string url = GetUrl();
            string tradeType = _webClient.DownloadString(url);
            switch (tradeType)
            {
                case "1":
                    return TradeType.Buy;
                case "-1":
                    return TradeType.Sell;
                case "0":
                    return null;
                default:
                    throw new InvalidOperationException("Not an expected return from the ML: " + (tradeType ?? "(null)"));
            }
        }

        protected override void OnStart()
        {
            _webClient = new WebClient();
            _maxSpreadLimitAbsolute = MaxSpreadLimitInPips * Symbol.PipSize;
            _baseUrlParam = BaseUrl + "/" + Symbol.Name.ToLower() + "/" + BatchSize + "/" + WindowSize + "/" + MAPeriods + "/" + (Symbol.PipSize * Pips) + "/" + PredSize;
        }

        protected override void OnBar()
        {
            // Prevents running this in production
            if (!IsBacktesting)
            {
                return;
            }
            if ((Ask - Bid) > _maxSpreadLimitAbsolute)
            {
                return;
            }

            if (Positions.FindAll(Name, SymbolName).Any())
            {
                return;
            }

            TradeType? tradeType = GetMLPrediction();

            if (tradeType == null)
            {
                return;
            }

            ExecuteMarketOrder(tradeType.Value, Symbol.Name, Volume, Name, Pips, Pips);
        }

 

 

 

 

 


@thenewlearner
Replies

amusleh
18 Apr 2021, 09:21

This line is the issue:

sb.Append(((Bars.Last(i).High + Bars.Last(i).Low) / 2).ToString());

It throws argument out of range because you don't check the number of available values inside bars collection.

You have to check if there is that many bars or not via Bars Count property before trying to access a value via an index or last method.

And for working with URIs I recommend you to use .NET UriBuilder instead of StringBuilder.

To debug your indicator/cBot use Print method or attach Visual Studio debugger and set break points.


@amusleh

thenewlearner
18 Apr 2021, 15:32

RE:

thanks amusleh

the thing which is strange is this code works for m1 and m5 but not m15 or h1. So that is confusing for me.

I was expecting might be the history in ctrader is not up-to-date (like metatrader there is a history tab and some time you just need to download the history yourself) 

If you have time, then can you tell exactly what to modify in the code to work.

 

Thanks once again

amusleh said:

This line is the issue:

sb.Append(((Bars.Last(i).High + Bars.Last(i).Low) / 2).ToString());

It throws argument out of range because you don't check the number of available values inside bars collection.

You have to check if there is that many bars or not via Bars Count property before trying to access a value via an index or last method.

And for working with URIs I recommend you to use .NET UriBuilder instead of StringBuilder.

To debug your indicator/cBot use Print method or attach Visual Studio debugger and set break points.

 


@thenewlearner

amusleh
19 Apr 2021, 10:41

You should check the Bars number:

using cAlgo.API;
using cAlgo.API.Internals;
using System;
using System.Linq;
using System.Net;
using System.Text;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MLBot : Robot
    {
        private const string Name = "MLBot";

        [Parameter("Base Url", DefaultValue = "http://localhost:5000/predict")]
        public string BaseUrl { get; set; }

        [Parameter("Batch Size", MinValue = 1, DefaultValue = 128)]
        public int BatchSize { get; set; }

        [Parameter("Window Size", MinValue = 1, DefaultValue = 256)]
        public int WindowSize { get; set; }

        [Parameter("MA Periods", MinValue = 1, DefaultValue = 14)]
        public int MAPeriods { get; set; }

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

        [Parameter("Prediction Size", MinValue = 1, DefaultValue = 4)]
        public int PredSize { get; set; }

        [Parameter("Volume", Group = "Standard", DefaultValue = 20000)]
        public double Volume { get; set; }

        [Parameter("Max Spread Limit (Pips)", Group = "Standard", DefaultValue = 1.5)]
        public double MaxSpreadLimitInPips { get; set; }

        private WebClient _webClient;
        private double _maxSpreadLimitAbsolute = 0;
        private string _baseUrlParam;

        private string GetUrl()
        {
            StringBuilder sb = new StringBuilder(_baseUrlParam);
            sb.Append("/");
            sb.Append(Time.ToString("yyyyMMddHHmmss"));
            sb.Append("/");

            for (int i = WindowSize + MAPeriods - 1; i >= 0; i--)
            {
                if (Bars.Count > i)
                {
                    sb.Append(((Bars.Last(i).High + Bars.Last(i).Low) / 2).ToString());
                }

                sb.Append(",");
            }
            sb.Length = sb.Length - 1;
            // removing the last comma
            return sb.ToString();
        }

        private TradeType? GetMLPrediction()
        {
            string url = GetUrl();
            string tradeType = _webClient.DownloadString(url);
            switch (tradeType)
            {
                case "1":
                    return TradeType.Buy;

                case "-1":
                    return TradeType.Sell;

                case "0":
                    return null;

                default:
                    throw new InvalidOperationException("Not an expected return from the ML: " + (tradeType ?? "(null)"));
            }
        }

        protected override void OnStart()
        {
            _webClient = new WebClient();
            _maxSpreadLimitAbsolute = MaxSpreadLimitInPips * Symbol.PipSize;
            _baseUrlParam = BaseUrl + "/" + Symbol.Name.ToLower() + "/" + BatchSize + "/" + WindowSize + "/" + MAPeriods + "/" + (Symbol.PipSize * Pips) + "/" + PredSize;
        }

        protected override void OnBar()
        {
            // Prevents running this in production
            if (!IsBacktesting)
            {
                return;
            }
            if ((Ask - Bid) > _maxSpreadLimitAbsolute)
            {
                return;
            }

            if (Positions.FindAll(Name, SymbolName).Any())
            {
                return;
            }

            TradeType? tradeType = GetMLPrediction();

            if (tradeType == null)
            {
                return;
            }

            ExecuteMarketOrder(tradeType.Value, Symbol.Name, Volume, Name, Pips, Pips);
        }
    }
}

 


@amusleh

thenewlearner
19 Apr 2021, 12:34

RE:

Thanks a lot

now it does not throw error..

But main issue is still present i.e. trying to load more history then available. i.e. if i do the count of hourly history bars = 128 for gbpusd,  But I require 269 bars of data is required.

I searched on the forum and it appears LoadMoreHistory does not work. 

Any suggestion

 

 

Regards

 


@thenewlearner

christoph.keller
19 Apr 2021, 13:30

RE: RE:

thenewlearner said:

Thanks a lot

now it does not throw error..

But main issue is still present i.e. trying to load more history then available. i.e. if i do the count of hourly history bars = 128 for gbpusd,  But I require 269 bars of data is required.

I searched on the forum and it appears LoadMoreHistory does not work. 

Any suggestion

 

 

Regards

 

LoadMoreHistory is not working in Backtest currently. You have to extend your backtest time-range until you have enough bars loaded.

For example:

in GetMlPrediction before you call GetUrl, add the following:

if (Bars.Count < WindowSize + MAPeriods - 1)
{
   return null;
}

With this, you check that the current Bars contains enough data.

Please remember to extend your Backtest / Optimization Time accordingly.

 

Best regards,
Chris


@christoph.keller

amusleh
19 Apr 2021, 13:38

Hi,

The LoadMoreHistory method doesn't work during back test, to solve your issue you have to check how many bars are available in Bars, and whenever the amount of bars reached your desired number you can execute your code, you can use the IsBacktesting property to run that code only when you are back testing.


@amusleh