egui_charts/studies/builtin/
williams_r.rs1use super::over_finite_bars;
23use crate::model::Bar;
24use crate::studies::palette;
25use crate::studies::stepper::{rolling_max, rolling_min};
26use crate::studies::stepper::{update_window, usable_behind};
27use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
28use egui::Color32;
29
30#[derive(Clone)]
32pub struct WilliamsR {
33 period: usize,
34 values: IndicatorValues,
35 color: Color32,
36 visible: bool,
37}
38
39impl WilliamsR {
40 pub fn new(period: usize) -> Self {
45 Self {
46 period: period.max(1),
47 values: IndicatorValues::new(),
48 color: palette::PURPLE,
49 visible: true,
50 }
51 }
52
53 pub fn with_color(mut self, color: Color32) -> Self {
55 self.color = color;
56 self
57 }
58}
59
60impl Default for WilliamsR {
62 fn default() -> Self {
63 Self::new(14)
64 }
65}
66
67impl WilliamsR {
68 fn compute(&self, data: &[Bar]) -> IndicatorValues {
70 let mut values = IndicatorValues::new();
71 if data.len() < self.period {
72 for _ in 0..data.len() {
73 values.push(IndicatorValue::None);
74 }
75 return values;
76 }
77
78 let highest = rolling_max(data.iter().map(|b| b.high), self.period);
79 let lowest = rolling_min(data.iter().map(|b| b.low), self.period);
80 for i in 0..data.len() {
81 if i < self.period - 1 {
82 values.push(IndicatorValue::None);
83 } else {
84 let (highest, lowest) = (highest[i], lowest[i]);
85 let range = highest - lowest;
86
87 if range == 0.0 {
88 values.push(IndicatorValue::Single(-50.0)); } else {
90 let r = (highest - data[i].close) / range * -100.0;
91 values.push(IndicatorValue::Single(r.clamp(-100.0, 0.0)));
92 }
93 }
94 }
95
96 values
97 }
98}
99
100impl Indicator for WilliamsR {
101 crate::studies::inputs::study_inputs!(self;
102 period: length("Period", 1, usize::MAX)
103 ; {});
104
105 fn value_format(&self) -> crate::scales::StudyFormat {
106 crate::scales::StudyFormat::Percent
107 }
108
109 fn name(&self) -> &str {
110 "Williams %R"
111 }
112
113 fn desc(&self) -> &str {
114 "Williams %R - Momentum oscillator (-100 to 0)"
115 }
116
117 fn calculate(&mut self, data: &[Bar]) {
118 self.update(data, 0);
119 }
120
121 fn update(&mut self, data: &[Bar], from: usize) {
122 let mut values = std::mem::take(&mut self.values);
123 update_window(
124 &mut values,
125 data,
126 from,
127 usable_behind(self.period - 1, |b| {
128 b.high.is_finite() && b.low.is_finite() && b.close.is_finite()
129 }),
130 |data| {
131 over_finite_bars(
132 data,
133 |b| b.high.is_finite() && b.low.is_finite() && b.close.is_finite(),
134 |data| self.compute(data),
135 )
136 },
137 );
138 self.values = values;
139 }
140
141 fn is_incremental(&self) -> bool {
142 true
143 }
144
145 fn values(&self) -> &IndicatorValues {
146 &self.values
147 }
148
149 fn colors(&self) -> Vec<Color32> {
150 vec![self.color]
151 }
152
153 fn set_colors(&mut self, colors: Vec<Color32>) {
154 if !colors.is_empty() {
155 self.color = colors[0];
156 }
157 }
158
159 fn is_overlay(&self) -> bool {
160 false }
162
163 fn is_visible(&self) -> bool {
164 self.visible
165 }
166
167 fn set_visible(&mut self, visible: bool) {
168 self.visible = visible;
169 }
170
171 fn clone_box(&self) -> Box<dyn Indicator> {
172 Box::new(self.clone())
173 }
174
175 fn line_names(&self) -> Vec<String> {
176 vec![format!("%R({})", self.period)]
177 }
178}