StockSharp/StockSharp · error · ArgumentOutOfRangeException
candlesSeries
Error message
candlesSeries
What it means
The Keltner Channels GPU calculator's Calculate method throws ArgumentOutOfRangeException(nameof(candlesSeries)) when the GpuCandle[][] jagged array passed as candlesSeries contains zero series. The method flattens every series into one contiguous GPU buffer and builds a kernel grid extent of Index2D(parameters.Length, seriesCount); an empty series array produces a degenerate zero-extent grid and a meaningless output buffer. The guard fires before any GPU allocation so no device resource is leaked.
Source
Thrown at Algo.Gpu/Indicators/GpuKeltnerChannelsCalculator.cs:152
/// <summary>
/// Initializes a new instance of the <see cref="GpuKeltnerChannelsCalculator"/> class.
/// </summary>
/// <param name="context">ILGPU context.</param>
/// <param name="accelerator">ILGPU accelerator.</param>
public GpuKeltnerChannelsCalculator(Context context, Accelerator accelerator)
: base(context, accelerator)
{
_kernel = Accelerator.LoadAutoGroupedStreamKernel<Index2D, ArrayView<GpuCandle>, ArrayView<GpuKeltnerChannelsResult>, ArrayView<int>, ArrayView<int>, ArrayView<GpuKeltnerChannelsParams>>(KeltnerChannelsParamsSeriesKernel);
}
/// <inheritdoc />
public override GpuKeltnerChannelsResult[][][] Calculate(GpuCandle[][] candlesSeries, GpuKeltnerChannelsParams[] parameters)
{
ArgumentNullException.ThrowIfNull(candlesSeries);
ArgumentNullException.ThrowIfNull(parameters);
if (candlesSeries.Length == 0)
throw new ArgumentOutOfRangeException(nameof(candlesSeries));
if (parameters.Length == 0)
throw new ArgumentOutOfRangeException(nameof(parameters));
var seriesCount = candlesSeries.Length;
var totalSize = 0;
var seriesOffsets = new int[seriesCount];
var seriesLengths = new int[seriesCount];
for (var s = 0; s < seriesCount; s++)
{
seriesOffsets[s] = totalSize;
var len = candlesSeries[s]?.Length ?? 0;
seriesLengths[s] = len;
totalSize += len;
}
View on GitHub (pinned to 601a191de6)
Solutions
- Inspect the data source feeding candlesSeries before the call — log candlesSeries.Length and each inner array's length to identify where the emptiness originates.
- Guard the caller: if candlesSeries is null or has zero length, skip the Calculate call or return an empty result set rather than forwarding the empty array.
- Verify upstream data loading: confirm the historical-data provider, database query, or CSV parser actually returned rows for every requested symbol.
- If the empty case is valid (e.g., no trading data for a holiday), short-circuit early with an empty result array matching the expected return shape rather than calling Calculate.
Example fix
// before
var results = calc.Calculate(candlesSeries, parameters); // candlesSeries may be empty
// after
if (candlesSeries is null || candlesSeries.Length == 0)
return Array.Empty<GpuIndicatorResult[][]>(); // or return the appropriately-shaped empty result
var results = calc.Calculate(candlesSeries, parameters); Defensive patterns
Strategy: validation
Validate before calling
// Validate before calling Calculate
if (candlesSeries is null || candlesSeries.Length == 0)
throw new ArgumentException("candlesSeries must contain at least one series.", nameof(candlesSeries));
// Also verify each inner series is non-null and has data
for (var i = 0; i < candlesSeries.Length; i++)
{
if (candlesSeries[i] is null || candlesSeries[i].Length == 0)
throw new ArgumentException($"Series at index {i} is null or empty.", nameof(candlesSeries));
}
var results = calc.Calculate(candlesSeries, parameters); Type guard
static bool IsValidCandlesSeries(GpuCandle[][] series)
=> series is not null
&& series.Length > 0
&& series.All(s => s is not null && s.Length > 0);
// usage
if (!IsValidCandlesSeries(candlesSeries))
return EmptyResults();
var results = calc.Calculate(candlesSeries, parameters); Try / catch
// Wrap only if the empty-array case is a recoverable runtime condition
try
{
var results = calc.Calculate(candlesSeries, parameters);
// process results ...
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "candlesSeries")
{
logger.LogWarning("No candle series provided for {Name}; skipping calculation", nameof(calc));
return Array.Empty<GpuIndicatorResult[][]>();
} Prevention
- Always check candlesSeries.Length > 0 before calling Calculate — the method does not accept empty arrays.
- Log the source of each batch (symbol list, date range, provider name) so empty batches are traceable to their origin.
- Use a data-provider wrapper that guarantees non-empty arrays or returns a sentinel/None instead of forwarding empties.
- In backtest/batch loops, filter out symbols with insufficient historical data before building the candlesSeries array.
When it happens
Trigger: Calling Keltner ChannelsCalculator.Calculate(Array.Empty<GpuCandle[]>(), parameters) or passing a new GpuCandle[0][] as candlesSeries. Also triggered when candlesSeries is populated from a LINQ pipeline or data provider that yields zero elements (e.g., .Where(filter) eliminates all series, or a batch scheduler submits an empty work set).
Common situations: Market-data provider returned no bars for the requested symbol/timeframe (holiday, pre-market, or newly listed instrument). A volume or spread filter upstream stripped every series from the batch. A backtest engine iterating symbol groups hit an empty group. A configuration or scheduling layer defaulted candlesSeries to an empty collection instead of null.
Related errors
AI-assisted analysis of StockSharp/StockSharp@601a191de6 (2026-08-13).
Data as JSON: /api/errors/f0a7fef48e8f9356.
Report an issue: GitHub.