indexes_rs/v2/std_dev/
main.rs1use crate::v2::std_dev::types::{StandardDeviationConfig, StandardDeviationError, StandardDeviationInput, StandardDeviationOutput, StandardDeviationState, VolatilityLevel};
2
3pub struct StandardDeviation {
24 state: StandardDeviationState,
25}
26
27impl StandardDeviation {
28 pub fn new() -> Self {
30 Self::with_config(StandardDeviationConfig::default())
31 }
32
33 pub fn with_period(period: usize) -> Result<Self, StandardDeviationError> {
35 if period == 0 {
36 return Err(StandardDeviationError::InvalidPeriod);
37 }
38
39 let config = StandardDeviationConfig { period, ..Default::default() };
40 Ok(Self::with_config(config))
41 }
42
43 pub fn population(period: usize) -> Result<Self, StandardDeviationError> {
45 if period == 0 {
46 return Err(StandardDeviationError::InvalidPeriod);
47 }
48
49 let config = StandardDeviationConfig { period, use_sample: false };
50 Ok(Self::with_config(config))
51 }
52
53 pub fn sample(period: usize) -> Result<Self, StandardDeviationError> {
55 if period <= 1 {
56 return Err(StandardDeviationError::InvalidPeriod);
57 }
58
59 let config = StandardDeviationConfig { period, use_sample: true };
60 Ok(Self::with_config(config))
61 }
62
63 pub fn with_config(config: StandardDeviationConfig) -> Self {
65 Self {
66 state: StandardDeviationState::new(config),
67 }
68 }
69
70 pub fn calculate(&mut self, input: StandardDeviationInput) -> Result<StandardDeviationOutput, StandardDeviationError> {
72 self.validate_input(&input)?;
74 self.validate_config()?;
75
76 self.update_value_history(input.value);
78
79 let (std_dev, variance, mean) = if self.state.has_sufficient_data {
81 self.calculate_standard_deviation()?
82 } else {
83 (0.0, 0.0, input.value) };
85
86 let z_score = if std_dev != 0.0 { (input.value - mean) / std_dev } else { 0.0 };
88
89 let coefficient_of_variation = if mean != 0.0 { (std_dev / mean.abs()) * 100.0 } else { 0.0 };
90
91 let volatility_level = self.classify_volatility(std_dev, mean);
93
94 Ok(StandardDeviationOutput {
95 std_dev,
96 variance,
97 mean,
98 current_value: input.value,
99 z_score,
100 coefficient_of_variation,
101 volatility_level,
102 })
103 }
104
105 pub fn calculate_batch(&mut self, inputs: &[StandardDeviationInput]) -> Result<Vec<StandardDeviationOutput>, StandardDeviationError> {
107 inputs.iter().map(|input| self.calculate(*input)).collect()
108 }
109
110 pub fn reset(&mut self) {
112 self.state = StandardDeviationState::new(self.state.config);
113 }
114
115 pub fn get_state(&self) -> &StandardDeviationState {
117 &self.state
118 }
119
120 pub fn set_state(&mut self, state: StandardDeviationState) {
122 self.state = state;
123 }
124
125 pub fn mean(&self) -> f64 {
127 self.state.current_mean
128 }
129
130 pub fn volatility_level(&self) -> VolatilityLevel {
132 if !self.state.has_sufficient_data {
133 VolatilityLevel::Insufficient
134 } else {
135 VolatilityLevel::Normal
137 }
138 }
139
140 pub fn is_outlier(&self, value: f64, threshold_std_devs: f64) -> bool {
142 if !self.state.has_sufficient_data {
143 return false;
144 }
145
146 let mean = self.state.current_mean;
148
149 if let Ok((std_dev, _, _)) = self.calculate_standard_deviation() {
151 if std_dev == 0.0 {
152 return false;
153 }
154 let z_score = (value - mean) / std_dev;
155 z_score.abs() > threshold_std_devs
156 } else {
157 false
158 }
159 }
160
161 fn validate_input(&self, input: &StandardDeviationInput) -> Result<(), StandardDeviationError> {
164 if !input.value.is_finite() {
165 return Err(StandardDeviationError::InvalidValue);
166 }
167 Ok(())
168 }
169
170 fn validate_config(&self) -> Result<(), StandardDeviationError> {
171 if self.state.config.period == 0 {
172 return Err(StandardDeviationError::InvalidPeriod);
173 }
174
175 if self.state.config.use_sample && self.state.config.period <= 1 {
176 return Err(StandardDeviationError::InvalidPeriod);
177 }
178
179 Ok(())
180 }
181
182 fn update_value_history(&mut self, value: f64) {
183 if self.state.values.len() >= self.state.config.period {
185 if let Some(oldest) = self.state.values.pop_front() {
186 self.state.sum -= oldest;
187 self.state.sum_squared -= oldest * oldest;
188 }
189 }
190
191 self.state.values.push_back(value);
193 self.state.sum += value;
194 self.state.sum_squared += value * value;
195
196 if !self.state.values.is_empty() {
198 self.state.current_mean = self.state.sum / self.state.values.len() as f64;
199 }
200
201 let min_required = if self.state.config.use_sample { 2 } else { 1 };
203 self.state.has_sufficient_data = self.state.values.len() >= self.state.config.period.max(min_required);
204 }
205
206 fn calculate_standard_deviation(&self) -> Result<(f64, f64, f64), StandardDeviationError> {
207 if !self.state.has_sufficient_data {
208 return Ok((0.0, 0.0, self.state.current_mean));
209 }
210
211 let n = self.state.values.len() as f64;
212 let mean = self.state.sum / n;
213
214 let variance_raw = (self.state.sum_squared / n) - (mean * mean);
217
218 let variance = if self.state.config.use_sample && n > 1.0 {
220 variance_raw * n / (n - 1.0)
222 } else {
223 variance_raw
225 };
226
227 let variance = variance.max(0.0);
229
230 let std_dev = variance.sqrt();
231
232 if !std_dev.is_finite() {
233 return Err(StandardDeviationError::DivisionByZero);
234 }
235
236 Ok((std_dev, variance, mean))
237 }
238
239 fn classify_volatility(&self, std_dev: f64, mean: f64) -> VolatilityLevel {
240 if !self.state.has_sufficient_data {
241 return VolatilityLevel::Insufficient;
242 }
243
244 let cv = if mean != 0.0 { (std_dev / mean.abs()) * 100.0 } else { 0.0 };
246
247 match cv {
249 cv if cv < 5.0 => VolatilityLevel::VeryLow,
250 cv if cv < 15.0 => VolatilityLevel::Low,
251 cv if cv < 25.0 => VolatilityLevel::Normal,
252 cv if cv < 50.0 => VolatilityLevel::High,
253 _ => VolatilityLevel::VeryHigh,
254 }
255 }
256}
257
258impl Default for StandardDeviation {
259 fn default() -> Self {
260 Self::new()
261 }
262}
263
264pub fn calculate_standard_deviation_simple(values: &[f64], period: usize, use_sample: bool) -> Result<Vec<f64>, StandardDeviationError> {
266 if values.is_empty() {
267 return Ok(Vec::new());
268 }
269
270 let config = StandardDeviationConfig { period, use_sample };
271
272 let mut std_dev_calculator = StandardDeviation::with_config(config);
273 let mut results = Vec::with_capacity(values.len());
274
275 for &value in values {
276 let input = StandardDeviationInput { value };
277 let output = std_dev_calculator.calculate(input)?;
278 results.push(output.std_dev);
279 }
280
281 Ok(results)
282}
283
284pub fn rolling_standard_deviation(values: &[f64], window: usize, use_sample: bool) -> Result<Vec<f64>, StandardDeviationError> {
286 if values.is_empty() || window == 0 {
287 return Ok(Vec::new());
288 }
289
290 if values.len() < window {
291 return Ok(vec![0.0; values.len()]);
292 }
293
294 let mut results = Vec::with_capacity(values.len());
295
296 for i in 0..values.len() {
297 let start = (i + 1).saturating_sub(window);
298 let end = i + 1;
299 let window_values = &values[start..end];
300
301 if window_values.len() >= if use_sample { 2 } else { 1 } {
302 let mean = window_values.iter().sum::<f64>() / window_values.len() as f64;
303 let variance = window_values.iter().map(|x| (x - mean).powi(2)).sum::<f64>()
304 / if use_sample && window_values.len() > 1 {
305 window_values.len() - 1
306 } else {
307 window_values.len()
308 } as f64;
309
310 results.push(variance.sqrt());
311 } else {
312 results.push(0.0);
313 }
314 }
315
316 Ok(results)
317}