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
//! 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)–O(window) 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.
//!
//! Conventions worth knowing:
//!
//! - **Stochastic flat window** (HH == LL): carry previous raw %K, else 50.
//! - **Bollinger stdev**: [`StdevKind::Sample`] (`n−1`) default; optional population (`n`).
//! - **SMA/EMA batch** shares code with [`SmaState`] / [`EmaState`] (parity by construction).
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 *;