egui_charts/studies/builtin/
roc.rs1use crate::model::Bar;
26use crate::studies::palette;
27use crate::studies::stepper::update_window;
28use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
29use egui::Color32;
30
31#[derive(Clone)]
33pub struct RateOfChange {
34 period: usize,
35 values: IndicatorValues,
36 color: Color32,
37 visible: bool,
38}
39
40impl RateOfChange {
41 pub fn new(period: usize) -> Self {
46 Self {
47 period: period.max(1),
48 values: IndicatorValues::new(),
49 color: palette::BLUE,
50 visible: true,
51 }
52 }
53
54 pub fn with_color(mut self, color: Color32) -> Self {
56 self.color = color;
57 self
58 }
59}
60
61impl Default for RateOfChange {
63 fn default() -> Self {
64 Self::new(9)
65 }
66}
67
68impl RateOfChange {
69 fn compute(&self, data: &[Bar]) -> IndicatorValues {
70 let mut values = IndicatorValues::new();
71
72 if data.len() <= self.period {
73 for _ in 0..data.len() {
74 values.push(IndicatorValue::None);
75 }
76 return values;
77 }
78
79 for i in 0..data.len() {
80 if i < self.period {
81 values.push(IndicatorValue::None);
82 } else {
83 let prev_close = data[i - self.period].close;
84 if prev_close == 0.0 {
85 values.push(IndicatorValue::None);
86 } else {
87 let roc = ((data[i].close - prev_close) / prev_close) * 100.0;
88 values.push(IndicatorValue::Single(roc));
89 }
90 }
91 }
92 values
93 }
94}
95
96impl Indicator for RateOfChange {
97 fn source(&self) -> Option<crate::studies::StudySource> {
98 Some(crate::studies::StudySource::Price(
99 crate::model::PriceSource::Close,
100 ))
101 }
102
103 crate::studies::inputs::study_inputs!(self;
104 period: length("Period", 1, usize::MAX)
105 ; {});
106
107 fn value_format(&self) -> crate::scales::StudyFormat {
108 crate::scales::StudyFormat::Percent
109 }
110
111 fn name(&self) -> &str {
112 "ROC"
113 }
114
115 fn desc(&self) -> &str {
116 "Rate of Change - Momentum oscillator (percentage)"
117 }
118
119 fn calculate(&mut self, data: &[Bar]) {
120 self.update(data, 0);
121 }
122
123 fn update(&mut self, data: &[Bar], from: usize) {
124 let mut values = std::mem::take(&mut self.values);
125 update_window(
126 &mut values,
127 data,
128 from,
129 |_data, from| from.saturating_sub(self.period),
130 |data| self.compute(data),
131 );
132 self.values = values;
133 }
134
135 fn is_incremental(&self) -> bool {
136 true
137 }
138
139 fn values(&self) -> &IndicatorValues {
140 &self.values
141 }
142
143 fn colors(&self) -> Vec<Color32> {
144 vec![self.color]
145 }
146
147 fn set_colors(&mut self, colors: Vec<Color32>) {
148 if !colors.is_empty() {
149 self.color = colors[0];
150 }
151 }
152
153 fn is_overlay(&self) -> bool {
154 false }
156
157 fn is_visible(&self) -> bool {
158 self.visible
159 }
160
161 fn set_visible(&mut self, visible: bool) {
162 self.visible = visible;
163 }
164
165 fn clone_box(&self) -> Box<dyn Indicator> {
166 Box::new(self.clone())
167 }
168
169 fn line_names(&self) -> Vec<String> {
170 vec![format!("ROC({})", self.period)]
171 }
172}