{"record":{"id":"8d62f3d34ac5f109","repo":"StockSharp/StockSharp","slug":"candlesseries-8d62f3","errorCode":null,"errorMessage":"candlesSeries","messagePattern":"candlesSeries","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"Algo.Gpu/Indicators/GpuEnvelopeCalculator.cs","lineNumber":135,"sourceCode":"\t/// Initializes a new instance of the <see cref=\"GpuEnvelopeCalculator\"/> class.\n\t/// </summary>\n\t/// <param name=\"context\">ILGPU context.</param>\n\t/// <param name=\"accelerator\">ILGPU accelerator.</param>\n\tpublic GpuEnvelopeCalculator(Context context, Accelerator accelerator)\n\t\t: base(context, accelerator)\n\t{\n\t\t_paramsSeriesKernel = Accelerator.LoadAutoGroupedStreamKernel\n\t\t\t<Index3D, ArrayView<GpuCandle>, ArrayView<GpuEnvelopeResult>, ArrayView<int>, ArrayView<int>, ArrayView<GpuEnvelopeParams>>(EnvelopeParamsSeriesKernel);\n\t}\n\n\t/// <inheritdoc />\n\tpublic override GpuEnvelopeResult[][][] Calculate(GpuCandle[][] candlesSeries, GpuEnvelopeParams[] parameters)\n\t{\n\t\tArgumentNullException.ThrowIfNull(candlesSeries);\n\t\tArgumentNullException.ThrowIfNull(parameters);\n\n\t\tif (candlesSeries.Length == 0)\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(candlesSeries));\n\n\t\tif (parameters.Length == 0)\n\t\t\tthrow new ArgumentOutOfRangeException(nameof(parameters));\n\n\t\tvar seriesCount = candlesSeries.Length;\n\n\t\t// Flatten input\n\t\tvar totalSize = 0;\n\t\tvar seriesOffsets = new int[seriesCount];\n\t\tvar seriesLengths = new int[seriesCount];\n\n\t\tfor (var s = 0; s < seriesCount; s++)\n\t\t{\n\t\t\tseriesOffsets[s] = totalSize;\n\t\t\tvar len = candlesSeries[s]?.Length ?? 0;\n\t\t\tseriesLengths[s] = len;\n\t\t\ttotalSize += len;\n\t\t}","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/StockSharp/StockSharp/blob/601a191de678bff83da28b14828f8885214ca71c/Algo.Gpu/Indicators/GpuEnvelopeCalculator.cs#L117-L153","documentation":"The Envelope calculator computes upper and lower price bands set at a fixed percentage offset from a moving average. Its Calculate method dispatches a 3DD ILGPU kernel over a series x parameters x candles grid, requiring a pre-computed totalSize (the flattened candle count across all sub-arrays) to size GPU memory buffers. An empty candlesSeries (outer Length == 0) keeps totalSize at 0, producing zero-length buffers and a degenerate kernel grid; ArgumentOutOfRangeException(nameof(candlesSeries)) fires at method entry to abort before any GPU allocation or dispatch.","triggerScenarios":"Calling Calculate with a GpuCandle[][] whose outer Length is 0 (e.g., calculator.Calculate(Array.Empty<GpuCandle[]>(), parameters)). Also triggered when a LINQ pipeline, API deserializer, or database query returns an empty collection that is forwarded to Calculate without checking the element count.","commonSituations":"Newly listed symbols with no historical candle data in the data store; a date-range filter that excludes all available candles (weekends, holidays, future dates); a backfill job that failed silently or has not completed; a REST API or database query returning zero rows forwarded without a count check; a test fixture that constructed the calculator but forgot to populate candle data.","solutions":["Guard the call site: check candlesSeries.Length > 0 before invoking Calculate and return an empty GpuEnvelopeResult[][][] or log a warning instead of letting it throw.","Verify the upstream data provider (REST API, database, file cache) actually returned rows; log the series count at the data boundary.","If building the series from multiple sources or filters, ensure at least one sub-array with candles survives deduplication and date-range filtering.","For batch pipelines, skip the calculator for symbols flagged as having insufficient data rather than passing an empty array through."],"exampleFix":"// before -- throws ArgumentOutOfRangeException (ParamName: candlesSeries)\nvar results = calculator.Calculate(candlesSeries, parameters);\n\n// after -- guard before calling\nif (candlesSeries is null || candlesSeries.Length == 0)\n{\n    logger.LogWarning(\"GpuEnvelopeCalculator: no candle series, skipping\");\n    return Array.Empty<GpuEnvelopeResult[][][]>();\n}\nvar results = calculator.Calculate(candlesSeries, parameters);","handlingStrategy":"validation","validationCode":"// Pre-call validation for GpuEnvelopeCalculator.Calculate\nif (candlesSeries is null || candlesSeries.Length == 0)\n{\n    logger.LogWarning(\"GpuEnvelopeCalculator: candle series is null or empty\");\n    return Array.Empty<GpuEnvelopeResult[][][]>();\n}","typeGuard":"static bool HasCandleSeries(GpuCandle[][]? candlesSeries) =>\n    candlesSeries is { Length: > 0 };","tryCatchPattern":"GpuEnvelopeResult[][][] results;\ntry\n{\n    results = calculator.Calculate(candlesSeries, parameters);\n}\ncatch (ArgumentOutOfRangeException ex) when (ex.ParamName == \"candlesSeries\")\n{\n    logger.LogWarning(\"GpuEnvelopeCalculator: candle series was empty, returning empty results\");\n    results = Array.Empty<GpuEnvelopeResult[][][]>();\n}","preventionTips":["Null- and empty-check every jagged array before passing it to a GPU calculator.","Log element counts at each data boundary (API, cache, file) to catch upstream gaps early.","Skip calculation for symbols flagged as having insufficient data instead of passing an empty array through.","Write edge-case unit tests that pass empty and single-element series to verify guard behavior."],"tags":["gpu","argument-validation","candle-data","ilgpu","envelope"],"backgroundTag":null,"analyzedSha":"601a191de678bff83da28b14828f8885214ca71c","analyzedAt":"2026-08-13T20:43:24.460Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}