indexes_rs/v2/std_dev/
main.rs

1use crate::v2::std_dev::types::{StandardDeviationConfig, StandardDeviationError, StandardDeviationInput, StandardDeviationOutput, StandardDeviationState, VolatilityLevel};
2
3/// Standard Deviation Indicator
4///
5/// Standard deviation measures the amount of variation or dispersion in a set of values.
6/// It's a fundamental statistical measure used in many other technical indicators.
7///
8/// Formulas:
9/// - Population Standard Deviation: σ = √(Σ(x - μ)² / N)
10/// - Sample Standard Deviation: s = √(Σ(x - μ)² / (N-1))
11///
12/// Where:
13/// - x = individual values
14/// - μ = mean of the values
15/// - N = number of values
16///
17/// Uses:
18/// - Measuring price volatility
19/// - Bollinger Bands calculation
20/// - Risk assessment
21/// - Normalization of other indicators
22/// - Z-score calculations
23pub struct StandardDeviation {
24    state: StandardDeviationState,
25}
26
27impl StandardDeviation {
28    /// Create a new Standard Deviation calculator with default configuration (period=20, sample=true)
29    pub fn new() -> Self {
30        Self::with_config(StandardDeviationConfig::default())
31    }
32
33    /// Create a new Standard Deviation calculator with custom period
34    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    /// Create a new Standard Deviation calculator for population standard deviation
44    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    /// Create a new Standard Deviation calculator for sample standard deviation
54    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    /// Create a new Standard Deviation calculator with custom configuration
64    pub fn with_config(config: StandardDeviationConfig) -> Self {
65        Self {
66            state: StandardDeviationState::new(config),
67        }
68    }
69
70    /// Calculate Standard Deviation for the given input
71    pub fn calculate(&mut self, input: StandardDeviationInput) -> Result<StandardDeviationOutput, StandardDeviationError> {
72        // Validate input
73        self.validate_input(&input)?;
74        self.validate_config()?;
75
76        // Update value history
77        self.update_value_history(input.value);
78
79        // Calculate standard deviation if we have enough data
80        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) // Default values when insufficient data
84        };
85
86        // Calculate derived metrics
87        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        // Classify volatility level
92        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    /// Calculate Standard Deviation for a batch of inputs
106    pub fn calculate_batch(&mut self, inputs: &[StandardDeviationInput]) -> Result<Vec<StandardDeviationOutput>, StandardDeviationError> {
107        inputs.iter().map(|input| self.calculate(*input)).collect()
108    }
109
110    /// Reset the calculator state
111    pub fn reset(&mut self) {
112        self.state = StandardDeviationState::new(self.state.config);
113    }
114
115    /// Get current state (for serialization/debugging)
116    pub fn get_state(&self) -> &StandardDeviationState {
117        &self.state
118    }
119
120    /// Restore state (for deserialization)
121    pub fn set_state(&mut self, state: StandardDeviationState) {
122        self.state = state;
123    }
124
125    /// Get current mean
126    pub fn mean(&self) -> f64 {
127        self.state.current_mean
128    }
129
130    /// Get current volatility level
131    pub fn volatility_level(&self) -> VolatilityLevel {
132        if !self.state.has_sufficient_data {
133            VolatilityLevel::Insufficient
134        } else {
135            // Would need last std_dev to classify properly
136            VolatilityLevel::Normal
137        }
138    }
139
140    /// Check if value is outlier (beyond N standard deviations)
141    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        // Use current state values instead of recalculating
147        let mean = self.state.current_mean;
148
149        // Calculate current std dev from state
150        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    // Private helper methods
162
163    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        // Remove oldest if at capacity
184        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        // Add new value
192        self.state.values.push_back(value);
193        self.state.sum += value;
194        self.state.sum_squared += value * value;
195
196        // Update mean
197        if !self.state.values.is_empty() {
198            self.state.current_mean = self.state.sum / self.state.values.len() as f64;
199        }
200
201        // Check if we have sufficient data
202        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        // Calculate variance using the computational formula
215        // Var = (Σx²)/n - (Σx/n)²
216        let variance_raw = (self.state.sum_squared / n) - (mean * mean);
217
218        // Apply sample vs population correction
219        let variance = if self.state.config.use_sample && n > 1.0 {
220            // Sample variance: multiply by n/(n-1)
221            variance_raw * n / (n - 1.0)
222        } else {
223            // Population variance
224            variance_raw
225        };
226
227        // Ensure variance is non-negative (due to floating point precision)
228        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        // Use coefficient of variation for relative volatility measurement
245        let cv = if mean != 0.0 { (std_dev / mean.abs()) * 100.0 } else { 0.0 };
246
247        // Classification based on coefficient of variation
248        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
264/// Convenience function to calculate standard deviation for a series of values
265pub 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
284/// Calculate rolling standard deviation over a window
285pub 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}