indexes_rs/v2/williams_r/
main.rs1use crate::v2::williams_r::types::{WilliamsRConfig, WilliamsRError, WilliamsRInput, WilliamsRMarketCondition, WilliamsROutput, WilliamsRState};
2
3pub struct WilliamsR {
28 state: WilliamsRState,
29}
30
31impl WilliamsR {
32 pub fn new() -> Self {
34 Self {
36 state: WilliamsRState::new(WilliamsRConfig::default()),
37 }
38 }
39
40 pub fn with_period(period: usize) -> Result<Self, WilliamsRError> {
42 if period == 0 {
43 return Err(WilliamsRError::InvalidPeriod);
44 }
45
46 let config = WilliamsRConfig { period, ..Default::default() };
47 Ok(Self::with_config(config))
48 }
49
50 pub fn with_thresholds(period: usize, overbought: f64, oversold: f64, extreme_overbought: f64, extreme_oversold: f64) -> Result<Self, WilliamsRError> {
52 if period == 0 {
53 return Err(WilliamsRError::InvalidPeriod);
54 }
55
56 if overbought >= 0.0
61 || oversold >= overbought || extreme_overbought >= 0.0
63 || extreme_overbought <= overbought || extreme_oversold >= oversold
65 {
66 return Err(WilliamsRError::InvalidThresholds);
68 }
69
70 let config = WilliamsRConfig {
71 period,
72 overbought,
73 oversold,
74 extreme_overbought,
75 extreme_oversold,
76 };
77 Ok(Self::with_config(config))
78 }
79
80 pub fn with_config(config: WilliamsRConfig) -> Self {
82 Self {
83 state: WilliamsRState::new(config),
84 }
85 }
86
87 pub fn calculate(&mut self, input: WilliamsRInput) -> Result<WilliamsROutput, WilliamsRError> {
89 self.validate_input(&input)?;
91 self.validate_config()?;
92
93 self.update_price_history(input.high, input.low);
95
96 let williams_r = if self.state.has_sufficient_data {
98 self.calculate_williams_r_value(input.close)?
99 } else {
100 -50.0 };
102
103 let market_condition = self.determine_market_condition(williams_r);
105
106 let distance_from_overbought = williams_r - self.state.config.overbought;
108 let distance_from_oversold = williams_r - self.state.config.oversold;
109
110 let price_range = self.state.highest_high - self.state.lowest_low;
112
113 Ok(WilliamsROutput {
114 williams_r,
115 highest_high: self.state.highest_high,
116 lowest_low: self.state.lowest_low,
117 close: input.close,
118 price_range,
119 market_condition,
120 distance_from_overbought,
121 distance_from_oversold,
122 })
123 }
124
125 pub fn calculate_batch(&mut self, inputs: &[WilliamsRInput]) -> Result<Vec<WilliamsROutput>, WilliamsRError> {
127 inputs.iter().map(|input| self.calculate(*input)).collect()
128 }
129
130 pub fn reset(&mut self) {
132 self.state = WilliamsRState::new(self.state.config);
133 }
134
135 pub fn get_state(&self) -> &WilliamsRState {
137 &self.state
138 }
139
140 pub fn set_state(&mut self, state: WilliamsRState) {
142 self.state = state;
143 }
144
145 pub fn is_overbought(&self, williams_r: f64) -> bool {
147 williams_r >= self.state.config.overbought
148 }
149
150 pub fn is_oversold(&self, williams_r: f64) -> bool {
152 williams_r <= self.state.config.oversold
153 }
154
155 pub fn is_extreme_condition(&self, williams_r: f64) -> bool {
157 williams_r >= self.state.config.extreme_overbought || williams_r <= self.state.config.extreme_oversold
158 }
159
160 pub fn signal_strength(&self, williams_r: f64) -> f64 {
162 let distance_from_center = (williams_r + 50.0).abs(); (distance_from_center / 50.0).min(1.0)
166 }
167
168 fn validate_input(&self, input: &WilliamsRInput) -> Result<(), WilliamsRError> {
171 if !input.high.is_finite() || !input.low.is_finite() || !input.close.is_finite() {
173 return Err(WilliamsRError::InvalidPrice);
174 }
175
176 if input.high < input.low {
178 return Err(WilliamsRError::InvalidHLC);
179 }
180
181 if input.close < input.low || input.close > input.high {
182 return Err(WilliamsRError::InvalidHLC);
183 }
184
185 Ok(())
186 }
187
188 fn validate_config(&self) -> Result<(), WilliamsRError> {
189 if self.state.config.period == 0 {
190 return Err(WilliamsRError::InvalidPeriod);
191 }
192
193 let config = &self.state.config;
194
195 if config.overbought >= 0.0
199 || config.oversold >= config.overbought
200 || config.extreme_overbought >= 0.0
201 || config.extreme_overbought <= config.overbought
202 || config.extreme_oversold >= config.oversold
203 {
204 return Err(WilliamsRError::InvalidThresholds);
205 }
206
207 Ok(())
208 }
209
210 fn update_price_history(&mut self, high: f64, low: f64) {
211 if self.state.highs.len() >= self.state.config.period {
213 self.state.highs.pop_front();
214 self.state.lows.pop_front();
215 }
216
217 self.state.highs.push_back(high);
219 self.state.lows.push_back(low);
220
221 self.update_extremes();
223
224 self.state.has_sufficient_data = self.state.highs.len() >= self.state.config.period;
226 }
227
228 fn update_extremes(&mut self) {
229 if self.state.highs.is_empty() {
230 return;
231 }
232
233 self.state.highest_high = self.state.highs.iter().fold(f64::NEG_INFINITY, |acc, &x| acc.max(x));
235 self.state.lowest_low = self.state.lows.iter().fold(f64::INFINITY, |acc, &x| acc.min(x));
236 }
237
238 fn calculate_williams_r_value(&self, close: f64) -> Result<f64, WilliamsRError> {
239 if !self.state.has_sufficient_data {
240 return Ok(-50.0); }
242
243 let price_range = self.state.highest_high - self.state.lowest_low;
244
245 if price_range == 0.0 {
246 return Ok(-50.0);
248 }
249
250 let williams_r = ((self.state.highest_high - close) / price_range) * -100.0;
252
253 if !williams_r.is_finite() {
254 return Err(WilliamsRError::DivisionByZero);
255 }
256
257 Ok(williams_r.clamp(-100.0, 0.0))
259 }
260
261 fn determine_market_condition(&self, williams_r: f64) -> WilliamsRMarketCondition {
262 if !self.state.has_sufficient_data {
263 WilliamsRMarketCondition::Insufficient
264 } else if williams_r >= self.state.config.extreme_overbought {
265 WilliamsRMarketCondition::ExtremeOverbought
266 } else if williams_r >= self.state.config.overbought {
267 WilliamsRMarketCondition::Overbought
268 } else if williams_r <= self.state.config.extreme_oversold {
269 WilliamsRMarketCondition::ExtremeOversold
270 } else if williams_r <= self.state.config.oversold {
271 WilliamsRMarketCondition::Oversold
272 } else {
273 WilliamsRMarketCondition::Normal
274 }
275 }
276}
277
278impl Default for WilliamsR {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284pub fn calculate_williams_r_simple(highs: &[f64], lows: &[f64], closes: &[f64], period: usize) -> Result<Vec<f64>, WilliamsRError> {
286 let len = highs.len();
287 if len != lows.len() || len != closes.len() {
288 return Err(WilliamsRError::InvalidInput("All price arrays must have same length".to_string()));
289 }
290
291 if len == 0 {
292 return Ok(Vec::new());
293 }
294
295 let mut williams_r_calculator = WilliamsR::with_period(period)?;
296 let mut results = Vec::with_capacity(len);
297
298 for i in 0..len {
299 let input = WilliamsRInput {
300 high: highs[i],
301 low: lows[i],
302 close: closes[i],
303 };
304 let output = williams_r_calculator.calculate(input)?;
305 results.push(output.williams_r);
306 }
307
308 Ok(results)
309}