kestrel_chartkit/indicator/
mcginley.rs1use super::{Indicator, IndicatorAlert, IndicatorOutput};
2use crate::model::Bar;
3
4#[derive(Debug, Clone)]
19pub struct McGinleyDynamicEngine {
20 period: usize,
21 state: Option<f64>,
22}
23
24impl McGinleyDynamicEngine {
25 pub fn new(period: usize) -> Self {
26 Self {
27 period: period.max(1),
28 state: None,
29 }
30 }
31}
32
33impl Indicator for McGinleyDynamicEngine {
34 fn name(&self) -> &str {
35 "mcginley"
36 }
37
38 fn warmup_period(&self) -> usize {
39 self.period
40 }
41
42 fn reset(&mut self) {
43 self.state = None;
44 }
45
46 fn on_bar(&mut self, bar: &Bar) -> Option<IndicatorOutput> {
47 let price = bar.close;
48 let k = self.period as f64;
49
50 let next = match self.state {
51 None => price,
52 Some(prev) => {
53 let ratio = (price / prev.max(1e-8)).powi(4);
54 prev + (price - prev) / (k * ratio).max(1e-6)
55 }
56 };
57
58 self.state = Some(next);
59 Some(IndicatorOutput::new(next))
60 }
61
62 fn alerts(&self) -> Vec<IndicatorAlert> {
63 Vec::new()
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_mcginley_basic() {
73 let mut mg = McGinleyDynamicEngine::new(14);
74 let b1 = Bar::new(1, 100.0, 105.0, 95.0, 100.0, 1000.0);
75 let out1 = mg.on_bar(&b1).unwrap();
76 assert_eq!(out1.value, 100.0);
77 }
78}