finlib_ta/indicators/
chandelier_exit.rs1use core::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use crate::errors::Result;
7use crate::indicators::{AverageTrueRange, Maximum, Minimum};
8use crate::{Close, High, Low, Next, Period, Reset};
9
10#[doc(alias = "CE")]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[derive(Debug, Clone)]
56pub struct ChandelierExit {
57 atr: AverageTrueRange,
58 min: Minimum,
59 max: Maximum,
60 multiplier: f64,
61}
62
63impl ChandelierExit {
64 pub fn new(period: usize, multiplier: f64) -> Result<Self> {
65 Ok(Self {
66 atr: AverageTrueRange::new(period)?,
67 min: Minimum::new(period)?,
68 max: Maximum::new(period)?,
69 multiplier,
70 })
71 }
72
73 pub fn multiplier(&self) -> f64 {
74 self.multiplier
75 }
76}
77
78#[derive(Debug, Clone, PartialEq)]
79pub struct ChandelierExitOutput {
80 pub long: f64,
81 pub short: f64,
82}
83
84impl From<ChandelierExitOutput> for (f64, f64) {
85 fn from(ce: ChandelierExitOutput) -> Self {
86 (ce.long, ce.short)
87 }
88}
89
90impl Period for ChandelierExit {
91 fn period(&self) -> usize {
92 self.atr.period()
93 }
94}
95
96impl<T: Low + High + Close> Next<&T> for ChandelierExit {
97 type Output = ChandelierExitOutput;
98
99 fn next(&mut self, input: &T) -> Self::Output {
100 let atr = self.atr.next(input) * self.multiplier;
101 let min = self.min.next(input);
102 let max = self.max.next(input);
103
104 ChandelierExitOutput {
105 long: max - atr,
106 short: min + atr,
107 }
108 }
109}
110
111impl Reset for ChandelierExit {
112 fn reset(&mut self) {
113 self.atr.reset();
114 self.min.reset();
115 self.max.reset();
116 }
117}
118
119impl Default for ChandelierExit {
120 fn default() -> Self {
121 Self::new(22, 3.0).unwrap()
122 }
123}
124
125impl fmt::Display for ChandelierExit {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 write!(f, "CE({}, {})", self.atr.period(), self.multiplier)
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use crate::test_helper::*;
134
135 use super::*;
136 use alloc::format;
137
138 type Ce = ChandelierExit;
139
140 fn round(nums: (f64, f64)) -> (f64, f64) {
141 let n0 = (nums.0 * 100.0).round() / 100.0;
142 let n1 = (nums.1 * 100.0).round() / 100.0;
143 (n0, n1)
144 }
145
146 #[test]
147 fn test_new() {
148 assert!(Ce::new(0, 0.0).is_err());
149 assert!(Ce::new(1, 1.0).is_ok());
150 assert!(Ce::new(22, 3.0).is_ok());
151 }
152
153 #[test]
154 fn test_next_bar() {
155 let mut ce = Ce::new(5, 2.0).unwrap();
156
157 let bar1 = Bar::new().high(2).low(1).close(1.5);
158 assert_eq!(round(ce.next(&bar1).into()), (0.0, 3.0));
159
160 let bar2 = Bar::new().high(5).low(3).close(4);
161 assert_eq!(round(ce.next(&bar2).into()), (1.33, 4.67));
162
163 let bar3 = Bar::new().high(9).low(7).close(8);
164 assert_eq!(round(ce.next(&bar3).into()), (3.22, 6.78));
165
166 let bar4 = Bar::new().high(5).low(3).close(4);
167 assert_eq!(round(ce.next(&bar4).into()), (1.81, 8.19));
168
169 let bar5 = Bar::new().high(5).low(3).close(4);
170 assert_eq!(round(ce.next(&bar5).into()), (2.88, 7.12));
171
172 let bar6 = Bar::new().high(2).low(1).close(1.5);
173 assert_eq!(round(ce.next(&bar6).into()), (2.92, 7.08));
174 }
175
176 #[test]
177 fn test_reset() {
178 let mut ce = Ce::new(5, 2.0).unwrap();
179
180 let bar1 = Bar::new().high(2).low(1).close(1.5);
181 let bar2 = Bar::new().high(5).low(3).close(4);
182
183 assert_eq!(round(ce.next(&bar1).into()), (0.0, 3.0));
184 assert_eq!(round(ce.next(&bar2).into()), (1.33, 4.67));
185
186 ce.reset();
187
188 assert_eq!(round(ce.next(&bar1).into()), (0.0, 3.0));
189 assert_eq!(round(ce.next(&bar2).into()), (1.33, 4.67));
190 }
191
192 #[test]
193 fn test_default() {
194 Ce::default();
195 }
196
197 #[test]
198 fn test_display() {
199 let indicator = Ce::new(10, 5.0).unwrap();
200 assert_eq!(format!("{}", indicator), "CE(10, 5)");
201 }
202}