Skip to main content

indicators/
indicator_config.rs

1//! `IndicatorConfig` — the indicator-math subset of `BotSettings`.
2//!
3//! Pure configuration: no I/O, no runtime, no exchange types.
4//! This struct is what `compute_signal` and all indicator constructors need.
5//!
6
7use serde::{Deserialize, Serialize};
8
9/// All tunable parameters that live inside indicators and `compute_signal`.
10/// Every field maps 1-to-1 to a key in the Python `SETTINGS` dict so
11/// Optuna-tuned JSON files load with zero field renaming.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct IndicatorConfig {
14    // ── Engine Buffer ────────────────────────────────────────────────────────
15    /// Candle buffer capacity
16    pub history_candles: usize,
17
18    // ── Layer 1-2: VWAP & EMA ────────────────────────────────────────────────
19    pub ema_len: usize,
20
21    // ── Layer 3: ML SuperTrend ───────────────────────────────────────────────
22    pub atr_len: usize,
23    pub st_factor: f64,
24    pub training_period: usize,
25    pub highvol_pct: f64,
26    pub midvol_pct: f64,
27    pub lowvol_pct: f64,
28
29    // ── Layer 4: Trend Speed ─────────────────────────────────────────────────
30    pub ts_max_length: usize,
31    pub ts_accel_mult: f64,
32    pub ts_rma_len: usize,
33    pub ts_hma_len: usize,
34    pub ts_collen: usize,
35    pub ts_lookback: usize,
36    pub ts_speed_exit_threshold: Option<f64>,
37
38    // ── Layer 5: Liquidity Profile ───────────────────────────────────────────
39    pub liq_period: usize,
40    pub liq_bins: usize,
41
42    // ── Layer 6: Confluence ──────────────────────────────────────────────────
43    pub conf_ema_fast: usize,
44    pub conf_ema_slow: usize,
45    pub conf_ema_trend: usize,
46    pub conf_rsi_len: usize,
47    pub conf_adx_len: usize,
48    pub conf_min_score: f64,
49
50    // ── Layer 7: Market Structure + Fibonacci ────────────────────────────────
51    pub struct_swing_len: usize,
52    pub struct_atr_mult: f64,
53    pub fib_zone_enabled: bool,
54
55    // ── Signal mode ──────────────────────────────────────────────────────────
56    /// `"majority"` | `"strict"` | `"any"`
57    pub signal_mode: String,
58    pub signal_confirm_bars: usize,
59
60    // ── Layer 8: CVD ─────────────────────────────────────────────────────────
61    pub cvd_slope_bars: usize,
62    pub cvd_div_lookback: usize,
63
64    // ── Layer 9: AO + Percentile gates ──────────────────────────────────────
65    pub wave_pct_l: f64,
66    pub wave_pct_s: f64,
67    pub mom_pct_min: f64,
68    pub vol_pct_window: usize,
69
70    // ── Layer 10: Hurst ──────────────────────────────────────────────────────
71    pub hurst_threshold: f64,
72    pub hurst_lookback: usize,
73
74    // ── Layer 11: Price Acceleration / Stop ──────────────────────────────────
75    pub stop_atr_mult: f64,
76
77    // ── Entry gates ──────────────────────────────────────────────────────────
78    pub min_vol_pct: f64,
79    pub min_hold_candles: usize,
80}
81
82impl Default for IndicatorConfig {
83    fn default() -> Self {
84        Self {
85            history_candles: 200,
86            ema_len: 9,
87            atr_len: 10,
88            st_factor: 3.0,
89            training_period: 100,
90            highvol_pct: 0.75,
91            midvol_pct: 0.50,
92            lowvol_pct: 0.25,
93            ts_max_length: 50,
94            ts_accel_mult: 5.0,
95            ts_rma_len: 10,
96            ts_hma_len: 5,
97            ts_collen: 100,
98            ts_lookback: 50,
99            ts_speed_exit_threshold: None,
100            liq_period: 100,
101            liq_bins: 31,
102            conf_ema_fast: 9,
103            conf_ema_slow: 21,
104            conf_ema_trend: 55,
105            conf_rsi_len: 13,
106            conf_adx_len: 14,
107            conf_min_score: 5.0,
108            struct_swing_len: 10,
109            struct_atr_mult: 0.5,
110            fib_zone_enabled: true,
111            signal_mode: "majority".into(),
112            signal_confirm_bars: 2,
113            cvd_slope_bars: 10,
114            cvd_div_lookback: 30,
115            wave_pct_l: 0.25,
116            wave_pct_s: 0.75,
117            mom_pct_min: 0.30,
118            vol_pct_window: 200,
119            hurst_threshold: 0.52,
120            hurst_lookback: 20,
121            stop_atr_mult: 1.5,
122            min_vol_pct: 0.20,
123            min_hold_candles: 2,
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn default_signal_mode_is_majority() {
134        assert_eq!(IndicatorConfig::default().signal_mode, "majority");
135    }
136
137    #[test]
138    fn serde_round_trip() {
139        let cfg = IndicatorConfig::default();
140        let json = serde_json::to_string(&cfg).unwrap();
141        let back: IndicatorConfig = serde_json::from_str(&json).unwrap();
142        assert_eq!(back.ema_len, cfg.ema_len);
143        assert_eq!(back.signal_mode, cfg.signal_mode);
144    }
145}