use crate::bar_indicators::average::sma::Sma;
use crate::bar_indicators::average::wma::Wma;
use crate::bar_indicators::average::rma::Rma;
use crate::bar_indicators::average::ema::Ema;
use crate::bar_indicators::average::dema::Dema;
use crate::bar_indicators::average::hma::Hma;
use crate::bar_indicators::average::tema::Tema;
use crate::bar_indicators::average::tma::Tma;
use crate::bar_indicators::average::vwap::Vwap;
use crate::bar_indicators::average::vwma::Vwma;
use crate::bar_indicators::average::ama::Ama;
use crate::bar_indicators::indicator_value::IndicatorValue;
pub use crate::bar_indicators::ohlcv_field::OhlcvField;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum MovingAverageType {
SMA, WMA, EMA, RMA, DEMA, TEMA, HMA, TMA, VWMA, VWAP, AMA, }
impl std::fmt::Display for MovingAverageType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone)]
pub enum MovingAverageProvider {
Sma(Box<Sma>),
Wma(Box<Wma>),
Rma(Box<Rma>),
Ema(Box<Ema>),
Dema(Box<Dema>),
Tema(Box<Tema>),
Tma(Box<Tma>),
Hma(Box<Hma>),
Ama(Box<Ama>),
Vwma(Box<Vwma>),
Vwap(Box<Vwap>),
}
impl MovingAverageProvider {
pub fn period(&self) -> usize {
match self {
Self::Sma(ma) => ma.period(),
Self::Wma(ma) => ma.period(),
Self::Rma(ma) => ma.period(),
Self::Ema(ma) => ma.period(),
Self::Dema(ma) => ma.period(),
Self::Tema(ma) => ma.period(),
Self::Tma(ma) => ma.period(),
Self::Hma(ma) => ma.period(),
Self::Ama(ma) => ma.period(),
Self::Vwma(ma) => ma.period(),
Self::Vwap(ma) => ma.period(),
}
}
pub fn new(ma_type: MovingAverageType, period: usize) -> Self {
match ma_type {
MovingAverageType::SMA => Self::Sma(Box::new(Sma::new(period.max(1)))),
MovingAverageType::WMA => Self::Wma(Box::new(Wma::new(period.max(1)))),
MovingAverageType::EMA => Self::Ema(Box::new(Ema::new(period.max(1)))),
MovingAverageType::RMA => Self::Rma(Box::new(Rma::new(period.max(1)))),
MovingAverageType::DEMA => Self::Dema(Box::new(Dema::new(period.max(1)))),
MovingAverageType::TEMA => Self::Tema(Box::new(Tema::new(period.max(1)))),
MovingAverageType::HMA => Self::Hma(Box::new(Hma::new(period))),
MovingAverageType::TMA => Self::Tma(Box::new(Tma::new(period.max(1)))),
MovingAverageType::VWMA => Self::Vwma(Box::new(Vwma::new(period.max(1)))),
MovingAverageType::VWAP => Self::Vwap(Box::new(Vwap::new(period.max(1)))),
MovingAverageType::AMA => {
let p = period.max(2);
Self::Ama(Box::new(Ama::new(p, 2, 30)))
}
}
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> f64 {
match self {
Self::Sma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Wma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Rma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Ema(ma) => ma.update_bar(open, high, low, close, volume),
Self::Dema(ma) => ma.update_bar(open, high, low, close, volume),
Self::Tema(ma) => ma.update_bar(open, high, low, close, volume),
Self::Tma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Hma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Ama(ma) => ma.update_bar(open, high, low, close, volume),
Self::Vwma(ma) => ma.update_bar(open, high, low, close, volume),
Self::Vwap(ma) => ma.update_bar(open, high, low, close, volume),
}
}
pub fn value(&self) -> IndicatorValue {
match self {
Self::Sma(ma) => ma.value(),
Self::Wma(ma) => ma.value(),
Self::Rma(ma) => ma.value(),
Self::Ema(ma) => ma.value(),
Self::Dema(ma) => ma.value(),
Self::Tema(ma) => ma.value(),
Self::Tma(ma) => ma.value(),
Self::Hma(ma) => ma.value(),
Self::Ama(ma) => ma.value(),
Self::Vwma(ma) => ma.value(),
Self::Vwap(ma) => ma.value(),
}
}
pub fn is_ready(&self) -> bool {
match self {
Self::Sma(ma) => ma.is_ready(),
Self::Wma(ma) => ma.is_ready(),
Self::Rma(ma) => ma.is_ready(),
Self::Ema(ma) => ma.is_ready(),
Self::Dema(ma) => ma.is_ready(),
Self::Tema(ma) => ma.is_ready(),
Self::Tma(ma) => ma.is_ready(),
Self::Hma(ma) => ma.is_ready(),
Self::Ama(ma) => ma.is_ready(),
Self::Vwma(ma) => ma.is_ready(),
Self::Vwap(ma) => ma.is_ready(),
}
}
pub fn reset(&mut self) {
match self {
Self::Sma(ma) => ma.reset(),
Self::Wma(ma) => ma.reset(),
Self::Rma(ma) => ma.reset(),
Self::Ema(ma) => ma.reset(),
Self::Dema(ma) => ma.reset(),
Self::Tema(ma) => ma.reset(),
Self::Tma(ma) => ma.reset(),
Self::Hma(ma) => ma.reset(),
Self::Ama(ma) => ma.reset(),
Self::Vwma(ma) => ma.reset(),
Self::Vwap(ma) => ma.reset(),
}
}
}
#[derive(Debug, Clone)]
pub struct MovingAverageWithField {
ma: MovingAverageProvider,
field: OhlcvField,
}
impl MovingAverageWithField {
pub fn new(ma_type: MovingAverageType, period: usize, field: OhlcvField) -> Self {
Self {
ma: MovingAverageProvider::new(ma_type, period),
field,
}
}
#[inline]
fn extract_field(&self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> f64 {
self.field.extract(open, high, low, close, volume)
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> f64 {
let value = self.extract_field(open, high, low, close, volume);
self.ma.update_bar(value, value, value, value, value)
}
pub fn value(&self) -> IndicatorValue {
self.ma.value()
}
pub fn is_ready(&self) -> bool {
self.ma.is_ready()
}
pub fn reset(&mut self) {
self.ma.reset()
}
pub fn period(&self) -> usize {
self.ma.period()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_moving_average_type_display() {
assert_eq!(format!("{}", MovingAverageType::SMA), "SMA");
assert_eq!(format!("{}", MovingAverageType::EMA), "EMA");
assert_eq!(format!("{}", MovingAverageType::HMA), "HMA");
}
#[test]
fn test_provider_all_types() {
let types = [
MovingAverageType::SMA,
MovingAverageType::WMA,
MovingAverageType::EMA,
MovingAverageType::RMA,
MovingAverageType::DEMA,
MovingAverageType::TEMA,
MovingAverageType::HMA,
MovingAverageType::TMA,
MovingAverageType::VWMA,
MovingAverageType::VWAP,
MovingAverageType::AMA,
];
for ma_type in types {
let mut ma = MovingAverageProvider::new(ma_type, 10);
for i in 1..=20 {
ma.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 1000.0);
}
assert!(ma.value().main() > 0.0, "{:?} should produce value", ma_type);
}
}
#[test]
fn test_provider_reset() {
let mut ma = MovingAverageProvider::new(MovingAverageType::SMA, 5);
for i in 1..=10 {
ma.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 0.0);
}
assert!(ma.is_ready());
ma.reset();
assert!(!ma.is_ready());
}
#[test]
fn test_with_field_close() {
let mut ma = MovingAverageWithField::new(MovingAverageType::SMA, 3, OhlcvField::Close);
ma.update_bar(100.0, 110.0, 90.0, 105.0, 1000.0);
ma.update_bar(105.0, 115.0, 95.0, 110.0, 1000.0);
ma.update_bar(110.0, 120.0, 100.0, 115.0, 1000.0);
assert!(ma.is_ready());
assert!((ma.value().main() - 110.0).abs() < 0.01);
}
#[test]
fn test_with_field_high() {
let mut ma = MovingAverageWithField::new(MovingAverageType::SMA, 3, OhlcvField::High);
ma.update_bar(100.0, 110.0, 90.0, 105.0, 1000.0);
ma.update_bar(105.0, 115.0, 95.0, 110.0, 1000.0);
ma.update_bar(110.0, 120.0, 100.0, 115.0, 1000.0);
assert!(ma.is_ready());
assert!((ma.value().main() - 115.0).abs() < 0.01);
}
#[test]
fn test_with_field_low() {
let mut ma = MovingAverageWithField::new(MovingAverageType::SMA, 3, OhlcvField::Low);
ma.update_bar(100.0, 110.0, 90.0, 105.0, 1000.0);
ma.update_bar(105.0, 115.0, 95.0, 110.0, 1000.0);
ma.update_bar(110.0, 120.0, 100.0, 115.0, 1000.0);
assert!(ma.is_ready());
assert!((ma.value().main() - 95.0).abs() < 0.01);
}
}