--- name: indicator-series description: Implement Series-style batch indicators (`To{Name}()` in `{Name}.Series.cs`), the canonical numerical reference every BufferList and StreamHub must match exactly. Owns the full per-indicator file set and the completion checklist across all styles, tests, catalog, docs, regression baseline, and benchmark. Use when adding a new indicator, creating or editing a `src/Indicators/**/{Name}.Series.cs`, `{Name}Result.cs`, or `{Name}.Utilities.cs` file, choosing a result or input interface, optimizing a Series calculation, or checking whether an indicator is complete. --- # Series indicator development Series results are the source of truth. A BufferList or StreamHub that disagrees with Series is wrong unless the Series result is proven wrong against reference data. Load [result interface selection](references/decision-tree.md) before writing a `{Name}Result.cs` record or choosing the `this` parameter type of `To{Name}()`. ## Per-indicator file set Source files live in `src/Indicators/{folder}/{Name}/`, where `{folder}` is the alphabetical bucket `a-b`, `c-d`, `e-j`, `k-q`, `r-s`, or `t-z`: | File | Contents | | ---- | -------- | | `{Name}.Series.cs` | `public static partial class {Name}` with the `To{Name}()` extension | | `{Name}List.cs` | `{Name}List` BufferList class plus the `To{Name}List()` extension | | `{Name}Hub.cs` | `{Name}Hub` class with an `internal` constructor plus the `To{Name}Hub()` extension | | `{Name}.Catalog.cs` | `CommonListing`, `SeriesListing`, `BufferListing`, `StreamListing` | | `{Name}Result.cs` | Positional result `record` | | `{Name}.Utilities.cs` | `internal static void Validate(...)`, any shared `Increment(...)` kernel, and any `RemoveWarmupPeriods()` overload | | `I{Name}.cs` | Public interface of configuration properties (`LookbackPeriods`, derived constants such as `K`); never result properties | Add a `RemoveWarmupPeriods()` overload only when values keep converging after the first calculated one (see `Ema.Utilities.cs`) or the result is not `IReusable`; otherwise the generic `PruningExtensions.RemoveWarmupPeriods()` covers it. Tests live in `tests/Library/Indicators/{folder}/{Name}/`: `{Name}SeriesTests.cs`, `{Name}BufferListTests.cs`, `{Name}HubTests.cs`, `{Name}CatalogTests.cs`, `{Name}RegressionTests.cs`, and the `{Name}.Calc.xlsx` spreadsheet holding the manually calculated reference values. ## Completion checklist An indicator is complete when every item holds. Series-only indicators skip the List, Hub, and their tests, listings, and benchmarks. - [ ] Every applicable source file in the table above exists (Series-only indicators omit `{Name}List.cs` and `{Name}Hub.cs`); the indicator-buffer and indicator-stream skills own the List and Hub specifics. - [ ] `{Name}SeriesTests` inherits `StaticSeriesTestBase` and asserts spot values from `{Name}.Calc.xlsx`; assert a documented value range with `IsBetween` where the indicator has one. The testing-standards skill owns base classes, required methods, and precision constants. - [ ] `{Name}BufferListTests` and `{Name}HubTests` assert `IsExactly` parity with the Series output. - [ ] Catalog listings are registered in `src/Common/Catalog/Catalog.Listings.cs`, the catalog shape snapshot is regenerated, and `{Name}CatalogTests` covers each listing; the indicator-catalog skill owns listing rules, registration order, and the catalog shape snapshot. - [ ] `tests/Library/TestData/results/{uiid-lowercase}.standard.json` exists, generated by `dotnet run --project tools/baselining -- --indicator {UIID}`. - [ ] `{Name}RegressionTests` inherits `RegressionTestBase`, carries `[TestClass, TestCategory("Regression")]`, passes the baseline filename to the base constructor, and calls each style with the catalog default parameters, because the baseline is generated from those defaults. - [ ] Benchmarks exist in `tools/performance/Perf.Series.cs`, `Perf.Buffer.cs`, and `Perf.Stream.cs`; the performance-testing skill owns their shape. - [ ] `docs/indicators/{kebab-name}.md` exists or is updated; the documentation skill owns page structure. - [ ] `docs/migration/v3.md` is updated when the indicator existed in v2 and its API or results changed. ## Implementation rules - Validate arguments first: `ArgumentNullException.ThrowIfNull(source)`, then `Validate(...)`, which throws `ArgumentOutOfRangeException` naming the parameter and offending value. - Calculate in `double`, use `double.NaN` for incalculable internal state, and convert with `.NaN2Null()` only when constructing the result. - Accept NaN inputs and let them propagate. Guard every variable denominator with `denom != 0 ? num / denom : double.NaN`, comparing to exactly zero. - Keep the calculation single-pass O(n) and allocate only the result array plus minimal working buffers. - When the List or Hub repeats the per-step math, put it in an `Increment(...)` kernel in `{Name}.Utilities.cs` and call it from every style instead of re-deriving the formula (see `Ema.Increment`). When the result count equals the input count, fill a preallocated array, then copy it into the returned list: ```csharp TResult[] results = new TResult[length]; // results[i] = new TResult(...); return new List(results); ``` Benchmark this against `List.Add()` before committing to it; some indicators, such as ADL, are faster with `Add()`. ## Reference examples - Single value, chainable: `src/Indicators/r-s/Sma/Sma.Series.cs` - Exponential smoothing with an SMA seed: `src/Indicators/e-j/Ema/Ema.Series.cs` - Multi-stage from bars: `src/Indicators/a-b/Adx/Adx.Series.cs` - Multi-value `ISeries` result: `src/Indicators/a-b/Alligator/Alligator.Series.cs` ## Do not do these - Do not verify a formula against anything but an authoritative reference publication. - Do not change a test's expected value or loosen its precision constant to make a failing calculation pass; fix the formula. - Do not reject or filter NaN inputs; downstream chaining depends on propagation. - Do not use epsilon comparisons for zero checks. - Do not fix a BufferList or StreamHub mismatch by changing Series unless the Series value is shown wrong against `{Name}.Calc.xlsx` or the reference publication.