finlib_ta/indicators/
keltner_channel.rs

1use core::fmt;
2
3use crate::errors::Result;
4use crate::indicators::{AverageTrueRange, ExponentialMovingAverage};
5use crate::{Close, High, Low, Next, Period, Reset};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9/// Keltner Channel (KC).
10///
11/// A Keltner Channel is an indicator showing the Average True Range (ATR) of a
12/// price surrounding a central moving average. The ATR bands are typically
13/// shown 'k' times moved away from the moving average.
14///
15/// # Formula
16///
17/// See EMA, ATR documentation.
18///
19/// KC is composed as:
20///
21///  * _KC<sub>Middle Band</sub>_ - Exponential Moving Average (EMA).
22///  * _KC<sub>Upper Band</sub>_ = EMA + ATR of observation * multipler (usually 2.0)
23///  * _KC<sub>Lower Band</sub>_ = EMA - ATR of observation * multipler (usually 2.0)
24///
25/// # Example
26///
27///```
28/// use finlib_ta::indicators::{KeltnerChannel, KeltnerChannelOutput};
29/// use finlib_ta::Next;
30///
31/// let mut kc = KeltnerChannel::new(3, 2.0_f64).unwrap();
32///
33/// let out_0 = kc.next(2.0);
34///
35/// let out_1 = kc.next(5.0);
36///
37/// assert_eq!(out_0.average, 2.0);
38/// assert_eq!(out_0.upper, 2.0);
39/// assert_eq!(out_0.lower, 2.0);
40///
41/// assert_eq!(out_1.average, 3.5);
42/// assert_eq!(out_1.upper, 6.5);
43/// assert_eq!(out_1.lower, 0.5);
44/// ```
45///
46/// # Links
47///
48/// * [Keltner channel, Wikipedia](https://en.wikipedia.org/wiki/Keltner_channel)
49#[doc(alias = "KC")]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51#[derive(Debug, Clone)]
52pub struct KeltnerChannel {
53    period: usize,
54    multiplier: f64,
55    atr: AverageTrueRange,
56    ema: ExponentialMovingAverage,
57}
58
59#[derive(Debug, Clone, PartialEq)]
60pub struct KeltnerChannelOutput {
61    pub average: f64,
62    pub upper: f64,
63    pub lower: f64,
64}
65
66impl KeltnerChannel {
67    pub fn new(period: usize, multiplier: f64) -> Result<Self> {
68        Ok(Self {
69            period,
70            multiplier,
71            atr: AverageTrueRange::new(period)?,
72            ema: ExponentialMovingAverage::new(period)?,
73        })
74    }
75
76    pub fn multiplier(&self) -> f64 {
77        self.multiplier
78    }
79}
80
81impl Period for KeltnerChannel {
82    fn period(&self) -> usize {
83        self.period
84    }
85}
86
87impl Next<f64> for KeltnerChannel {
88    type Output = KeltnerChannelOutput;
89
90    fn next(&mut self, input: f64) -> Self::Output {
91        let atr = self.atr.next(input);
92        let average = self.ema.next(input);
93
94        Self::Output {
95            average,
96            upper: average + atr * self.multiplier,
97            lower: average - atr * self.multiplier,
98        }
99    }
100}
101
102impl<T: Close + High + Low> Next<&T> for KeltnerChannel {
103    type Output = KeltnerChannelOutput;
104
105    fn next(&mut self, input: &T) -> Self::Output {
106        let typical_price = (input.close() + input.high() + input.low()) / 3.0;
107
108        let average = self.ema.next(typical_price);
109        let atr = self.atr.next(input);
110
111        Self::Output {
112            average,
113            upper: average + atr * self.multiplier,
114            lower: average - atr * self.multiplier,
115        }
116    }
117}
118
119impl Reset for KeltnerChannel {
120    fn reset(&mut self) {
121        self.atr.reset();
122        self.ema.reset();
123    }
124}
125
126impl Default for KeltnerChannel {
127    fn default() -> Self {
128        Self::new(10, 2_f64).unwrap()
129    }
130}
131
132impl fmt::Display for KeltnerChannel {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        write!(f, "KC({}, {})", self.period, self.multiplier)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::test_helper::*;
142    use alloc::format;
143
144    test_indicator!(KeltnerChannel);
145
146    #[test]
147    fn test_new() {
148        assert!(KeltnerChannel::new(0, 2_f64).is_err());
149        assert!(KeltnerChannel::new(1, 2_f64).is_ok());
150        assert!(KeltnerChannel::new(2, 2_f64).is_ok());
151    }
152
153    #[test]
154    fn test_next() {
155        let mut kc = KeltnerChannel::new(3, 2.0_f64).unwrap();
156
157        let a = kc.next(2.0);
158        let b = kc.next(5.0);
159        let c = kc.next(1.0);
160        let d = kc.next(6.25);
161
162        assert_eq!(round(a.average), 2.0);
163        assert_eq!(round(b.average), 3.5);
164        assert_eq!(round(c.average), 2.25);
165        assert_eq!(round(d.average), 4.25);
166
167        assert_eq!(round(a.upper), 2.0);
168        assert_eq!(round(b.upper), 6.5);
169        assert_eq!(round(c.upper), 7.75);
170        assert_eq!(round(d.upper), 12.25);
171
172        assert_eq!(round(a.lower), 2.0);
173        assert_eq!(round(b.lower), 0.5);
174        assert_eq!(round(c.lower), -3.25);
175        assert_eq!(round(d.lower), -3.75);
176    }
177
178    #[test]
179    fn test_next_with_data_item() {
180        let mut kc = KeltnerChannel::new(3, 2.0_f64).unwrap();
181
182        let dt1 = Bar::new().low(1.2).high(1.7).close(1.3); // typical_price = 1.4
183        let o1 = kc.next(&dt1);
184        assert_eq!(round(o1.average), 1.4);
185        assert_eq!(round(o1.lower), 0.4);
186        assert_eq!(round(o1.upper), 2.4);
187
188        let dt2 = Bar::new().low(1.3).high(1.8).close(1.4); // typical_price = 1.5
189        let o2 = kc.next(&dt2);
190        assert_eq!(round(o2.average), 1.45);
191        assert_eq!(round(o2.lower), 0.45);
192        assert_eq!(round(o2.upper), 2.45);
193
194        let dt3 = Bar::new().low(1.4).high(1.9).close(1.5); // typical_price = 1.6
195        let o3 = kc.next(&dt3);
196        assert_eq!(round(o3.average), 1.525);
197        assert_eq!(round(o3.lower), 0.525);
198        assert_eq!(round(o3.upper), 2.525);
199    }
200
201    #[test]
202    fn test_reset() {
203        let mut kc = KeltnerChannel::new(5, 2.0_f64).unwrap();
204
205        let out = kc.next(3.0);
206
207        assert_eq!(out.average, 3.0);
208        assert_eq!(out.upper, 3.0);
209        assert_eq!(out.lower, 3.0);
210
211        kc.next(2.5);
212        kc.next(3.5);
213        kc.next(4.0);
214
215        let out = kc.next(2.0);
216
217        assert_eq!(round(out.average), 2.914);
218        assert_eq!(round(out.upper), 4.864);
219        assert_eq!(round(out.lower), 0.963);
220
221        kc.reset();
222        let out = kc.next(3.0);
223        assert_eq!(out.average, 3.0);
224        assert_eq!(out.lower, 3.0);
225        assert_eq!(out.upper, 3.0);
226    }
227
228    #[test]
229    fn test_default() {
230        KeltnerChannel::default();
231    }
232
233    #[test]
234    fn test_display() {
235        let kc = KeltnerChannel::new(10, 3.0_f64).unwrap();
236        assert_eq!(format!("{}", kc), "KC(10, 3)");
237    }
238}