nautilus_indicators/volatility/
atr.rs1use std::fmt::{Debug, Display};
17
18use nautilus_model::data::{Bar, QuoteTick, TradeTick};
19
20use crate::{
21 average::{MovingAverageFactory, MovingAverageType},
22 indicator::{Indicator, MovingAverage},
23};
24
25#[repr(C)]
27#[derive(Debug)]
28#[cfg_attr(
29 feature = "python",
30 pyo3::pyclass(module = "nautilus_trader.indicators", unsendable)
31)]
32#[cfg_attr(
33 feature = "python",
34 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.indicators")
35)]
36pub struct AverageTrueRange {
37 pub period: usize,
38 pub ma_type: MovingAverageType,
39 pub use_previous: bool,
40 pub value_floor: f64,
41 pub value: f64,
42 pub count: usize,
43 pub initialized: bool,
44 ma: Box<dyn MovingAverage + Send + 'static>,
45 has_inputs: bool,
46 previous_close: f64,
47}
48
49impl Display for AverageTrueRange {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(
52 f,
53 "{}({},{},{},{})",
54 self.name(),
55 self.period,
56 self.ma_type,
57 self.use_previous,
58 self.value_floor,
59 )
60 }
61}
62
63impl Indicator for AverageTrueRange {
64 fn name(&self) -> String {
65 stringify!(AverageTrueRange).to_string()
66 }
67
68 fn has_inputs(&self) -> bool {
69 self.has_inputs
70 }
71
72 fn initialized(&self) -> bool {
73 self.initialized
74 }
75
76 fn handle_quote(&mut self, _quote: &QuoteTick) -> anyhow::Result<()> {
77 Ok(())
78 }
79
80 fn handle_trade(&mut self, _trade: &TradeTick) {}
81
82 fn handle_bar(&mut self, bar: &Bar) {
83 self.update_raw((&bar.high).into(), (&bar.low).into(), (&bar.close).into());
84 }
85
86 fn reset(&mut self) {
87 self.ma.reset();
88 self.previous_close = 0.0;
89 self.value = 0.0;
90 self.count = 0;
91 self.has_inputs = false;
92 self.initialized = false;
93 }
94}
95
96impl AverageTrueRange {
97 #[must_use]
99 pub fn new(
100 period: usize,
101 ma_type: Option<MovingAverageType>,
102 use_previous: Option<bool>,
103 value_floor: Option<f64>,
104 ) -> Self {
105 Self {
106 period,
107 ma_type: ma_type.unwrap_or(MovingAverageType::Simple),
108 use_previous: use_previous.unwrap_or(true),
109 value_floor: value_floor.unwrap_or(0.0),
110 value: 0.0,
111 count: 0,
112 previous_close: 0.0,
113 ma: MovingAverageFactory::create(ma_type.unwrap_or(MovingAverageType::Simple), period),
114 has_inputs: false,
115 initialized: false,
116 }
117 }
118
119 pub fn update_raw(&mut self, high: f64, low: f64, close: f64) {
120 if self.use_previous {
121 if !self.has_inputs {
122 self.previous_close = close;
123 }
124 self.ma.update_raw(
125 f64::max(self.previous_close, high) - f64::min(low, self.previous_close),
126 );
127 self.previous_close = close;
128 } else {
129 self.ma.update_raw(high - low);
130 }
131
132 self.apply_floor();
133 self.increment_count();
134 }
135
136 fn apply_floor(&mut self) {
137 if self.value_floor == 0.0 || self.value_floor < self.ma.value() {
138 self.value = self.ma.value();
139 } else {
140 self.value = self.value_floor;
142 }
143 }
144
145 const fn increment_count(&mut self) {
146 self.count += 1;
147
148 if !self.initialized {
149 self.has_inputs = true;
150
151 if self.count >= self.period {
152 self.initialized = true;
153 }
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use rstest::rstest;
161
162 use super::*;
163 use crate::testing::assert_approx_equal;
164
165 #[rstest]
166 fn test_name_returns_expected_string() {
167 let atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
168 assert_eq!(atr.name(), "AverageTrueRange");
169 }
170
171 #[rstest]
172 fn test_str_repr_returns_expected_string() {
173 let atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), Some(true), Some(0.0));
174 assert_eq!(format!("{atr}"), "AverageTrueRange(10,SIMPLE,true,0)");
175 }
176
177 #[rstest]
178 fn test_period() {
179 let atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
180 assert_eq!(atr.period, 10);
181 }
182
183 #[rstest]
184 #[case(None, "SimpleMovingAverage")]
185 #[case(Some(MovingAverageType::Simple), "SimpleMovingAverage")]
186 #[case(Some(MovingAverageType::Exponential), "ExponentialMovingAverage")]
187 #[case(
188 Some(MovingAverageType::DoubleExponential),
189 "DoubleExponentialMovingAverage"
190 )]
191 #[case(Some(MovingAverageType::Wilder), "WilderMovingAverage")]
192 #[case(Some(MovingAverageType::Hull), "HullMovingAverage")]
193 fn test_ma_type_creates_expected_inner_ma(
194 #[case] ma_type: Option<MovingAverageType>,
195 #[case] expected: &str,
196 ) {
197 let atr = AverageTrueRange::new(10, ma_type, None, None);
198 assert_eq!(atr.ma.name(), expected);
199 }
200
201 #[rstest]
202 fn test_initialized_without_inputs_returns_false() {
203 let atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
204 assert!(!atr.initialized());
205 }
206
207 #[rstest]
208 fn test_initialized_with_required_inputs_returns_true() {
209 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
210 for _ in 0..10 {
211 atr.update_raw(1.0, 1.0, 1.0);
212 }
213 assert!(atr.initialized());
214 }
215
216 #[rstest]
217 fn test_value_with_no_inputs_returns_zero() {
218 let atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
219 assert_eq!(atr.value, 0.0);
220 }
221
222 #[rstest]
223 fn test_value_with_epsilon_input() {
224 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
225 let epsilon = f64::EPSILON;
226 atr.update_raw(epsilon, epsilon, epsilon);
227 assert_eq!(atr.value, 0.0);
228 }
229
230 #[rstest]
231 fn test_value_with_one_ones_input() {
232 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
233 atr.update_raw(1.0, 1.0, 1.0);
234 assert_eq!(atr.value, 0.0);
235 }
236
237 #[rstest]
238 fn test_value_with_one_input() {
239 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
240 atr.update_raw(1.00020, 1.0, 1.00010);
241 assert_approx_equal(atr.value, 0.0002);
242 }
243
244 #[rstest]
245 fn test_value_with_three_inputs() {
246 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
247 atr.update_raw(1.00020, 1.0, 1.00010);
248 atr.update_raw(1.00020, 1.0, 1.00010);
249 atr.update_raw(1.00020, 1.0, 1.00010);
250 assert_approx_equal(atr.value, 0.0002);
251 }
252
253 #[rstest]
254 fn test_value_with_close_on_high() {
255 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
256 let mut high = 1.00010;
257 let mut low = 1.0;
258
259 for _ in 0..1000 {
260 high += 0.00010;
261 low += 0.00010;
262 let close = high;
263 atr.update_raw(high, low, close);
264 }
265 assert_approx_equal(atr.value, 0.0001);
266 }
267
268 #[rstest]
269 fn test_value_with_close_on_low() {
270 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
271 let mut high = 1.00010;
272 let mut low = 1.0;
273
274 for _ in 0..1000 {
275 high -= 0.00010;
276 low -= 0.00010;
277 let close = low;
278 atr.update_raw(high, low, close);
279 }
280 assert_approx_equal(atr.value, 0.0001);
281 }
282
283 #[rstest]
284 fn test_floor_with_ten_ones_inputs() {
285 let floor = 0.00005;
286 let mut floored_atr =
287 AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, Some(floor));
288
289 for _ in 0..20 {
290 floored_atr.update_raw(1.0, 1.0, 1.0);
291 }
292 assert_eq!(floored_atr.value, 5e-05);
293 }
294
295 #[rstest]
296 fn test_floor_with_exponentially_decreasing_high_inputs() {
297 let floor = 0.00005;
298 let mut floored_atr =
299 AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, Some(floor));
300 let mut high = 1.00020;
301 let low = 1.0;
302 let close = 1.0;
303
304 for _ in 0..20 {
305 high -= (high - low) / 2.0;
306 floored_atr.update_raw(high, low, close);
307 }
308 assert_eq!(floored_atr.value, floor);
309 }
310
311 #[rstest]
312 fn test_reset_successfully_returns_indicator_to_fresh_state() {
313 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
314 for _ in 0..1000 {
315 atr.update_raw(1.00010, 1.0, 1.00005);
316 }
317 atr.reset();
318 assert!(!atr.initialized);
319 assert_eq!(atr.value, 0.0);
320 }
321
322 #[rstest]
323 fn test_reset_resets_inner_ma() {
324 let mut atr = AverageTrueRange::new(10, Some(MovingAverageType::Simple), None, None);
325 atr.update_raw(1.00010, 1.0, 1.00005);
326 atr.reset();
327 assert_eq!(atr.ma.count(), 0);
328 }
329}