Skip to main content

kestrel_chartkit/indicator/
chaikin_osc.rs

1use super::smoothing::Ema;
2use super::volume_indicators::AccDistEngine;
3use super::{Indicator, IndicatorAlert, IndicatorOutput};
4use crate::model::Bar;
5
6/// Chaikin Oscillator Engine.
7/// Chaikin Oscillator = EMA(ADL, 3) - EMA(ADL, 10)
8#[derive(Debug, Clone)]
9pub struct ChaikinOscillatorEngine {
10    fast_len: usize,
11    slow_len: usize,
12    adl: AccDistEngine,
13    fast_ema: Ema,
14    slow_ema: Ema,
15    count: usize,
16}
17
18impl ChaikinOscillatorEngine {
19    pub fn new(fast_len: usize, slow_len: usize) -> Self {
20        Self {
21            fast_len: fast_len.max(1),
22            slow_len: slow_len.max(1),
23            adl: AccDistEngine::new(),
24            fast_ema: Ema::new(fast_len),
25            slow_ema: Ema::new(slow_len),
26            count: 0,
27        }
28    }
29
30    pub fn with_defaults() -> Self {
31        Self::new(3, 10)
32    }
33
34    pub fn fast_len(&self) -> usize {
35        self.fast_len
36    }
37
38    pub fn slow_len(&self) -> usize {
39        self.slow_len
40    }
41}
42
43impl Indicator for ChaikinOscillatorEngine {
44    fn name(&self) -> &str {
45        "chaikin_oscillator"
46    }
47
48    fn warmup_period(&self) -> usize {
49        self.slow_len
50    }
51
52    fn reset(&mut self) {
53        self.adl.reset();
54        self.fast_ema.reset();
55        self.slow_ema.reset();
56        self.count = 0;
57    }
58
59    fn on_bar(&mut self, bar: &Bar) -> Option<IndicatorOutput> {
60        self.count += 1;
61        let adl_val = self.adl.on_bar(bar)?.value;
62
63        let fast = self.fast_ema.update(adl_val);
64        let slow = self.slow_ema.update(adl_val);
65
66        if self.count < self.slow_len {
67            return None;
68        }
69
70        let cho_val = fast - slow;
71        Some(IndicatorOutput::new(cho_val))
72    }
73
74    fn alerts(&self) -> Vec<IndicatorAlert> {
75        Vec::new()
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_chaikin_oscillator() {
85        let mut cho = ChaikinOscillatorEngine::with_defaults();
86        let mut out = None;
87        for i in 0..20 {
88            let b = Bar::new(i, 100.0, 105.0, 95.0, 100.0 + i as f64, 1000.0);
89            out = cho.on_bar(&b);
90        }
91        assert!(out.is_some());
92    }
93}