use crate::{Interval, Series};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::fmt;
use self::{
bollinger::calculate_bollinger_bands,
ema::{calculate_ema, calculate_ema_slice},
macd::calculate_macd,
rsi::calculate_rsi,
sma::calculate_sma,
stochastic::calculate_stochastic,
};
pub(crate) mod bollinger;
pub(crate) mod ema;
pub(crate) mod macd;
pub(crate) mod rsi;
pub(crate) mod sma;
pub(crate) mod stochastic;
pub type TripleIndicatorData = (VecDeque<f32>, VecDeque<f32>, VecDeque<f32>);
#[derive(Debug, Serialize, Deserialize)]
pub struct EnhancedMarketSeries {
pub symbol: String,
pub interval: Interval,
pub series: Vec<Series>,
pub asks: Vec<Ask>,
pub indicators: Indicators,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Ask {
Sma(usize),
Ema(usize),
Rsi(usize),
Stochastic(usize),
Macd(usize, usize, usize),
Bb(usize, usize),
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Indicators {
pub sma: HashMap<String, VecDeque<f32>>,
pub ema: HashMap<String, VecDeque<f32>>,
pub rsi: HashMap<String, VecDeque<f32>>,
pub stochastic: HashMap<String, VecDeque<f32>>,
pub macd: HashMap<String, TripleIndicatorData>,
pub bb: HashMap<String, TripleIndicatorData>,
}
impl EnhancedMarketSeries {
pub fn with_sma(mut self, period: usize) -> Self {
self.asks.push(Ask::Sma(period));
self
}
pub fn with_ema(mut self, period: usize) -> Self {
self.asks.push(Ask::Ema(period));
self
}
pub fn with_rsi(mut self, period: usize) -> Self {
self.asks.push(Ask::Rsi(period));
self
}
pub fn with_stochastic(mut self, period: usize) -> Self {
self.asks.push(Ask::Stochastic(period));
self
}
pub fn with_macd(mut self, fast: usize, slow: usize, signal: usize) -> Self {
self.asks.push(Ask::Macd(fast, slow, signal));
self
}
pub fn with_bb(mut self, period: usize, std_dev: usize) -> Self {
self.asks.push(Ask::Bb(period, std_dev));
self
}
pub fn calculate(mut self) -> Self {
let series = self.series.clone();
for ind in self.asks.iter() {
match ind {
Ask::Sma(period) => {
let calc_sma = calculate_sma(&series, *period);
self.indicators
.sma
.insert(format!("SMA {}", period), calc_sma);
}
Ask::Ema(period) => {
let calc_ema = calculate_ema(&series, *period);
self.indicators
.ema
.insert(format!("EMA {}", period), calc_ema);
}
Ask::Rsi(period) => {
let calc_rsi = calculate_rsi(&series, *period);
self.indicators
.rsi
.insert(format!("RSI {}", period), calc_rsi);
}
Ask::Stochastic(period) => {
let calc_stoch = calculate_stochastic(&series, *period);
self.indicators
.stochastic
.insert(format!("STO {}", period), calc_stoch);
}
Ask::Macd(fast, slow, signal) => {
let (calc_macd, calc_signal, calc_histogram) =
calculate_macd(&series, *fast, *slow, *signal);
self.indicators.macd.insert(
format!("MACD ({}, {}, {})", fast, slow, signal),
(calc_macd, calc_signal, calc_histogram),
);
}
Ask::Bb(period, std_dev) => {
let (upper_band, mid_band, lower_band) =
calculate_bollinger_bands(&series, *period, *std_dev);
self.indicators.bb.insert(
format!("BB ({}, {})", period, std_dev),
(upper_band, mid_band, lower_band),
);
}
}
}
self
}
}
impl fmt::Display for Ask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Ask::Sma(period) => write!(f, "SMA({})", period),
Ask::Ema(period) => write!(f, "EMA({})", period),
Ask::Rsi(period) => write!(f, "RSI({})", period),
Ask::Macd(fast, slow, signal) => write!(f, "MACD({}, {}, {})", fast, slow, signal),
Ask::Stochastic(period) => write!(f, "STO({})", period),
Ask::Bb(period, std_dev) => write!(f, "BB({}, {})", period, std_dev),
}
}
}
impl fmt::Display for EnhancedMarketSeries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"EnhancedMarketSeries: Symbol = {}, Interval = {}, Requested Indicators: {:?}",
self.symbol, self.interval, self.asks
)?;
writeln!(f, "{:-<120}", "")?;
for (i, series) in self.series.iter().enumerate() {
let mut row = format!(
"DateTime: {}, O: {:.2}, C: {:.2}, H: {:.2}, L: {:.2}, V: {:.0}",
series.datetime, series.open, series.close, series.high, series.low, series.volume
);
for (name, values) in &self.indicators.sma {
if let Some(val) = values.get(i) {
row.push_str(&format!(" | {}: {:.2}", name, val));
}
}
for (name, values) in &self.indicators.ema {
if let Some(val) = values.get(i) {
row.push_str(&format!(" | {}: {:.2}", name, val));
}
}
for (name, values) in &self.indicators.rsi {
if let Some(val) = values.get(i) {
row.push_str(&format!(" | {}: {:.2}", name, val));
}
}
for (name, values) in &self.indicators.stochastic {
if let Some(val) = values.get(i) {
row.push_str(&format!(" | {}: {:.2}", name, val));
}
}
for (name, (m, s, h)) in &self.indicators.macd {
if let (Some(mv), Some(sv), Some(hv)) = (m.get(i), s.get(i), h.get(i)) {
row.push_str(&format!(" | {}: {:.2}/{:.2}/{:.2}", name, mv, sv, hv));
}
}
for (name, (u, m, l)) in &self.indicators.bb {
if let (Some(uv), Some(mv), Some(lv)) = (u.get(i), m.get(i), l.get(i)) {
row.push_str(&format!(" | {}: {:.2}/{:.2}/{:.2}", name, uv, mv, lv));
}
}
writeln!(f, "{}", row)?;
}
Ok(())
}
}