use crate::backtesting::strategy::StrategyContext;
use crate::indicators::Indicator;
use super::IndicatorRef;
#[derive(Debug, Clone)]
pub struct SmaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for SmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Sma(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn sma(period: usize) -> SmaRef {
SmaRef {
period,
key: format!("sma_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct EmaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for EmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Ema(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn ema(period: usize) -> EmaRef {
EmaRef {
period,
key: format!("ema_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct WmaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for WmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Wma(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn wma(period: usize) -> WmaRef {
WmaRef {
period,
key: format!("wma_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct DemaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for DemaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Dema(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn dema(period: usize) -> DemaRef {
DemaRef {
period,
key: format!("dema_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct TemaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for TemaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Tema(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn tema(period: usize) -> TemaRef {
TemaRef {
period,
key: format!("tema_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct HmaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for HmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Hma(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn hma(period: usize) -> HmaRef {
HmaRef {
period,
key: format!("hma_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct VwmaRef {
pub period: usize,
key: String,
}
impl IndicatorRef for VwmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::Vwma(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn vwma(period: usize) -> VwmaRef {
VwmaRef {
period,
key: format!("vwma_{period}"),
}
}
#[derive(Debug, Clone)]
pub struct McginleyDynamicRef {
pub period: usize,
key: String,
}
impl IndicatorRef for McginleyDynamicRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(self.key.clone(), Indicator::McginleyDynamic(self.period))]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[inline]
pub fn mcginley(period: usize) -> McginleyDynamicRef {
McginleyDynamicRef {
period,
key: format!("mcginley_{period}"),
}
}
#[derive(Debug, Clone, Copy)]
pub struct AlmaConfig {
pub period: usize,
pub offset: f64,
pub sigma: f64,
}
#[inline]
pub fn alma(period: usize, offset: f64, sigma: f64) -> AlmaRef {
AlmaRef::new(period, offset, sigma)
}
#[derive(Debug, Clone)]
pub struct AlmaRef {
pub period: usize,
pub offset: f64,
pub sigma: f64,
key: String,
}
impl AlmaRef {
fn new(period: usize, offset: f64, sigma: f64) -> Self {
Self {
period,
offset,
sigma,
key: format!("alma_{period}_{offset}_{sigma}"),
}
}
}
impl IndicatorRef for AlmaRef {
fn key(&self) -> &str {
&self.key
}
fn required_indicators(&self) -> Vec<(String, Indicator)> {
vec![(
self.key.clone(),
Indicator::Alma {
period: self.period,
offset: self.offset,
sigma: self.sigma,
},
)]
}
fn value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator(self.key())
}
fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
ctx.indicator_prev(self.key())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_moving_average_keys() {
assert_eq!(sma(20).key(), "sma_20");
assert_eq!(ema(12).key(), "ema_12");
assert_eq!(wma(14).key(), "wma_14");
assert_eq!(dema(21).key(), "dema_21");
assert_eq!(tema(21).key(), "tema_21");
assert_eq!(hma(9).key(), "hma_9");
assert_eq!(vwma(20).key(), "vwma_20");
assert_eq!(mcginley(14).key(), "mcginley_14");
assert_eq!(alma(9, 0.85, 6.0).key(), "alma_9_0.85_6");
}
#[test]
fn test_required_indicators() {
let sma_ref = sma(20);
let indicators = sma_ref.required_indicators();
assert_eq!(indicators.len(), 1);
assert_eq!(indicators[0].0, "sma_20");
assert!(matches!(indicators[0].1, Indicator::Sma(20)));
}
}