finlib_ta/indicators/
keltner_channel.rs1use 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#[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); 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); 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); 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}