Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index
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);
}
Replies
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
amusleh
18 Apr 2021, 09:21
This line is the issue:
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