egui_charts/studies/builtin/
aroon.rs1use super::over_finite_bars;
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#[derive(Clone)]
34pub struct Aroon {
35 period: usize,
36 values: IndicatorValues,
37 up_color: Color32,
38 down_color: Color32,
39 visible: bool,
40}
41
42impl Aroon {
43 pub fn new(period: usize) -> Self {
48 Self {
49 period: period.max(1),
50 values: IndicatorValues::new(),
51 up_color: palette::DARK_ORANGE,
52 down_color: palette::BLUE,
53 visible: true,
54 }
55 }
56
57 pub fn default_params() -> Self {
59 Self::new(25)
60 }
61
62 pub fn with_colors(mut self, up: Color32, down: Color32) -> Self {
64 self.up_color = up;
65 self.down_color = down;
66 self
67 }
68}
69
70impl Default for Aroon {
72 fn default() -> Self {
73 Self::new(14)
74 }
75}
76
77impl Aroon {
78 fn compute(&self, data: &[Bar]) -> IndicatorValues {
80 let mut values = IndicatorValues::new();
81 if data.is_empty() {
82 return values;
83 }
84
85 for i in 0..data.len() {
86 if i < self.period {
87 values.push(IndicatorValue::None);
88 continue;
89 }
90
91 let start = i - self.period;
93 let mut highest_idx = start;
94 let mut lowest_idx = start;
95 let mut highest = data[start].high;
96 let mut lowest = data[start].low;
97
98 for j in (start + 1)..=i {
99 if data[j].high >= highest {
100 highest = data[j].high;
101 highest_idx = j;
102 }
103 if data[j].low <= lowest {
104 lowest = data[j].low;
105 lowest_idx = j;
106 }
107 }
108
109 let days_since_high = i - highest_idx;
111 let days_since_low = i - lowest_idx;
112
113 let aroon_up = ((self.period - days_since_high) as f64 / self.period as f64) * 100.0;
115 let aroon_down = ((self.period - days_since_low) as f64 / self.period as f64) * 100.0;
116
117 values.push_lines(&[aroon_up, aroon_down]);
118 }
119
120 values
121 }
122}
123
124impl Indicator for Aroon {
125 crate::studies::inputs::study_inputs!(self;
126 period: length("Period", 1, usize::MAX)
127 ; {});
128
129 fn name(&self) -> &str {
130 "Aroon"
131 }
132
133 fn desc(&self) -> &str {
134 "Aroon - Trend identification indicator"
135 }
136
137 fn calculate(&mut self, data: &[Bar]) {
138 self.update(data, 0);
139 }
140
141 fn update(&mut self, data: &[Bar], from: usize) {
142 let mut values = std::mem::take(&mut self.values);
143 update_window(
144 &mut values,
145 data,
146 from,
147 usable_behind(self.period, |b| b.high.is_finite() && b.low.is_finite()),
148 |data| {
149 over_finite_bars(
150 data,
151 |b| b.high.is_finite() && b.low.is_finite(),
152 |data| self.compute(data),
153 )
154 },
155 );
156 self.values = values;
157 }
158
159 fn is_incremental(&self) -> bool {
160 true
161 }
162
163 fn values(&self) -> &IndicatorValues {
164 &self.values
165 }
166
167 fn colors(&self) -> Vec<Color32> {
168 vec![self.up_color, self.down_color]
169 }
170
171 fn set_colors(&mut self, colors: Vec<Color32>) {
172 if !colors.is_empty() {
173 self.up_color = colors[0];
174 }
175 if colors.len() > 1 {
176 self.down_color = colors[1];
177 }
178 }
179
180 fn is_overlay(&self) -> bool {
181 false }
183
184 fn line_count(&self) -> usize {
185 2 }
187
188 fn is_visible(&self) -> bool {
189 self.visible
190 }
191
192 fn set_visible(&mut self, visible: bool) {
193 self.visible = visible;
194 }
195
196 fn clone_box(&self) -> Box<dyn Indicator> {
197 Box::new(self.clone())
198 }
199
200 fn line_names(&self) -> Vec<String> {
201 vec![
202 format!("Aroon Up({})", self.period),
203 format!("Aroon Down({})", self.period),
204 ]
205 }
206}