use super::*;
use crate::test_helpers::helpers::{make_candle, ts};
use rust_decimal_macros::dec;
#[test]
fn ema_multiplier() {
let ema = Ema::new(9).expect("valid EMA period");
assert_eq!(ema.multiplier(), dec!(0.2));
let ema = Ema::new(19).expect("valid EMA period");
assert_eq!(ema.multiplier(), dec!(0.1));
}
#[test]
fn ema_first_value_is_sma() {
let candles: Vec<Candle> = vec![
make_candle(dec!(100), ts(0)),
make_candle(dec!(102), ts(1)),
make_candle(dec!(104), ts(2)),
];
let ema = Ema::new(3).expect("valid EMA period");
let series = ema.compute(&candles).expect("sufficient data for EMA");
let values = series.decimal_values();
assert_eq!(values[0], dec!(102));
}
#[test]
fn ema_reacts_to_changes() {
let candles: Vec<Candle> = vec![
make_candle(dec!(100), ts(0)),
make_candle(dec!(100), ts(1)),
make_candle(dec!(100), ts(2)),
make_candle(dec!(120), ts(3)), ];
let ema = Ema::new(3).expect("valid EMA period");
let series = ema.compute(&candles).expect("sufficient data for EMA");
let values = series.decimal_values();
assert_eq!(values[0], dec!(100));
assert_eq!(values[1], dec!(110));
}
#[test]
fn ema_converges_to_constant() {
let candles: Vec<Candle> = (0..20).map(|i| make_candle(dec!(100), ts(i))).collect();
let ema = Ema::new(5).expect("valid EMA period");
let series = ema.compute(&candles).expect("sufficient data for EMA");
for (_, value) in series.values() {
assert_eq!(*value, dec!(100));
}
}
#[test]
fn ema_output_length() {
let candles: Vec<Candle> = (0..10)
.map(|i| make_candle(Decimal::from(100 + i), ts(i)))
.collect();
let ema = Ema::new(3).expect("valid EMA period");
let series = ema.compute(&candles).expect("sufficient data for EMA");
assert_eq!(series.len(), 8);
}
#[test]
fn ema_insufficient_data() {
let candles: Vec<Candle> = vec![make_candle(dec!(100), ts(0)), make_candle(dec!(102), ts(1))];
let ema = Ema::new(3).expect("valid EMA period");
let result = ema.compute(&candles);
assert!(matches!(
result,
Err(IndicatorError::InsufficientData {
required: 3,
actual: 2
})
));
}
#[test]
fn ema_period_zero() {
let result = Ema::new(0);
assert!(matches!(
result,
Err(IndicatorError::InvalidParameter { .. })
));
}
#[test]
fn ema_name() {
let ema = Ema::new(20).expect("valid EMA period");
assert_eq!(ema.name(), "EMA(20)");
}
#[test]
fn ema_warmup_period() {
let ema = Ema::new(14).expect("valid EMA period");
assert_eq!(ema.warmup_period(), 14);
}
#[test]
fn ema_faster_than_sma() {
use crate::sma::Sma;
let candles: Vec<Candle> = vec![
make_candle(dec!(100), ts(0)),
make_candle(dec!(100), ts(1)),
make_candle(dec!(100), ts(2)),
make_candle(dec!(120), ts(3)),
make_candle(dec!(120), ts(4)),
make_candle(dec!(120), ts(5)),
];
let ema = Ema::new(3).expect("valid EMA period");
let sma = Sma::new(3).expect("valid SMA period");
let ema_series = ema.compute(&candles).expect("sufficient data for EMA");
let sma_series = sma.compute(&candles).expect("sufficient data for SMA");
let ema_values = ema_series.decimal_values();
let sma_values = sma_series.decimal_values();
let target = dec!(120);
let ema_dist = (target - ema_values[1]).abs();
let sma_dist = (target - sma_values[1]).abs();
assert!(
ema_dist < sma_dist,
"EMA ({}) should be closer to 120 than SMA ({})",
ema_values[1],
sma_values[1]
);
}