1use motosan_ta_stream::engine::TaEngine;
15use motosan_ta_stream::indicator::*;
16use motosan_ta_stream::snapshot::{IndicatorValue, TaSnapshot};
17use motosan_ta_stream::types::{Bar, Interval};
18
19use crate::technical_analysis::TechnicalIndicators;
20use crate::Candle;
21
22pub fn build_default_engine(symbol: &str) -> TaEngine {
30 TaEngine::new(symbol, Interval::H1)
31 .add("SMA_20", Sma::new(MaConfig { period: 20 }))
33 .add("SMA_50", Sma::new(MaConfig { period: 50 }))
34 .add("EMA_12", Ema::new(MaConfig { period: 12 }))
35 .add("EMA_20", Ema::new(MaConfig { period: 20 }))
36 .add("EMA_26", Ema::new(MaConfig { period: 26 }))
37 .add("EMA_50", Ema::new(MaConfig { period: 50 }))
38 .add("RSI_14", Rsi::new(RsiConfig { period: 14 }))
40 .add(
41 "MACD",
42 Macd::new(MacdConfig {
43 fast: 12,
44 slow: 26,
45 signal: 9,
46 }),
47 )
48 .add("CCI_20", Cci::new(CciConfig { period: 20 }))
49 .add(
50 "WILLIAMS_R_14",
51 WilliamsR::new(WilliamsRConfig { period: 14 }),
52 )
53 .add("ROC_12", Roc::new(RocConfig { period: 12 }))
54 .add("MFI_14", Mfi::new(MfiConfig { period: 14 }))
55 .add("ATR_14", Atr::new(AtrConfig { period: 14 }))
57 .add(
58 "BB_20",
59 Bbands::new(BbandsConfig {
60 period: 20,
61 std_dev: 2.0,
62 }),
63 )
64 .add("ADX_14", Adx::new(AdxConfig { period: 14 }))
65 .add(
66 "KC_20",
67 Keltner::new(KeltnerConfig {
68 period: 20,
69 multiplier: 1.5,
70 }),
71 )
72 .add(
74 "SUPERTREND",
75 Supertrend::new(SupertrendConfig {
76 period: 10,
77 multiplier: 3.0,
78 }),
79 )
80 .add("DONCHIAN_20", Donchian::new(DonchianConfig { period: 20 }))
82 .add("DONCHIAN_10", Donchian::new(DonchianConfig { period: 10 }))
83 .add("VWAP", Vwap::new(VwapConfig { auto_reset: false }))
85}
86
87pub fn calculate_snapshot(candles: &[Candle], symbol: &str) -> Option<TaSnapshot> {
95 let mut engine = build_default_engine(symbol);
96 let mut last_snapshot = None;
97
98 for candle in candles {
99 let bar = Bar {
100 time: candle.time as i64 * 1000, open: candle.open,
102 high: candle.high,
103 low: candle.low,
104 close: candle.close,
105 volume: candle.volume,
106 is_closed: true,
107 };
108 if let Some(snapshot) = engine.feed(&bar) {
109 last_snapshot = Some(snapshot);
110 }
111 }
112
113 last_snapshot
114}
115
116pub fn get_snapshot_value(snapshot: &TaSnapshot, key: &str) -> Option<f64> {
125 match snapshot.results.get(key)? {
126 IndicatorValue::Single(v) => Some(*v),
127 IndicatorValue::Multi(map) => map
128 .get("value")
129 .or_else(|| map.get("line"))
130 .or_else(|| map.get("adx"))
131 .or_else(|| map.get("k"))
132 .copied(),
133 }
134}
135
136pub fn get_snapshot_sub_value(snapshot: &TaSnapshot, key: &str, sub_key: &str) -> Option<f64> {
138 match snapshot.results.get(key)? {
139 IndicatorValue::Multi(map) => map.get(sub_key).copied(),
140 IndicatorValue::Single(v) => {
141 if sub_key == "value" {
142 Some(*v)
143 } else {
144 None
145 }
146 }
147 }
148}
149
150pub fn snapshot_to_indicators(snapshot: &TaSnapshot) -> TechnicalIndicators {
160 TechnicalIndicators {
161 sma_20: get_snapshot_value(snapshot, "SMA_20"),
162 sma_50: get_snapshot_value(snapshot, "SMA_50"),
163 ema_12: get_snapshot_value(snapshot, "EMA_12"),
164 ema_20: get_snapshot_value(snapshot, "EMA_20"),
165 ema_26: get_snapshot_value(snapshot, "EMA_26"),
166 ema_50: get_snapshot_value(snapshot, "EMA_50"),
167 rsi_14: get_snapshot_value(snapshot, "RSI_14"),
168 macd_line: get_snapshot_sub_value(snapshot, "MACD", "macd"),
169 macd_signal: get_snapshot_sub_value(snapshot, "MACD", "signal"),
170 macd_histogram: get_snapshot_sub_value(snapshot, "MACD", "histogram"),
171 bb_upper: get_snapshot_sub_value(snapshot, "BB_20", "upper"),
172 bb_middle: get_snapshot_sub_value(snapshot, "BB_20", "middle"),
173 bb_lower: get_snapshot_sub_value(snapshot, "BB_20", "lower"),
174 atr_14: get_snapshot_value(snapshot, "ATR_14"),
175 adx_14: get_snapshot_sub_value(snapshot, "ADX_14", "adx"),
176 stoch_k: None, stoch_d: None,
178 cci_20: get_snapshot_value(snapshot, "CCI_20"),
179 williams_r_14: get_snapshot_value(snapshot, "WILLIAMS_R_14"),
180 obv: None, mfi_14: get_snapshot_value(snapshot, "MFI_14"),
182 roc_12: get_snapshot_value(snapshot, "ROC_12"),
183 donchian_upper_20: get_snapshot_sub_value(snapshot, "DONCHIAN_20", "upper"),
184 donchian_lower_20: get_snapshot_sub_value(snapshot, "DONCHIAN_20", "lower"),
185 donchian_upper_10: get_snapshot_sub_value(snapshot, "DONCHIAN_10", "upper"),
186 donchian_lower_10: get_snapshot_sub_value(snapshot, "DONCHIAN_10", "lower"),
187 close_zscore_20: None, volume_zscore_20: None,
189 hv_20: None, hv_60: None,
191 kc_upper_20: get_snapshot_sub_value(snapshot, "KC_20", "upper"),
192 kc_lower_20: get_snapshot_sub_value(snapshot, "KC_20", "lower"),
193 supertrend_value: get_snapshot_sub_value(snapshot, "SUPERTREND", "value"),
194 supertrend_direction: get_snapshot_sub_value(snapshot, "SUPERTREND", "direction"),
195 vwap: get_snapshot_value(snapshot, "VWAP"),
196 plus_di_14: get_snapshot_sub_value(snapshot, "ADX_14", "di_plus"),
197 minus_di_14: get_snapshot_sub_value(snapshot, "ADX_14", "di_minus"),
198 }
199}