Skip to main content

egui_charts/studies/builtin/
aroon_oscillator.rs

1//! Aroon Oscillator
2//!
3//! Tushar Chande's Aroon Oscillator is the difference between Aroon Up and Aroon Down: positive
4//! when the latest high is more recent than the latest low, negative otherwise.
5//!
6//! # Formula
7//! ```text
8//! Aroon Up   = 100 * (length - bars since the highest high) / length
9//! Aroon Down = 100 * (length - bars since the lowest low) / length
10//! Oscillator = Aroon Up - Aroon Down
11//! ```
12//! Both use the window of [`Aroon`]: the current bar and the `length` bars before it, a tie
13//! counting the most recent bar. The value ranges from -100 to 100.
14//!
15//! # Example
16//! ```
17//! # 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();
18//! use egui_charts::studies::{AroonOscillator, Indicator};
19//!
20//! let mut oscillator = AroonOscillator::new(14);
21//! oscillator.calculate(&bars);
22//! assert!(oscillator.values().value(0, 14).is_some());
23//! ```
24
25use super::Aroon;
26use crate::model::Bar;
27use crate::studies::palette;
28use crate::studies::stepper::{update_window, usable_behind};
29use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
30use egui::Color32;
31
32/// Aroon Up minus Aroon Down.
33#[derive(Clone)]
34pub struct AroonOscillator {
35    period: usize,
36    values: IndicatorValues,
37    colors: [Color32; 2],
38    visible: bool,
39}
40
41impl AroonOscillator {
42    /// Create an Aroon Oscillator over `period` bars (conventionally 14).
43    pub fn new(period: usize) -> Self {
44        Self {
45            period: period.max(1),
46            values: IndicatorValues::new(),
47            colors: [palette::GREEN, palette::HISTOGRAM_RED],
48            visible: true,
49        }
50    }
51}
52
53impl Default for AroonOscillator {
54    fn default() -> Self {
55        Self::new(14)
56    }
57}
58
59impl AroonOscillator {
60    fn compute(&self, data: &[Bar]) -> IndicatorValues {
61        let mut aroon = Aroon::new(self.period);
62        aroon.calculate(data);
63        let lines = aroon.values();
64        (0..data.len())
65            .map(|bar| match (lines.value(0, bar), lines.value(1, bar)) {
66                (Some(up), Some(down)) => IndicatorValue::Single(up - down),
67                _ => IndicatorValue::None,
68            })
69            .collect()
70    }
71}
72
73impl Indicator for AroonOscillator {
74    fn plot_color(&self, _line: usize, bar: usize) -> Option<Color32> {
75        self.values
76            .value(0, bar)
77            .map(|value| self.colors[usize::from(value < 0.0)])
78    }
79    fn line_count(&self) -> usize {
80        1
81    }
82    fn set_color(&mut self, line: usize, color: Color32) {
83        if line == 0 {
84            self.colors.fill(color);
85        }
86    }
87
88    crate::studies::inputs::study_inputs!(self;
89        period: length("Length", 1, usize::MAX)
90    ; {});
91
92    fn name(&self) -> &str {
93        "Aroon Oscillator"
94    }
95
96    fn desc(&self) -> &str {
97        "Aroon Oscillator - Aroon Up minus Aroon Down"
98    }
99
100    fn calculate(&mut self, data: &[Bar]) {
101        self.update(data, 0);
102    }
103
104    fn update(&mut self, data: &[Bar], from: usize) {
105        let mut values = std::mem::take(&mut self.values);
106        update_window(
107            &mut values,
108            data,
109            from,
110            usable_behind(self.period, |bar| {
111                bar.high.is_finite() && bar.low.is_finite()
112            }),
113            |data| self.compute(data),
114        );
115        self.values = values;
116    }
117
118    fn is_incremental(&self) -> bool {
119        true
120    }
121
122    fn values(&self) -> &IndicatorValues {
123        &self.values
124    }
125
126    fn colors(&self) -> Vec<Color32> {
127        self.colors.to_vec()
128    }
129
130    fn set_colors(&mut self, colors: Vec<Color32>) {
131        if let Some(&color) = colors.first() {
132            self.colors = [color, colors.get(1).copied().unwrap_or(color)];
133        }
134    }
135
136    fn pane(&self) -> crate::studies::StudyPane {
137        crate::studies::StudyPane {
138            range: Some((-100.0, 100.0)),
139            levels: vec![(0.0, palette::GRAY, "Zero")],
140            ..Default::default()
141        }
142    }
143
144    fn is_overlay(&self) -> bool {
145        false
146    }
147
148    fn is_visible(&self) -> bool {
149        self.visible
150    }
151
152    fn set_visible(&mut self, visible: bool) {
153        self.visible = visible;
154    }
155
156    fn clone_box(&self) -> Box<dyn Indicator> {
157        Box::new(self.clone())
158    }
159
160    fn line_names(&self) -> Vec<String> {
161        vec![format!("Aroon Osc({})", self.period)]
162    }
163}