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