use crate::bar_indicators::average::{MovingAverageProvider, MovingAverageType};
use crate::bar_indicators::indicator_value::IndicatorValue;
use super::super::ohlcv_field::OhlcvField;
#[derive(Debug, Clone)]
pub struct JurikMa {
period: usize,
source: OhlcvField,
ema_fast: MovingAverageProvider,
ema_slow: MovingAverageProvider,
phase: f64,
value: f64,
}
impl JurikMa {
pub fn new(period: usize, phase: f64) -> Self {
Self::with_source(period, phase, OhlcvField::Close)
}
pub fn with_source(period: usize, phase: f64, source: OhlcvField) -> Self {
let p = period.max(1);
Self {
period: p,
source,
ema_fast: MovingAverageProvider::new(MovingAverageType::EMA, p),
ema_slow: MovingAverageProvider::new(MovingAverageType::EMA, (p * 2).max(2)),
phase,
value: 0.0,
}
}
#[inline]
pub fn is_ready(&self) -> bool {
self.ema_fast.is_ready() && self.ema_slow.is_ready()
}
#[inline]
pub fn value(&self) -> IndicatorValue {
IndicatorValue::Single(self.value)
}
#[inline]
pub fn reset(&mut self) {
self.ema_fast.reset();
self.ema_slow.reset();
self.value = 0.0;
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> f64 {
let value = self.source.extract(open, high, low, close, volume);
let f = self.ema_fast.update_bar(0.0, 0.0, 0.0, value, 0.0);
let s = self.ema_slow.update_bar(0.0, 0.0, 0.0, value, 0.0);
let w = (self.phase.clamp(-100.0, 100.0) + 100.0) / 200.0;
self.value = w * f + (1.0 - w) * s;
self.value
}
pub fn period(&self) -> usize {
self.period
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jma_basic_calculation() {
let mut jma = JurikMa::new(10, 0.0);
for i in 1..=30 {
jma.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 0.0);
}
assert!(jma.is_ready());
assert!(jma.value().main() > 0.0);
}
#[test]
fn test_jma_phase_effect() {
let mut jma_fast = JurikMa::new(10, 100.0);
let mut jma_slow = JurikMa::new(10, -100.0);
for i in 1..=30 {
jma_fast.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 0.0);
jma_slow.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 0.0);
}
assert!(jma_fast.value().main() > jma_slow.value().main());
}
#[test]
fn test_jma_reset() {
let mut jma = JurikMa::new(5, 0.0);
for i in 1..=20 {
jma.update_bar(0.0, 0.0, 0.0, i as f64 * 10.0, 0.0);
}
assert!(jma.is_ready());
jma.reset();
assert!(!jma.is_ready());
}
}