Skip to main content

trading_toolkit/indicator/
macd.rs

1use super::MovingAverage;
2use crate::types::{data::BaseData, error::ToolkitError};
3
4#[derive(Debug, Clone, Copy)]
5pub struct MovingAverageConvergenceDivergence {
6    signal: f64,
7    ema_12: f64,
8    ema_26: f64,
9}
10
11impl MovingAverageConvergenceDivergence {
12    pub fn new<T>(data: &[T]) -> Result<Self, ToolkitError>
13    where
14        T: BaseData + Clone,
15    {
16        if data.len() < 34 {
17            return Err(ToolkitError::DataNotEnough);
18        }
19        let mut data = data.to_vec();
20        data.sort_by_key(|k| k.epoch_time());
21        data = data.split_at(data.len() - 34).1.to_vec();
22        let n = data.len();
23        let ema_26 = MovingAverage::exponential(&data[n - 26..n].to_vec());
24        let ema_12 = MovingAverage::exponential(&data[n - 12..n].to_vec());
25        let macd_series: Vec<_> = (0..9)
26            .map(|i| {
27                let end = n - (8 - i); // 9번째 마지막이 현재 bar
28                let e26 = MovingAverage::exponential(&data[end - 26..end].to_vec()).inner();
29                let e12 = MovingAverage::exponential(&data[end - 12..end].to_vec()).inner();
30                e12 - e26
31            })
32            .collect();
33
34        // MACD 값들의 EMA_9 → Signal Line
35        // BaseData로 wrapping해서 기존 exponential 활용
36        #[derive(Debug, Clone, Copy)]
37        struct MacdPoint(f64, u128);
38        impl BaseData for MacdPoint {
39            fn value(&self) -> f64 {
40                self.0
41            }
42            fn weight(&self) -> u64 {
43                1
44            }
45            fn epoch_time(&self) -> u128 {
46                self.1
47            }
48        }
49        let macd_data: Vec<MacdPoint> = macd_series
50            .iter()
51            .enumerate()
52            .map(|(i, v)| MacdPoint(*v, i as u128))
53            .collect();
54
55        let signal = MovingAverage::exponential(&macd_data).inner();
56        Ok(Self {
57            signal: signal,
58            ema_12: ema_12.inner(),
59            ema_26: ema_26.inner(),
60        })
61    }
62
63    /// fast line / MACD line
64    pub fn fast(&self) -> f64 {
65        self.ema_12 - self.ema_26
66    }
67
68    /// slow line / Signal
69    pub fn slow(&self) -> f64 {
70        self.signal
71    }
72
73    /// MACD histogram
74    pub fn macd_histogram(&self) -> f64 {
75        self.fast() - self.slow()
76    }
77}