Quick Answer
A cBot that builds without errors and then stops while it runs, with a log line about an index being out of range, asked for an item that does not exist: bar number 50 when the chart has only 30 bars, the first position when there are no positions, or the 11th element of a list that holds 10. This is a runtime error, so the compiler cannot warn you. The cBot works in one test and crashes in another, because the number of bars, positions or list items is different.
The fix is always the same idea: check the count before you read an item. Check Bars.Count before you look back n bars, check Positions.Count before you read the first position, and use loop limits that stop before the last index. The sections below show each case with the guard that prevents it.
From the author: I build cBots in cTrader Automate and convert strategies from Pine Script and MQL5 to C#. In Pine Script,
close[200]on a short chart simply givesna. In MQL5, an array access out of range stops the EA with its own message (MQL5 array out of range). In a cBot the same mistake stops the bot too, and this guide shows where it hides.
Key Facts
| Where it happens | Usual cause | Guard |
|---|---|---|
| Bars.ClosePrices.Last(n) or Bars.ClosePrices[i] | n or i is larger than the number of bars | Check Bars.Count first |
| Positions[0] | There is no position | Check Positions.Count > 0 |
| for (i = 0; i <= list.Count; i++) | <= instead of < | Loop while i < list.Count |
| An indicator value at the start | Not enough bars for the indicator period | Skip until Bars.Count is large enough |
| array[index] with a computed index | The index is negative or too large | Check index >= 0 && index < array.Length |
| A string split or text parse | The text has fewer parts than expected | Check the length of the result |
What You See
When a handler such as OnBar or OnTick throws an exception that the cBot does not catch, cTrader stops the cBot, and the log shows the name of the handler and the exception. The exact wording depends on the cTrader version and on the kind of collection, so look for text such as IndexOutOfRangeException, ArgumentOutOfRangeException or Index was out of range next to the handler name. The error is also easy to reproduce: run the cBot on a chart with few bars, or with no open position, and it fails.
The Broken cBot
This cBot reads values from the past and from the open positions, and prints them. It does not place trades. Each marked line can crash it:
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class BrokenBot : Robot
{
[Parameter("Lookback", DefaultValue = 200, MinValue = 1)]
public int Lookback { get; set; }
protected override void OnBar()
{
// 1. Looks back Lookback bars without checking that they exist
double oldClose = Bars.ClosePrices.Last(Lookback);
// 2. Reads the first position without checking that one exists
double firstPips = Positions[0].Pips;
// 3. A loop that goes one step too far
double sum = 0;
for (int i = 0; i <= Positions.Count; i++)
sum += Positions[i].Pips;
Print("Old close {0}, first pips {1}, sum {2}", oldClose, firstPips, sum);
}
}
}
The first line fails on a chart that has fewer than about 200 bars, the second when there is no position, and the loop always fails: the last index it uses, Positions.Count, does not exist.
Cause 1: Looking Back Further Than the Chart
Last(n) counts back from the last bar. If you ask for more bars than the series has, there is nothing to read. This also happens at the start of a test or of a live run, when only a few bars are loaded, and when the user changes the lookback parameter to a larger value:
if (Bars.Count <= Lookback + 1)
return; // not enough history yet
double oldClose = Bars.ClosePrices.Last(Lookback);
An indicator has the same limit. It needs at least its period of bars before its first value is meaningful, and earlier values can be NaN. Check both: enough bars, and double.IsNaN(value) for an indicator. The Last(1) idea and the closed bar are explained in Pine Script strategy.entry to cTrader C#.
The most robust place for the check is the top of the handler, so that every line below it can rely on the history.
Cause 2: Reading a Position That Is Not There
Positions[0] assumes that at least one position exists. A cBot that waits for a signal does not have a position most of the time:
if (Positions.Count > 0)
{
double firstPips = Positions[0].Pips;
}
Often you do not want "the first position" at all, but "my position on this symbol". Filter by the symbol and the label, and handle the empty case:
using System.Linq;
var mine = Positions.Where(p => p.SymbolName == SymbolName && p.Label == "MyBot").ToList();
if (mine.Count > 0)
{
double pips = mine[0].Pips;
}
This needs using System.Linq;, and without it the error is CS1061 (cTrader C# error CS1061). The filter by label is the cTrader version of the magic number idea in MQL5 filter positions by magic number.
Cause 3: A Loop Limit One Too High
Indexes start at 0, so a collection with Count items has the indexes 0 to Count - 1. A loop with <= goes one step too far:
for (int i = 0; i < Positions.Count; i++) // < and not <=
sum += Positions[i].Pips;
A foreach loop avoids the problem, because it has no index:
foreach (var position in Positions)
sum += position.Pips;
If you close positions inside the loop, do not loop forward over the collection you change, because items move while you work. Copy the items first (.ToList()), or loop backwards.
Cause 4: A Computed Index
When the index comes from a formula (a bar number from a time, a position in a list from a search), it can be negative or too large. Check it before you use it:
int index = Bars.OpenTimes.GetIndexByTime(time);
if (index < 0 || index >= Bars.Count)
return;
double close = Bars.ClosePrices[index];
GetIndexByTime is a method of the time series. Check in the code completion of your cTrader version that it exists with this name before you use it, and read what it returns for a time that is not in the chart.
Cause 5: Arrays and Lists Built From Parameters
A cBot that keeps a list of levels, or an array of the last n values, can run past its end when a parameter changes the size:
double[] levels = new double[3];
for (int i = 0; i < Count; i++) // Count is a parameter: can be larger than 3
levels[i] = Bars.ClosePrices.Last(i + 1);
Use the real length in the loop (levels.Length), or size the array from the parameter, and give the parameter a MinValue and MaxValue so that a user cannot set a value that the code does not support.
A Safe Pattern for Every Handler
A handler that always starts with the same guards is easy to read and hard to crash:
protected override void OnBar()
{
if (Bars.Count <= Lookback + 2)
return;
double lastClose = Bars.ClosePrices.Last(1);
double oldClose = Bars.ClosePrices.Last(Lookback);
if (double.IsNaN(lastClose) || double.IsNaN(oldClose))
return;
// ... the rest of the logic ...
}
If you want the cBot to survive an unexpected exception, wrap the body of the handler in a try/catch that prints the message and returns. This keeps the cBot running, but it can also hide a real bug, so print the full message and look at it:
try
{
// handler body
}
catch (Exception ex)
{
Print("Error in OnBar: {0}", ex.Message);
}
This needs using System;. A catch is a safety net and not a replacement for the guards above.
The Fixed cBot
using System.Linq;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class FixedBot : Robot
{
private const string Label = "FixedBot";
[Parameter("Lookback", DefaultValue = 200, MinValue = 1, MaxValue = 1000)]
public int Lookback { get; set; }
protected override void OnBar()
{
if (Bars.Count <= Lookback + 2)
{
Print("Waiting for history: {0} bars, need {1}", Bars.Count, Lookback + 2);
return;
}
double oldClose = Bars.ClosePrices.Last(Lookback);
if (double.IsNaN(oldClose))
return;
var mine = Positions.Where(p => p.SymbolName == SymbolName && p.Label == Label).ToList();
double firstPips = mine.Count > 0 ? mine[0].Pips : 0.0;
double sum = 0;
foreach (var position in mine)
sum += position.Pips;
Print("Old close {0}, first pips {1}, sum {2}, positions {3}",
oldClose, firstPips, sum, mine.Count);
}
}
}
The bot only prints. Run it on a chart with few bars, and on a chart without positions, to see that it waits instead of crashing.
Checklist: What to Check When a cBot Stops With an Index Error
- Find the handler and the line in the log. Which collection does the line read?
- Count the items. How many bars, positions or list items exist at that moment?
- Add a guard before the read:
Bars.Count,Positions.Countor a length check. - Check the loop limits:
<and not<=, and aforeachwhere you can. - Check
NaNfor indicator values at the start of the chart. - Limit the parameters with
MinValueandMaxValue. - Test the edge cases: a short chart, no positions, one position, many positions.
- Test on a demo account before the cBot trades with real money.
Common Mistakes
Positions[0]without a count check.- A lookback that is larger than the loaded history.
<=in a loop limit.- Closing positions inside a forward loop over the same collection.
- Catching every exception and hiding the real bug.
- Testing only on a long chart that has plenty of history.
FAQ
Related Guides
- MQL5 array out of range error: the MetaEditor version of the same mistake.
- cTrader C# error CS1061: the object is found, the member is not.
- cTrader C# error CS0103: the name does not exist in the current context.
- cTrader C# error CS1503: an argument of the wrong type in a call.
- Pine Script strategy.entry to cTrader C#: closed-bar values and position checks.
- Pine Script close[1] in MQL5: how a lookback maps to code.
- What to check in a trading bot before going live: a pre-live checklist for stops, lot size, spread and trade results.
- cTrader C# error CS0029: a type error that stops the build, with the
TradeResultcase.
Fix Compile Errors Without Guessing
If a cBot or an Expert Advisor does not build, paste the code and the error message into the CodeFlowOS Error Fixer. It suggests a minimal fix, explains each error and shows what changed. A fix uses 1 credit, with up to 3 attempts included, and the compiler check is optional. Outputs are not guaranteed, and code that compiles can still crash or trade wrongly at run time, so always review the result. Before a bot goes live, the Code Review is free during the early-bird period. CodeFlowOS also converts strategies between Pine Script, MQL5 and cTrader C#: try the converter page.
This article is educational. It does not describe trading performance and is not financial advice. Always build the code in cTrader Automate and backtest it before using it on a live account.