egui_charts/studies/builtin/
accelerator_oscillator.rs1use 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#[derive(Clone)]
37pub struct AcceleratorOscillator {
38 values: IndicatorValues,
39 rising_color: Color32,
40 falling_color: Color32,
41 visible: bool,
42}
43
44impl AcceleratorOscillator {
45 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 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 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 #[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}