egui_charts/studies/builtin/
ultimate_oscillator.rs1use super::over_finite_bars;
27use crate::model::Bar;
28use crate::studies::stepper::{update_window, usable_behind};
29use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
30use egui::Color32;
31
32#[derive(Clone)]
34pub struct UltimateOscillator {
35 period1: usize, period2: usize, period3: usize, values: IndicatorValues,
39 color: Color32,
40 visible: bool,
41}
42
43impl UltimateOscillator {
44 pub fn new(period1: usize, period2: usize, period3: usize) -> Self {
51 Self {
52 period1: period1.max(1),
53 period2: period2.max(1),
54 period3: period3.max(1),
55 values: IndicatorValues::new(),
56 color: crate::studies::palette::BRIGHT_RED,
57 visible: true,
58 }
59 }
60
61 pub fn default_params() -> Self {
63 Self::new(7, 14, 28)
64 }
65
66 pub fn with_color(mut self, color: Color32) -> Self {
68 self.color = color;
69 self
70 }
71}
72
73impl Default for UltimateOscillator {
75 fn default() -> Self {
76 Self::new(7, 14, 28)
77 }
78}
79
80impl UltimateOscillator {
81 fn compute(&self, data: &[Bar]) -> IndicatorValues {
83 let mut values = IndicatorValues::new();
84 if data.is_empty() {
85 return values;
86 }
87
88 let max_period = self.period1.max(self.period2).max(self.period3);
89 if data.len() < max_period.saturating_add(1) {
90 for _ in 0..data.len() {
91 values.push(IndicatorValue::None);
92 }
93 return values;
94 }
95
96 let mut bp = vec![0.0; data.len()];
98 let mut tr = vec![0.0; data.len()];
99
100 for i in 1..data.len() {
101 let prev_close = data[i - 1].close;
102 let low_min = data[i].low.min(prev_close);
103 let high_max = data[i].high.max(prev_close);
104
105 bp[i] = data[i].close - low_min;
106 tr[i] = high_max - low_min;
107 }
108
109 for i in 0..data.len() {
111 if i < max_period {
112 values.push(IndicatorValue::None);
113 continue;
114 }
115
116 let bp1: f64 = bp[(i + 1 - self.period1)..=i].iter().sum();
118 let tr1: f64 = tr[(i + 1 - self.period1)..=i].iter().sum();
119
120 let bp2: f64 = bp[(i + 1 - self.period2)..=i].iter().sum();
121 let tr2: f64 = tr[(i + 1 - self.period2)..=i].iter().sum();
122
123 let bp3: f64 = bp[(i + 1 - self.period3)..=i].iter().sum();
124 let tr3: f64 = tr[(i + 1 - self.period3)..=i].iter().sum();
125
126 let avg1 = if tr1 != 0.0 { bp1 / tr1 } else { 0.5 };
128 let avg2 = if tr2 != 0.0 { bp2 / tr2 } else { 0.5 };
129 let avg3 = if tr3 != 0.0 { bp3 / tr3 } else { 0.5 };
130
131 let uo = 100.0 * ((4.0 * avg1) + (2.0 * avg2) + avg3) / 7.0;
133 values.push(IndicatorValue::Single(uo));
134 }
135
136 values
137 }
138}
139
140impl Indicator for UltimateOscillator {
141 crate::studies::inputs::study_inputs!(self;
142 period1: length("Period1", 1, usize::MAX),
143 period2: length("Period2", 1, usize::MAX),
144 period3: length("Period3", 1, usize::MAX)
145 ; {});
146
147 fn name(&self) -> &str {
148 "UO"
149 }
150
151 fn desc(&self) -> &str {
152 "Ultimate Oscillator - Multi-timeframe momentum indicator"
153 }
154
155 fn calculate(&mut self, data: &[Bar]) {
156 self.update(data, 0);
157 }
158
159 fn update(&mut self, data: &[Bar], from: usize) {
160 let mut values = std::mem::take(&mut self.values);
161 update_window(
162 &mut values,
163 data,
164 from,
165 usable_behind(self.period1.max(self.period2).max(self.period3), |b| {
166 b.high.is_finite() && b.low.is_finite() && b.close.is_finite()
167 }),
168 |data| {
169 over_finite_bars(
170 data,
171 |b| b.high.is_finite() && b.low.is_finite() && b.close.is_finite(),
172 |data| self.compute(data),
173 )
174 },
175 );
176 self.values = values;
177 }
178
179 fn is_incremental(&self) -> bool {
180 true
181 }
182
183 fn values(&self) -> &IndicatorValues {
184 &self.values
185 }
186
187 fn colors(&self) -> Vec<Color32> {
188 vec![self.color]
189 }
190
191 fn set_colors(&mut self, colors: Vec<Color32>) {
192 if !colors.is_empty() {
193 self.color = colors[0];
194 }
195 }
196
197 fn is_overlay(&self) -> bool {
198 false }
200
201 fn line_count(&self) -> usize {
202 1
203 }
204
205 fn is_visible(&self) -> bool {
206 self.visible
207 }
208
209 fn set_visible(&mut self, visible: bool) {
210 self.visible = visible;
211 }
212
213 fn clone_box(&self) -> Box<dyn Indicator> {
214 Box::new(self.clone())
215 }
216
217 fn line_names(&self) -> Vec<String> {
218 vec![format!(
219 "UO({}, {}, {})",
220 self.period1, self.period2, self.period3
221 )]
222 }
223}