Skip to main content

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)–O(window) 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//! Conventions worth knowing:
89//!
90//! - **Stochastic flat window** (HH == LL): carry previous raw %K, else 50.
91//! - **Bollinger stdev**: [`StdevKind::Sample`] (`n−1`) default; optional population (`n`).
92//! - **SMA/EMA batch** shares code with [`SmaState`] / [`EmaState`] (parity by construction).
93
94pub mod atr;
95pub mod bollinger;
96pub mod common;
97pub mod donchian;
98pub mod keltner;
99pub mod linear_regression;
100pub mod macd;
101pub mod moving_average;
102#[cfg(test)]
103mod proptests;
104pub mod ring;
105pub mod rsi;
106pub mod rvol;
107pub mod state;
108pub mod stochastic;
109pub mod vwap;
110
111#[doc(inline)]
112pub use atr::*;
113#[doc(inline)]
114pub use bollinger::*;
115#[doc(inline)]
116pub use common::StdevKind;
117#[doc(inline)]
118pub use donchian::*;
119#[doc(inline)]
120pub use keltner::*;
121#[doc(inline)]
122pub use linear_regression::*;
123#[doc(inline)]
124pub use macd::*;
125#[doc(inline)]
126pub use moving_average::*;
127#[doc(inline)]
128pub use rsi::*;
129#[doc(inline)]
130pub use rvol::*;
131#[doc(inline)]
132pub use state::*;
133#[doc(inline)]
134pub use stochastic::*;
135#[doc(inline)]
136pub use vwap::*;