1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use crate::{Ema, HullMa, Indicator, MaType, Sma};
/// Generic moving average — dispatches to the concrete MA variant.
///
/// Used by strategies that need configurable MA types without
/// trait objects. Enum dispatch avoids vtable overhead on the
/// hot-path `next()` / `compute()` calls.
#[derive(Debug, Clone)]
pub enum Ma {
Ema(Ema),
Hull(HullMa),
Sma(Sma),
}
impl Ma {
/// Create a new MA of the given type and period.
///
/// # Errors
///
/// Returns the underlying indicator's error if the period is invalid.
pub fn new(period: usize, ma_type: MaType) -> Result<Self, crate::IndicatorError> {
match ma_type {
MaType::Ema => Ok(Ma::Ema(Ema::new(period)?)),
MaType::Hull => Ok(Ma::Hull(HullMa::new(period)?)),
MaType::Sma => Ok(Ma::Sma(Sma::new(period)?)),
}
}
/// Indicator name (e.g., "EMA(12)", "HullMA(26)", "SMA(50)").
pub fn name(&self) -> &str {
match self {
Ma::Ema(i) => i.name(),
Ma::Hull(i) => i.name(),
Ma::Sma(i) => i.name(),
}
}
/// Clone as a boxed `dyn Indicator` for strategy registration.
pub fn to_boxed(&self) -> Box<dyn Indicator> {
match self {
Ma::Ema(i) => Box::new(i.clone()),
Ma::Hull(i) => Box::new(i.clone()),
Ma::Sma(i) => Box::new(i.clone()),
}
}
}
#[cfg(test)]
#[path = "ma_tests.rs"]
mod tests;