Skip to main content

egui_charts/studies/builtin/
accelerator_oscillator.rs

1//! Accelerator Oscillator (AC)
2//!
3//! Developed by Bill Williams, the Accelerator Oscillator measures the acceleration of
4//! momentum: how far the Awesome Oscillator stands from its own recent average.
5//!
6//! # Formula
7//! ```text
8//! median = (high + low) / 2
9//! AO = SMA(median, 5) - SMA(median, 34)
10//! AC = AO - SMA(AO, 5)
11//! ```
12//! The first value is on bar 37, once the slow average and the AO average are complete.
13//!
14//! # Presentation
15//! Columns, green when AC rises above its previous value and red when it falls or holds.
16//!
17//! # Example
18//! ```
19//! # 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();
20//! use egui_charts::studies::{AcceleratorOscillator, Indicator};
21//!
22//! let mut ac = AcceleratorOscillator::new();
23//! ac.calculate(&bars);
24//! assert!(ac.values().value(0, 37).is_some());
25//! ```
26
27use super::over_finite_bars;
28use crate::model::Bar;
29use crate::studies::palette;
30use crate::studies::stepper::rolling_mean;
31use crate::studies::stepper::{update_window, usable_behind};
32use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
33use egui::Color32;
34
35/// Accelerator Oscillator, with Bill Williams' fixed lengths 5, 34 and 5.
36#[derive(Clone)]
37pub struct AcceleratorOscillator {
38    values: IndicatorValues,
39    rising_color: Color32,
40    falling_color: Color32,
41    visible: bool,
42}
43
44impl AcceleratorOscillator {
45    /// Create an Accelerator Oscillator.
46    pub fn new() -> Self {
47        Self {
48            values: IndicatorValues::new(),
49            rising_color: palette::GREEN,
50            falling_color: palette::RED,
51            visible: true,
52        }
53    }
54
55    /// The values over `data`, every bar of which has a finite high and low.
56    fn compute(data: &[Bar]) -> IndicatorValues {
57        let median: Vec<f64> = data.iter().map(|b| (b.high + b.low) / 2.0).collect();
58        let ao: Vec<f64> = rolling_mean(&median, 5)
59            .into_iter()
60            .zip(rolling_mean(&median, 34))
61            .map(|(fast, slow)| fast - slow)
62            .collect();
63        ao.iter()
64            .zip(rolling_mean(&ao, 5))
65            .map(|(ao, average)| IndicatorValue::Single(ao - average))
66            .collect()
67    }
68}
69
70impl Default for AcceleratorOscillator {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl Indicator for AcceleratorOscillator {
77    fn source(&self) -> Option<crate::studies::StudySource> {
78        Some(crate::studies::StudySource::Price(
79            crate::model::PriceSource::HL2,
80        ))
81    }
82
83    fn plot_style(&self, _line: usize) -> crate::studies::PlotStyle {
84        crate::studies::PlotStyle {
85            color: Color32::from_rgb(0, 0, 128),
86            width: 1.0,
87            pattern: crate::model::LineStyle::Solid,
88            plot_type: crate::studies::PlotType::Histogram,
89            visible: true,
90        }
91    }
92
93    fn plot_color(&self, _line: usize, bar: usize) -> Option<Color32> {
94        let value = self.values.value(0, bar)?;
95        // Bars without a value, skipped as non-finite, do not count as the previous one.
96        let previous = (0..bar).rev().find_map(|bar| self.values.value(0, bar));
97        Some(if previous.is_some_and(|previous| value <= previous) {
98            self.falling_color
99        } else {
100            self.rising_color
101        })
102    }
103
104    fn value_format(&self) -> crate::scales::StudyFormat {
105        crate::scales::StudyFormat::Price
106    }
107
108    fn name(&self) -> &str {
109        "Accelerator Oscillator"
110    }
111
112    fn desc(&self) -> &str {
113        "Accelerator Oscillator - Awesome Oscillator minus its 5-bar average"
114    }
115
116    fn calculate(&mut self, data: &[Bar]) {
117        self.update(data, 0);
118    }
119
120    fn update(&mut self, data: &[Bar], from: usize) {
121        let mut values = std::mem::take(&mut self.values);
122        update_window(
123            &mut values,
124            data,
125            from,
126            usable_behind(37, |b| b.high.is_finite() && b.low.is_finite()),
127            |data| {
128                over_finite_bars(
129                    data,
130                    |b| b.high.is_finite() && b.low.is_finite(),
131                    Self::compute,
132                )
133            },
134        );
135        self.values = values;
136    }
137
138    fn is_incremental(&self) -> bool {
139        true
140    }
141
142    fn values(&self) -> &IndicatorValues {
143        &self.values
144    }
145
146    fn colors(&self) -> Vec<Color32> {
147        vec![self.rising_color, self.falling_color]
148    }
149
150    fn set_colors(&mut self, colors: Vec<Color32>) {
151        if let Some(&color) = colors.first() {
152            self.rising_color = color;
153        }
154        if let Some(&color) = colors.get(1) {
155            self.falling_color = color;
156        }
157    }
158
159    fn is_overlay(&self) -> bool {
160        false
161    }
162
163    fn line_count(&self) -> usize {
164        1
165    }
166
167    fn is_visible(&self) -> bool {
168        self.visible
169    }
170
171    fn set_visible(&mut self, visible: bool) {
172        self.visible = visible;
173    }
174
175    fn clone_box(&self) -> Box<dyn Indicator> {
176        Box::new(self.clone())
177    }
178
179    fn line_names(&self) -> Vec<String> {
180        vec!["AC".to_string()]
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    /// A column is red when AC falls or holds, green when it rises or has no previous value;
189    /// a bar without a value is passed over.
190    #[test]
191    fn an_unchanged_value_is_coloured_as_falling() {
192        let mut study = AcceleratorOscillator::new();
193        study.values = [1.0, 1.0, 2.0, 1.0, f64::NAN, 0.5]
194            .into_iter()
195            .map(IndicatorValue::Single)
196            .collect();
197        let (up, down) = (study.rising_color, study.falling_color);
198        let colors: Vec<_> = (0..6).map(|bar| study.plot_color(0, bar)).collect();
199        assert_eq!(
200            colors,
201            [Some(up), Some(down), Some(up), Some(down), None, Some(down)]
202        );
203    }
204}