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

  1. Inspect the data source feeding candlesSeries before the call — log candlesSeries.Length and each inner array's length to identify where the emptiness originates.
  2. 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.
  3. Verify upstream data loading: confirm the historical-data provider, database query, or CSV parser actually returned rows for every requested symbol.
  4. 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

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.