diff --git a/docs/wiki/indicators/moving-averages/Indicator-Sma.md b/docs/wiki/indicators/moving-averages/Indicator-Sma.md index 2e25ef1b..f828820a 100644 --- a/docs/wiki/indicators/moving-averages/Indicator-Sma.md +++ b/docs/wiki/indicators/moving-averages/Indicator-Sma.md @@ -23,7 +23,12 @@ SMA_t = (1 / n) * Σ_{i=0}^{n-1} price_{t-i} where `n = period`. Maintained incrementally as `sum -= window.pop_front(); sum += new_price; out = sum / n`, so `update` is O(1) regardless of -`period`. +`period`. To keep f64 rounding error bounded on long-running streams (where +catastrophic cancellation between add/subtract pairs could otherwise +accumulate), the running `sum` is reseeded from the live window every +`16 · period` updates — still amortised O(1) (`O(period)` work amortised +over `O(period)` updates), zero observable change on inputs that did not +drift to begin with. ## Parameters diff --git a/docs/wiki/indicators/price-statistics/Indicator-LinearRegression.md b/docs/wiki/indicators/price-statistics/Indicator-LinearRegression.md index c332a165..a5d18044 100644 --- a/docs/wiki/indicators/price-statistics/Indicator-LinearRegression.md +++ b/docs/wiki/indicators/price-statistics/Indicator-LinearRegression.md @@ -57,6 +57,16 @@ step. Because `Input = f64` it can sit inside a [`Chain`](../../Indicator-Chaini `LinearRegression::new(14).warmup_period() == 14`. The first value lands once the window holds a full `period` prices — on input index `period − 1`. +## Complexity + +Each `update` is **O(1)**: the `Σx` and `Σxx` terms depend only on `period` +and are precomputed once at construction, and `Σy` / `Σxy` are maintained +incrementally as the window slides via the closed-form identity +`new_Σxy = old_Σxy − old_Σy + popped_y₀` (then `Σxy += (n − 1) · new_value` +and `Σy += new_value`). The same applies to +[`LinRegSlope`](Indicator-LinRegSlope.md) and +[`LinRegAngle`](Indicator-LinRegAngle.md). + ## Edge cases - **`period < 2`.** Rejected at construction — a regression line is undefined @@ -65,7 +75,8 @@ the window holds a full `period` prices — on input index `period − 1`. the endpoint equals the current value (`perfect_line_returns_current_value` pins this). - **Constant series.** A flat input returns that constant. -- **Reset.** `lr.reset()` clears the rolling window. +- **Reset.** `lr.reset()` clears the rolling window and the running `Σy` / + `Σxy` accumulators. ## Examples diff --git a/docs/wiki/indicators/volatility-bands/Indicator-BollingerBands.md b/docs/wiki/indicators/volatility-bands/Indicator-BollingerBands.md index c8b4c9c5..bb163454 100644 --- a/docs/wiki/indicators/volatility-bands/Indicator-BollingerBands.md +++ b/docs/wiki/indicators/volatility-bands/Indicator-BollingerBands.md @@ -31,7 +31,11 @@ lower = mean - multiplier * stddev Wickra computes `var` from the streaming sums `Σ x` and `Σ x²` as `Σx²/n - (Σx/n)²` and clamps to `0.0` to absorb catastrophic cancellation on -near-constant inputs (`crates/wickra-core/src/indicators/bollinger.rs:82`). +near-constant inputs (`crates/wickra-core/src/indicators/bollinger.rs`). On +long-running streams the running `Σ x` and `Σ x²` are reseeded from the live +window every `16 · period` updates — amortised O(1), bounds the cancellation +drift to roughly `16 · period · ULP · max(|x|²)` (sub-picodollar on +real-world price scales). ## Parameters