finance_solution/stocks/ta/mod.rs
1//! Technical analysis indicators on price / volume series.
2//!
3//! **Scope of this module:** pure **batch** building blocks a quant *engine* or notebook
4//! consumes. This crate does **not** run an event loop, subscribe to market data, or own
5//! portfolio state. See the crate README (“Quant pattern” and “Why not a streaming engine?”).
6//!
7//! # Layers (performance)
8//!
9//! | Layer | API | Cost class | Use |
10//! |-------|-----|------------|-----|
11//! | **Config** | `*Params` / `Validated*` (`Copy`) | O(1) validate once | Build at startup / `const` |
12//! | **Hot path (batch)** | `sma`, `ema`, `stochastics`, `macd`, … | O(n) pure math, no `String` | Research, backtests |
13//! | **Hot path (live)** | `SmaState` / `StochState` / … `push` / `push_bars` | O(1) / amortized O(1) per bar | Streaming payloads |
14//! | **Solution** | `*_solution` | O(n) + formulas + tables | Teaching, audit, observability |
15//!
16//! # Quant ergonomics — recommended pattern (`const` + validate + `.compute`)
17//!
18//! Production code should **not** invent a new parameter list on every bar. Define the
19//! indicator variation once, validate once, reuse forever:
20//!
21//! ```
22//! use finance_solution::stocks::ta::{
23//! StochasticParams, ValidatedStochastic,
24//! MacdParams, ValidatedMacd,
25//! BollingerParams, ValidatedBollinger,
26//! };
27//!
28//! // --- Strategy knobs (module-level const packs) ---
29//! const FAST_STOCH_9_3: StochasticParams = StochasticParams::fast(9, 3);
30//! const MACD_12_26_9: MacdParams = MacdParams::standard();
31//! const BB_20_2: BollingerParams = BollingerParams::standard();
32//!
33//! // --- Startup: O(1) validation ---
34//! let stoch = ValidatedStochastic::new(FAST_STOCH_9_3).unwrap();
35//! let macd_eng = ValidatedMacd::new(MACD_12_26_9).unwrap();
36//! let bb = ValidatedBollinger::new(BB_20_2).unwrap();
37//!
38//! // --- Hot path: many symbols / many days ---
39//! # let high = [11.0_f64, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0];
40//! # let low = [10.0, 10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5];
41//! # let close= [10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5];
42//! # let closes: Vec<f64> = (1..=40).map(|x| 100.0 + x as f64 * 0.1).collect();
43//! let kd = stoch.compute(&high, &low, &close).unwrap();
44//! let m = macd_eng.compute(&closes).unwrap();
45//! let bands = bb.compute(&closes).unwrap();
46//! assert_eq!(kd.k.len(), high.len());
47//! assert_eq!(m.macd.len(), closes.len());
48//! assert_eq!(bands.middle.len(), closes.len());
49//! ```
50//!
51//! **Why this shape?**
52//!
53//! 1. **Clarity** — `FAST_STOCH_9_3` documents the strategy; no magic positional args.
54//! 2. **Safety** — period=0 fails at `new`, not mid-batch.
55//! 3. **Speed** — validation is noise vs O(n) windows (see Criterion suite D).
56//! 4. **Variations** — Fast(9,3), Full(14,3,3), MACD(8,17,9) are just different `const` packs
57//! on the **same** functions — no combinatorial API explosion.
58//!
59//! Free functions (`stochastics(...)`, `macd(...)`, …) remain for scripts and doctests.
60//!
61//! # Warm-up policy
62//!
63//! Output length equals input length. Bars before a window is full are [`None`].
64//! Solution tables print warm-up as `n/a`.
65//!
66//! # Indicators
67//!
68//! | Indicator | Params / presets | Series | Solution + table |
69//! |-----------|------------------|--------|------------------|
70//! | SMA / EMA / WMA / HMA | `period` | [`sma`], [`ema`], [`wma`], [`hma`] (+ `*State`) | — |
71//! | RMA / DEMA / TEMA / KAMA | period / [`KamaParams`] | [`rma`], [`dema`], [`tema`], [`kama`] | — |
72//! | Stochastic | [`StochasticParams::fast`] / [`full`](StochasticParams::full) | [`stochastics`] | [`stochastics_solution`] |
73//! | MACD | [`MacdParams::standard`] (12,26,9) | [`macd`] | [`macd_solution`] |
74//! | Bollinger | [`BollingerParams::standard`] (20,2, sample stdev) | [`bollinger`] | [`bollinger_solution`] |
75//! | Keltner | [`KeltnerParams::standard`] (20,10,2) | [`keltner`] | [`keltner_solution`] |
76//! | Donchian | [`DonchianParams::period_20`] | [`donchian`] | [`donchian_solution`] |
77//! | VWAP | [`VwapParams::cumulative_typical`] | [`vwap`] | [`vwap_solution`] |
78//! | RVOL | [`RvolParams::days_20`] | [`rvol`] | [`rvol_solution`] |
79//! | RSI | [`RsiParams::period_14`] | [`rsi`] | [`rsi_solution`] |
80//! | ATR | [`AtrParams::period_14`] | [`atr`] | [`atr_solution`] |
81//! | LinReg | [`LinRegParams::period_20`] | [`linear_regression`] | [`linear_regression_solution`] |
82//! | Williams %R | [`WillrParams::period_14`] | [`willr`] | [`willr_solution`] |
83//! | OBV | [`ObvParams::default_pack`] | [`obv`] | [`obv_solution`] |
84//! | CCI | [`CciParams::period_20`] | [`cci`] | [`cci_solution`] |
85//! | ADX / DI / DX | [`AdxParams::period_14`] | [`adx`] | [`adx_solution`] |
86//! | MOM / ROC / ROCP | [`MomParams::period_10`] | [`mom`], [`roc`], [`rocp`] | [`mom_solution`] |
87//! | MFI | [`MfiParams::period_14`] | [`mfi`] | [`mfi_solution`] |
88//! | Supertrend | [`SupertrendParams::standard`] | [`supertrend`] | [`supertrend_solution`] |
89//! | Parabolic SAR | [`SarParams::standard`] | [`sar`] | [`sar_solution`] |
90//! | TR / NATR | [`true_range_series`], [`natr`] | | — / via ATR |
91//!
92//! # Incremental / streaming state (live bars)
93//!
94//! For tick/5s/1m **payloads**, use `*State` types: `push` one bar at a time, `push_bars` for
95//! multi-bar messages, or `from_history` then only push live updates. See [`state`] module docs
96//! and the README quant-engine sketch. **You** call `reset()` on VWAP when your calendar says so.
97//!
98//! **Every public TA indicator has a matching `*State`**. Running *all* of them on one symbol
99//! each 5s bar is supported: hold one struct of states per symbol and call `push` sequentially.
100//! That is **caller-owned composition** (and multi-symbol parallelism is caller-side `rayon`).
101//! The crate does **not** ship a multi-indicator “run everything” engine.
102//!
103//! Hot-window slides (after warm-up): SMA/EMA/WMA/HMA/BB/LinReg/RSI/ATR/OBV/ADX/… are O(1);
104//! Stoch/WillR HH/LL and Donchian max/min are **amortized O(1)** via monotonic deques.
105//!
106//! Conventions worth knowing:
107//!
108//! - **Stochastic flat window** (HH == LL): carry previous raw %K, else 50.
109//! - **Williams %R flat window**: carry previous %R, else −50.
110//! - **Bollinger stdev**: [`StdevKind::Sample`] (`n−1`) default; optional population (`n`).
111//! - **Batch = stream path:** free functions / `Validated*::compute` route through the same
112//! `*State` machines as live `push` (parity by construction).
113
114pub mod advanced_ma;
115pub mod adx;
116pub mod atr;
117pub mod bollinger;
118pub mod cci;
119pub mod common;
120pub mod donchian;
121pub mod keltner;
122pub mod linear_regression;
123pub mod macd;
124pub mod mfi;
125pub mod momentum;
126pub mod moving_average;
127pub mod obv;
128#[cfg(test)]
129mod proptests;
130pub mod ring;
131pub mod rsi;
132pub mod rvol;
133pub mod sar;
134pub mod state;
135pub mod stochastic;
136pub mod supertrend;
137pub mod vwap;
138pub mod willr;
139
140#[doc(inline)]
141pub use advanced_ma::*;
142#[doc(inline)]
143pub use adx::*;
144#[doc(inline)]
145pub use atr::*;
146#[doc(inline)]
147pub use bollinger::*;
148#[doc(inline)]
149pub use cci::*;
150#[doc(inline)]
151pub use common::StdevKind;
152#[doc(inline)]
153pub use donchian::*;
154#[doc(inline)]
155pub use keltner::*;
156#[doc(inline)]
157pub use linear_regression::*;
158#[doc(inline)]
159pub use macd::*;
160#[doc(inline)]
161pub use mfi::*;
162#[doc(inline)]
163pub use momentum::*;
164#[doc(inline)]
165pub use moving_average::*;
166#[doc(inline)]
167pub use obv::*;
168#[doc(inline)]
169pub use rsi::*;
170#[doc(inline)]
171pub use rvol::*;
172#[doc(inline)]
173pub use sar::*;
174#[doc(inline)]
175pub use state::*;
176#[doc(inline)]
177pub use stochastic::*;
178#[doc(inline)]
179pub use supertrend::*;
180#[doc(inline)]
181pub use vwap::*;
182#[doc(inline)]
183pub use willr::*;