1use crate::{Interval, Series};
2use serde::{Deserialize, Serialize};
3use std::collections::{HashMap, VecDeque};
4use std::fmt;
5
6use self::{
7 bollinger::calculate_bollinger_bands,
8 ema::{calculate_ema, calculate_ema_slice},
9 macd::calculate_macd,
10 rsi::calculate_rsi,
11 sma::calculate_sma,
12 stochastic::calculate_stochastic,
13};
14
15pub(crate) mod bollinger;
16pub(crate) mod ema;
17pub(crate) mod macd;
18pub(crate) mod rsi;
19pub(crate) mod sma;
20pub(crate) mod stochastic;
21
22pub type TripleIndicatorData = (VecDeque<f32>, VecDeque<f32>, VecDeque<f32>);
24
25#[derive(Debug, Serialize, Deserialize)]
27pub struct EnhancedMarketSeries {
28 pub symbol: String,
30 pub interval: Interval,
32 pub series: Vec<Series>,
34 pub asks: Vec<Ask>,
36 pub indicators: Indicators,
38}
39
40#[derive(Debug, Serialize, Deserialize)]
41pub enum Ask {
42 Sma(usize),
43 Ema(usize),
44 Rsi(usize),
45 Stochastic(usize),
46 Macd(usize, usize, usize),
47 Bb(usize, usize),
48}
49
50#[derive(Debug, Default, Serialize, Deserialize)]
52pub struct Indicators {
53 pub sma: HashMap<String, VecDeque<f32>>,
55 pub ema: HashMap<String, VecDeque<f32>>,
57 pub rsi: HashMap<String, VecDeque<f32>>,
59 pub stochastic: HashMap<String, VecDeque<f32>>,
61 pub macd: HashMap<String, TripleIndicatorData>,
63 pub bb: HashMap<String, TripleIndicatorData>,
65}
66
67impl EnhancedMarketSeries {
68 pub fn with_sma(mut self, period: usize) -> Self {
70 self.asks.push(Ask::Sma(period));
71 self
72 }
73
74 pub fn with_ema(mut self, period: usize) -> Self {
76 self.asks.push(Ask::Ema(period));
77 self
78 }
79
80 pub fn with_rsi(mut self, period: usize) -> Self {
82 self.asks.push(Ask::Rsi(period));
83 self
84 }
85
86 pub fn with_stochastic(mut self, period: usize) -> Self {
88 self.asks.push(Ask::Stochastic(period));
89 self
90 }
91
92 pub fn with_macd(mut self, fast: usize, slow: usize, signal: usize) -> Self {
94 self.asks.push(Ask::Macd(fast, slow, signal));
95 self
96 }
97
98 pub fn with_bb(mut self, period: usize, std_dev: usize) -> Self {
100 self.asks.push(Ask::Bb(period, std_dev));
101 self
102 }
103
104 pub fn calculate(mut self) -> Self {
106 let series = self.series.clone();
107 for ind in self.asks.iter() {
108 match ind {
109 Ask::Sma(period) => {
110 let calc_sma = calculate_sma(&series, *period);
111 self.indicators
112 .sma
113 .insert(format!("SMA {}", period), calc_sma);
114 }
115
116 Ask::Ema(period) => {
117 let calc_ema = calculate_ema(&series, *period);
118 self.indicators
119 .ema
120 .insert(format!("EMA {}", period), calc_ema);
121 }
122
123 Ask::Rsi(period) => {
124 let calc_rsi = calculate_rsi(&series, *period);
125 self.indicators
126 .rsi
127 .insert(format!("RSI {}", period), calc_rsi);
128 }
129
130 Ask::Stochastic(period) => {
131 let calc_stoch = calculate_stochastic(&series, *period);
132 self.indicators
133 .stochastic
134 .insert(format!("STO {}", period), calc_stoch);
135 }
136
137 Ask::Macd(fast, slow, signal) => {
138 let (calc_macd, calc_signal, calc_histogram) =
139 calculate_macd(&series, *fast, *slow, *signal);
140
141 self.indicators.macd.insert(
142 format!("MACD ({}, {}, {})", fast, slow, signal),
143 (calc_macd, calc_signal, calc_histogram),
144 );
145 }
146 Ask::Bb(period, std_dev) => {
147 let (upper_band, mid_band, lower_band) =
148 calculate_bollinger_bands(&series, *period, *std_dev);
149
150 self.indicators.bb.insert(
151 format!("BB ({}, {})", period, std_dev),
152 (upper_band, mid_band, lower_band),
153 );
154 }
155 }
156 }
157
158 self
159 }
160}
161
162impl fmt::Display for Ask {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 Ask::Sma(period) => write!(f, "SMA({})", period),
166 Ask::Ema(period) => write!(f, "EMA({})", period),
167 Ask::Rsi(period) => write!(f, "RSI({})", period),
168 Ask::Macd(fast, slow, signal) => write!(f, "MACD({}, {}, {})", fast, slow, signal),
169 Ask::Stochastic(period) => write!(f, "STO({})", period),
170 Ask::Bb(period, std_dev) => write!(f, "BB({}, {})", period, std_dev),
171 }
172 }
173}
174
175impl fmt::Display for EnhancedMarketSeries {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 writeln!(
178 f,
179 "EnhancedMarketSeries: Symbol = {}, Interval = {}, Requested Indicators: {:?}",
180 self.symbol, self.interval, self.asks
181 )?;
182 writeln!(f, "{:-<120}", "")?;
183
184 for (i, series) in self.series.iter().enumerate() {
185 let mut row = format!(
186 "DateTime: {}, O: {:.2}, C: {:.2}, H: {:.2}, L: {:.2}, V: {:.0}",
187 series.datetime, series.open, series.close, series.high, series.low, series.volume
188 );
189
190 for (name, values) in &self.indicators.sma {
192 if let Some(val) = values.get(i) {
193 row.push_str(&format!(" | {}: {:.2}", name, val));
194 }
195 }
196
197 for (name, values) in &self.indicators.ema {
199 if let Some(val) = values.get(i) {
200 row.push_str(&format!(" | {}: {:.2}", name, val));
201 }
202 }
203
204 for (name, values) in &self.indicators.rsi {
206 if let Some(val) = values.get(i) {
207 row.push_str(&format!(" | {}: {:.2}", name, val));
208 }
209 }
210
211 for (name, values) in &self.indicators.stochastic {
213 if let Some(val) = values.get(i) {
214 row.push_str(&format!(" | {}: {:.2}", name, val));
215 }
216 }
217
218 for (name, (m, s, h)) in &self.indicators.macd {
220 if let (Some(mv), Some(sv), Some(hv)) = (m.get(i), s.get(i), h.get(i)) {
221 row.push_str(&format!(" | {}: {:.2}/{:.2}/{:.2}", name, mv, sv, hv));
222 }
223 }
224
225 for (name, (u, m, l)) in &self.indicators.bb {
227 if let (Some(uv), Some(mv), Some(lv)) = (u.get(i), m.get(i), l.get(i)) {
228 row.push_str(&format!(" | {}: {:.2}/{:.2}/{:.2}", name, uv, mv, lv));
229 }
230 }
231
232 writeln!(f, "{}", row)?;
233 }
234
235 Ok(())
236 }
237}