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//! | Stochastic | [`StochasticParams::fast`] / [`full`](StochasticParams::full) | [`stochastics`] | [`stochastics_solution`] |
72//! | MACD | [`MacdParams::standard`] (12,26,9) | [`macd`] | [`macd_solution`] |
73//! | Bollinger | [`BollingerParams::standard`] (20,2, sample stdev) | [`bollinger`] | [`bollinger_solution`] |
74//! | Keltner | [`KeltnerParams::standard`] (20,10,2) | [`keltner`] | [`keltner_solution`] |
75//! | Donchian | [`DonchianParams::period_20`] | [`donchian`] | [`donchian_solution`] |
76//! | VWAP | [`VwapParams::cumulative_typical`] | [`vwap`] | [`vwap_solution`] |
77//! | RVOL | [`RvolParams::days_20`] | [`rvol`] | [`rvol_solution`] |
78//! | RSI | [`RsiParams::period_14`] | [`rsi`] | [`rsi_solution`] |
79//! | ATR | [`AtrParams::period_14`] | [`atr`] | [`atr_solution`] |
80//! | LinReg | [`LinRegParams::period_20`] | [`linear_regression`] | [`linear_regression_solution`] |
81//!
82//! # Incremental / streaming state (live bars)
83//!
84//! For tick/5s/1m **payloads**, use `*State` types: `push` one bar at a time, `push_bars` for
85//! multi-bar messages, or `from_history` then only push live updates. See [`state`] module docs
86//! and the README quant-engine sketch. **You** call `reset()` on VWAP when your calendar says so.
87//!
88//! **Every public TA indicator has a matching `*State`** (SMA/EMA/WMA/HMA, Stoch, MACD, BB,
89//! Keltner, Donchian, VWAP, RVOL, RSI, ATR, LinReg). Running *all* of them on one symbol each
90//! 5s bar is supported: hold one struct of states per symbol and call `push` sequentially.
91//! That is **caller-owned composition** (and multi-symbol parallelism is caller-side `rayon`).
92//! The crate does **not** ship a multi-indicator “run everything” engine.
93//!
94//! Hot-window slides (after warm-up): SMA/EMA/WMA/HMA/BB/LinReg/RSI/ATR/… are O(1);
95//! Stoch HH/LL and Donchian max/min are **amortized O(1)** via monotonic deques.
96//!
97//! Conventions worth knowing:
98//!
99//! - **Stochastic flat window** (HH == LL): carry previous raw %K, else 50.
100//! - **Bollinger stdev**: [`StdevKind::Sample`] (`n−1`) default; optional population (`n`).
101//! - **Batch = stream path:** free functions / `Validated*::compute` for SMA/EMA/WMA/HMA,
102//! Stoch, MACD, Bollinger, RVOL, VWAP, RSI, ATR, LinReg, Donchian route through the same
103//! `*State` machines as live `push` (parity by construction; amortized O(1) hot windows).
104
105pub mod atr;
106pub mod bollinger;
107pub mod common;
108pub mod donchian;
109pub mod keltner;
110pub mod linear_regression;
111pub mod macd;
112pub mod moving_average;
113#[cfg(test)]
114mod proptests;
115pub mod ring;
116pub mod rsi;
117pub mod rvol;
118pub mod state;
119pub mod stochastic;
120pub mod vwap;
121
122#[doc(inline)]
123pub use atr::*;
124#[doc(inline)]
125pub use bollinger::*;
126#[doc(inline)]
127pub use common::StdevKind;
128#[doc(inline)]
129pub use donchian::*;
130#[doc(inline)]
131pub use keltner::*;
132#[doc(inline)]
133pub use linear_regression::*;
134#[doc(inline)]
135pub use macd::*;
136#[doc(inline)]
137pub use moving_average::*;
138#[doc(inline)]
139pub use rsi::*;
140#[doc(inline)]
141pub use rvol::*;
142#[doc(inline)]
143pub use state::*;
144#[doc(inline)]
145pub use stochastic::*;
146#[doc(inline)]
147pub use vwap::*;