Skip to main content

egui_charts/studies/builtin/
average_daily_range.rs

1//! Average Daily Range (ADR)
2//!
3//! The average distance between each bar's high and low over the last `length` bars. Unlike the
4//! true range of [`ATR`](super::ATR), it ignores gaps from the previous close. On a daily chart
5//! it is the average daily range; on other intervals, the average range of that interval's bars.
6//!
7//! # Formula
8//! ```text
9//! ADR = SMA(high - low, length)
10//! ```
11//!
12//! # Example
13//! ```
14//! # let bars: Vec<egui_charts::model::Bar> = (0..60).map(|i| { let p = 100.0 + (i as f64 * 0.3).sin() * 5.0; egui_charts::model::Bar::new(chrono::Utc::now(), p, p + 1.0, p - 1.0, p + 0.5, 1_000.0) }).collect();
15//! use egui_charts::studies::{AverageDailyRange, Indicator};
16//!
17//! let mut adr = AverageDailyRange::new(14);
18//! adr.calculate(&bars);
19//! assert_eq!(adr.values().value(0, 13), Some(2.0));
20//! ```
21
22use super::over_finite_bars;
23use crate::model::Bar;
24use crate::studies::palette;
25use crate::studies::stepper::{rolling_mean, update_window, usable_behind};
26use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
27use egui::Color32;
28
29/// Simple average of the bar range.
30#[derive(Clone)]
31pub struct AverageDailyRange {
32    period: usize,
33    values: IndicatorValues,
34    color: Color32,
35    visible: bool,
36}
37
38impl AverageDailyRange {
39    /// Create an Average Daily Range over `period` bars (conventionally 14).
40    pub fn new(period: usize) -> Self {
41        Self {
42            period: period.max(1),
43            values: IndicatorValues::new(),
44            color: palette::BLUE,
45            visible: true,
46        }
47    }
48}
49
50impl AverageDailyRange {
51    fn compute(&self, data: &[Bar]) -> IndicatorValues {
52        let ranges: Vec<f64> = data.iter().map(|b| b.high - b.low).collect();
53        rolling_mean(&ranges, self.period)
54            .into_iter()
55            .map(IndicatorValue::Single)
56            .collect()
57    }
58}
59
60impl Default for AverageDailyRange {
61    fn default() -> Self {
62        Self::new(14)
63    }
64}
65
66impl Indicator for AverageDailyRange {
67    crate::studies::inputs::study_inputs!(self;
68        period: length("Length", 1, usize::MAX)
69    ; {});
70
71    fn value_format(&self) -> crate::scales::StudyFormat {
72        crate::scales::StudyFormat::Price
73    }
74
75    fn name(&self) -> &str {
76        "Average Daily Range"
77    }
78
79    fn desc(&self) -> &str {
80        "Average Daily Range - Simple average of the high-low range"
81    }
82
83    fn calculate(&mut self, data: &[Bar]) {
84        self.update(data, 0);
85    }
86
87    fn update(&mut self, data: &[Bar], from: usize) {
88        let mut values = std::mem::take(&mut self.values);
89        update_window(
90            &mut values,
91            data,
92            from,
93            usable_behind(self.period - 1, |b| b.high.is_finite() && b.low.is_finite()),
94            |data| {
95                over_finite_bars(
96                    data,
97                    |b| b.high.is_finite() && b.low.is_finite(),
98                    |data| self.compute(data),
99                )
100            },
101        );
102        self.values = values;
103    }
104
105    fn is_incremental(&self) -> bool {
106        true
107    }
108
109    fn values(&self) -> &IndicatorValues {
110        &self.values
111    }
112
113    fn colors(&self) -> Vec<Color32> {
114        vec![self.color]
115    }
116
117    fn set_colors(&mut self, colors: Vec<Color32>) {
118        if let Some(&color) = colors.first() {
119            self.color = color;
120        }
121    }
122
123    fn is_overlay(&self) -> bool {
124        false
125    }
126
127    fn is_visible(&self) -> bool {
128        self.visible
129    }
130
131    fn set_visible(&mut self, visible: bool) {
132        self.visible = visible;
133    }
134
135    fn clone_box(&self) -> Box<dyn Indicator> {
136        Box::new(self.clone())
137    }
138
139    fn line_names(&self) -> Vec<String> {
140        vec![format!("ADR({})", self.period)]
141    }
142}