yata/methods/
heikin_ashi.rs1use crate::core::{Candle, Error, Method, ValueType, OHLCV};
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9pub struct HeikinAshi {
10 next_open: ValueType,
11}
12
13impl Method for HeikinAshi {
14 type Params = ();
15 type Input = dyn OHLCV;
16 type Output = Candle;
17
18 fn new((): Self::Params, value: &Self::Input) -> Result<Self, Error> {
19 Ok(Self {
23 next_open: value.ohlc4(),
24 })
25 }
26
27 #[inline]
28 fn next(&mut self, value: &Self::Input) -> Self::Output {
29 let open = self.next_open;
30 let close = value.ohlc4();
31
32 self.next_open = (open + close) * 0.5;
33
34 Candle {
35 open,
36 high: value.high().max(open),
37 low: value.low().min(open),
38 close,
39 volume: value.volume(),
40 }
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::{HeikinAshi, OHLCV};
47 use crate::core::{Candle, Method, ValueType};
48 use crate::helpers::{assert_eq_float, RandomCandles};
49
50 #[test]
51 #[allow(clippy::inspect_for_each)]
52 fn test_heikin_ashi() {
53 let mut candles = RandomCandles::default();
54
55 let first = candles.first();
56 let mut heikin_ashi = HeikinAshi::new((), &first).unwrap();
57
58 let mut prev = Candle {
59 open: first.ohlc4(),
60 high: ValueType::NAN,
61 low: ValueType::NAN,
62 close: first.ohlc4(),
63 volume: ValueType::NAN,
64 };
65
66 candles
67 .take(100)
68 .map(|candle| {
69 let open = (prev.open() + prev.close()) / 2.0;
70 let close = (candle.open() + candle.high() + candle.low() + candle.close()) / 4.0;
71
72 let tested = Candle {
73 open,
74 high: candle.high().max(open).max(close),
75 low: candle.low().min(open).min(close),
76 close,
77 ..candle
78 };
79
80 prev = tested;
81
82 (tested, heikin_ashi.next(&candle))
83 })
84 .inspect(|(original, ha)| assert_eq_float(original.close(), ha.close()))
85 .inspect(|(original, ha)| assert_eq_float(original.high(), ha.high()))
86 .inspect(|(original, ha)| assert_eq_float(original.low(), ha.low()))
87 .inspect(|(original, ha)| assert_eq_float(original.close(), ha.close()))
88 .for_each(|(original, ha)| assert_eq_float(original.volume(), ha.volume()));
89 }
90}