cxmr_ta_core/indicators/
true_range.rs

1use std::fmt;
2
3use crate::helpers::max3;
4use crate::{Calculate, Close, High, Low, Next, Reset};
5
6/// The range of a day's trading is simply _high_ - _low_.
7/// The true range extends it to yesterday's closing price if it was outside of today's range.
8///
9/// The true range is the largest of one the following:
10///
11/// * Most recent period's high minus the most recent period's low
12/// * Absolute value of the most recent period's high minus the previous close
13/// * Absolute value of the most recent period's low minus the previous close
14///
15/// # Formula
16///
17/// TR = max[(high - low), abs(high - close<sub>prev</sub>), abs(low - close<sub>prev</sub>)]
18///
19/// # Example
20///
21/// ```
22/// extern crate ta;
23/// #[macro_use] extern crate assert_approx_eq;
24///
25/// use ta::{Next, DataItem};
26/// use ta::indicators::TrueRange;
27///
28/// fn main() {
29///     let data = vec![
30///         // open, high, low, close, tr
31///         (9.7   , 10.0, 9.0, 9.5  , 1.0),  // tr = high - low = 10.0 - 9.0 = 1.0
32///         (9.9   , 10.4, 9.8, 10.2 , 0.9),  // tr = high - prev_close = 10.4 - 9.5 = 0.9
33///         (10.1  , 10.7, 9.4, 9.7  , 1.3),  // tr = high - low = 10.7 - 9.4 = 1.3
34///         (9.1   , 9.2 , 8.1, 8.4  , 1.6),  // tr = prev_close - low = 9.7 - 8.1 = 1.6
35///     ];
36///     let mut indicator = TrueRange::new();
37///
38///     for (open, high, low, close, tr) in data {
39///         let di = DataItem::builder()
40///             .high(high)
41///             .low(low)
42///             .close(close)
43///             .open(open)
44///             .volume(1000.0)
45///             .build().unwrap();
46///         assert_approx_eq!(indicator.next(&di), tr);
47///     }
48/// }
49/// ```
50#[derive(Debug, Clone)]
51pub struct TrueRange {
52    prev_close: Option<f64>,
53}
54
55impl TrueRange {
56    pub fn new() -> Self {
57        Self { prev_close: None }
58    }
59}
60
61impl Default for TrueRange {
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67impl fmt::Display for TrueRange {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        write!(f, "TRUE_RANGE()")
70    }
71}
72
73impl Calculate for TrueRange {
74    fn calc(&mut self, input: f64) -> f64 {
75        let distance = match self.prev_close {
76            Some(prev) => (input - prev).abs(),
77            None => 0.0,
78        };
79        self.prev_close = Some(input);
80        distance
81    }
82}
83
84impl<T: High + Low + Close> Next<T> for TrueRange {
85    fn next(&mut self, bar: &T) -> f64 {
86        let max_dist = match self.prev_close {
87            Some(prev_close) => {
88                let dist1 = bar.high() - bar.low();
89                let dist2 = (bar.high() - prev_close).abs();
90                let dist3 = (bar.low() - prev_close).abs();
91                max3(dist1, dist2, dist3)
92            }
93            None => bar.high() - bar.low(),
94        };
95        self.prev_close = Some(bar.close());
96        max_dist
97    }
98}
99
100impl Reset for TrueRange {
101    fn reset(&mut self) {
102        self.prev_close = None;
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use crate::test_helper::*;
110
111    test_indicator!(TrueRange);
112
113    #[test]
114    fn test_next_f64() {
115        let mut tr = TrueRange::new();
116        assert_eq!(round(tr.calc(2.5)), 0.0);
117        assert_eq!(round(tr.calc(3.6)), 1.1);
118        assert_eq!(round(tr.calc(3.3)), 0.3);
119    }
120
121    #[test]
122    fn test_next_bar() {
123        let mut tr = TrueRange::new();
124
125        let bar1 = Bar::new().high(10).low(7.5).close(9);
126        let bar2 = Bar::new().high(11).low(9).close(9.5);
127        let bar3 = Bar::new().high(9).low(5).close(8);
128
129        assert_eq!(tr.next(&bar1), 2.5);
130        assert_eq!(tr.next(&bar2), 2.0);
131        assert_eq!(tr.next(&bar3), 4.5);
132    }
133
134    #[test]
135    fn test_reset() {
136        let mut tr = TrueRange::new();
137
138        let bar1 = Bar::new().high(10).low(7.5).close(9);
139        let bar2 = Bar::new().high(11).low(9).close(9.5);
140
141        tr.next(&bar1);
142        tr.next(&bar2);
143
144        tr.reset();
145        let bar3 = Bar::new().high(60).low(15).close(51);
146        assert_eq!(tr.next(&bar3), 45.0);
147    }
148
149    #[test]
150    fn test_default() {
151        TrueRange::default();
152    }
153
154    #[test]
155    fn test_display() {
156        let indicator = TrueRange::new();
157        assert_eq!(format!("{}", indicator), "TRUE_RANGE()");
158    }
159}