optionstratlib_math/geometrics/analysis/
traits.rs1use crate::error::MetricsError;
2use crate::geometrics::{
3 BasicMetrics, Metrics, RangeMetrics, RiskMetrics, ShapeMetrics, TrendMetrics,
4};
5use optionstratlib_core::utils::Len;
6
7pub trait MetricsExtractor: Len {
31 fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError>;
44
45 fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError>;
57
58 fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError>;
70
71 fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError>;
83
84 fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError>;
99
100 fn compute_curve_metrics(&self) -> Result<Metrics, MetricsError> {
113 let basic = self.compute_basic_metrics()?;
114 let shape = self.compute_shape_metrics()?;
115 let range = self.compute_range_metrics()?;
116 let trend = self.compute_trend_metrics()?;
117 let risk = self.compute_risk_metrics()?;
118
119 Ok(Metrics::new(basic, shape, range, trend, risk))
120 }
121
122 fn compute_surface_metrics(&self) -> Result<Metrics, MetricsError> {
148 let basic = self.compute_basic_metrics()?;
149 let shape = self.compute_shape_metrics()?;
150 let range = self.compute_range_metrics()?;
151 let trend = self.compute_trend_metrics()?;
152 let risk = self.compute_risk_metrics()?;
153 Ok(Metrics::new(basic, shape, range, trend, risk))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::curves::Point2D;
161 use num_traits::FromPrimitive;
162 use rust_decimal::Decimal;
163 use rust_decimal_macros::dec;
164
165 struct MockCurve;
167
168 fn create_test_point(x: f64, y: f64) -> Point2D {
170 Point2D::new(Decimal::from_f64(x).unwrap(), Decimal::from_f64(y).unwrap())
171 }
172
173 impl Len for MockCurve {
174 fn len(&self) -> usize {
175 1
176 }
177 }
178
179 impl MetricsExtractor for MockCurve {
181 fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError> {
182 Ok(BasicMetrics {
183 mean: dec!(10.5),
184 median: dec!(10.0),
185 mode: dec!(9.0),
186 std_dev: dec!(1.2),
187 })
188 }
189
190 fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError> {
191 Ok(ShapeMetrics {
192 skewness: dec!(0.5),
193 kurtosis: dec!(3.0),
194 peaks: vec![create_test_point(1.0, 10.0)],
195 valleys: vec![create_test_point(2.0, 5.0)],
196 inflection_points: vec![create_test_point(1.5, 7.5)],
197 })
198 }
199
200 fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError> {
201 Ok(RangeMetrics {
202 min: create_test_point(0.0, 5.0),
203 max: create_test_point(10.0, 15.0),
204 range: dec!(10.0),
205 quartiles: (dec!(7.0), dec!(10.0), dec!(13.0)),
206 interquartile_range: dec!(6.0),
207 })
208 }
209
210 fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError> {
211 Ok(TrendMetrics {
212 slope: dec!(1.5),
213 intercept: dec!(2.0),
214 r_squared: dec!(0.95),
215 moving_average: vec![create_test_point(1.0, 3.5), create_test_point(2.0, 5.0)],
216 })
217 }
218
219 fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError> {
220 Ok(RiskMetrics {
221 volatility: dec!(0.15),
222 value_at_risk: dec!(0.05),
223 expected_shortfall: dec!(0.07),
224 coefficient_of_variation: dec!(1.2),
225 sharpe_ratio: dec!(2.5),
226 })
227 }
228 }
229
230 struct ErrorMockCurve;
232
233 impl Len for ErrorMockCurve {
234 fn len(&self) -> usize {
235 1
236 }
237 }
238
239 impl MetricsExtractor for ErrorMockCurve {
240 fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError> {
241 Err(MetricsError::BasicError(
242 "Basic metrics computation failed".to_string(),
243 ))
244 }
245
246 fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError> {
247 Err(MetricsError::ShapeError(
248 "Shape metrics computation failed".to_string(),
249 ))
250 }
251
252 fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError> {
253 Err(MetricsError::RangeError(
254 "Range metrics computation failed".to_string(),
255 ))
256 }
257
258 fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError> {
259 Err(MetricsError::TrendError(
260 "Trend metrics computation failed".to_string(),
261 ))
262 }
263
264 fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError> {
265 Err(MetricsError::RiskError(
266 "Risk metrics computation failed".to_string(),
267 ))
268 }
269 }
270
271 mod test_successful_computations {
272 use super::*;
273
274 #[test]
275 fn test_compute_basic_metrics() {
276 let curve = MockCurve;
277 let result = curve.compute_basic_metrics();
278 assert!(result.is_ok());
279 let metrics = result.unwrap();
280 assert_eq!(metrics.mean, dec!(10.5));
281 assert_eq!(metrics.median, dec!(10.0));
282 assert_eq!(metrics.mode, dec!(9.0));
283 assert_eq!(metrics.std_dev, dec!(1.2));
284 }
285
286 #[test]
287 fn test_compute_shape_metrics() {
288 let curve = MockCurve;
289 let result = curve.compute_shape_metrics();
290 assert!(result.is_ok());
291 let metrics = result.unwrap();
292 assert_eq!(metrics.skewness, dec!(0.5));
293 assert_eq!(metrics.kurtosis, dec!(3.0));
294 assert_eq!(metrics.peaks.len(), 1);
295 assert_eq!(metrics.valleys.len(), 1);
296 assert_eq!(metrics.inflection_points.len(), 1);
297 }
298
299 #[test]
300 fn test_compute_range_metrics() {
301 let curve = MockCurve;
302 let result = curve.compute_range_metrics();
303 assert!(result.is_ok());
304 let metrics = result.unwrap();
305 assert_eq!(metrics.range, dec!(10.0));
306 assert_eq!(metrics.quartiles.0, dec!(7.0));
307 assert_eq!(metrics.interquartile_range, dec!(6.0));
308 }
309
310 #[test]
311 fn test_compute_trend_metrics() {
312 let curve = MockCurve;
313 let result = curve.compute_trend_metrics();
314 assert!(result.is_ok());
315 let metrics = result.unwrap();
316 assert_eq!(metrics.slope, dec!(1.5));
317 assert_eq!(metrics.intercept, dec!(2.0));
318 assert_eq!(metrics.r_squared, dec!(0.95));
319 assert_eq!(metrics.moving_average.len(), 2);
320 }
321
322 #[test]
323 fn test_compute_risk_metrics() {
324 let curve = MockCurve;
325 let result = curve.compute_risk_metrics();
326 assert!(result.is_ok());
327 let metrics = result.unwrap();
328 assert_eq!(metrics.volatility, dec!(0.15));
329 assert_eq!(metrics.value_at_risk, dec!(0.05));
330 assert_eq!(metrics.expected_shortfall, dec!(0.07));
331 assert_eq!(metrics.coefficient_of_variation, dec!(1.2));
332 assert_eq!(metrics.sharpe_ratio, dec!(2.5));
333 }
334
335 #[test]
336 fn test_compute_curve_metrics() {
337 let curve = MockCurve;
338 let result = curve.compute_curve_metrics();
339 assert!(result.is_ok());
340
341 let metrics = result.unwrap();
342 assert_eq!(metrics.basic.mean, dec!(10.5));
343 assert_eq!(metrics.shape.skewness, dec!(0.5));
344 assert_eq!(metrics.range.range, dec!(10.0));
345 assert_eq!(metrics.trend.slope, dec!(1.5));
346 assert_eq!(metrics.risk.volatility, dec!(0.15));
347 }
348 }
349
350 mod test_error_cases {
351 use super::*;
352
353 #[test]
354 fn test_basic_metrics_error() {
355 let curve = ErrorMockCurve;
356 let result = curve.compute_basic_metrics();
357 assert!(result.is_err());
358 assert_eq!(
359 result.unwrap_err().to_string(),
360 "Basic Error: Basic metrics computation failed"
361 );
362 }
363
364 #[test]
365 fn test_shape_metrics_error() {
366 let curve = ErrorMockCurve;
367 let result = curve.compute_shape_metrics();
368 assert!(result.is_err());
369 assert_eq!(
370 result.unwrap_err().to_string(),
371 "Shape Error: Shape metrics computation failed"
372 );
373 }
374
375 #[test]
376 fn test_range_metrics_error() {
377 let curve = ErrorMockCurve;
378 let result = curve.compute_range_metrics();
379 assert!(result.is_err());
380 assert_eq!(
381 result.unwrap_err().to_string(),
382 "Range Error: Range metrics computation failed"
383 );
384 }
385
386 #[test]
387 fn test_trend_metrics_error() {
388 let curve = ErrorMockCurve;
389 let result = curve.compute_trend_metrics();
390 assert!(result.is_err());
391 assert_eq!(
392 result.unwrap_err().to_string(),
393 "Trend Error: Trend metrics computation failed"
394 );
395 }
396
397 #[test]
398 fn test_risk_metrics_error() {
399 let curve = ErrorMockCurve;
400 let result = curve.compute_risk_metrics();
401 assert!(result.is_err());
402 assert_eq!(
403 result.unwrap_err().to_string(),
404 "Risk Error: Risk metrics computation failed"
405 );
406 }
407
408 #[test]
409 fn test_curve_metrics_error_propagation() {
410 let curve = ErrorMockCurve;
411 let result = curve.compute_curve_metrics();
412 assert!(result.is_err());
413 assert_eq!(
414 result.unwrap_err().to_string(),
415 "Basic Error: Basic metrics computation failed"
416 );
417 }
418 }
419
420 mod test_surface_metrics {
421 use super::*;
422
423 #[test]
424 fn test_compute_surface_metrics_success() {
425 let curve = MockCurve;
426 let result = curve.compute_surface_metrics();
427 assert!(result.is_ok());
428
429 let metrics = result.unwrap();
430 assert_eq!(metrics.basic.mean, dec!(10.5));
431 assert_eq!(metrics.basic.median, dec!(10.0));
432 assert_eq!(metrics.basic.std_dev, dec!(1.2));
433
434 assert_eq!(metrics.shape.skewness, dec!(0.5));
435 assert_eq!(metrics.shape.kurtosis, dec!(3.0));
436 assert!(!metrics.shape.peaks.is_empty());
437
438 assert_eq!(metrics.range.range, dec!(10.0));
439 assert_eq!(metrics.range.interquartile_range, dec!(6.0));
440
441 assert_eq!(metrics.trend.slope, dec!(1.5));
442 assert_eq!(metrics.trend.r_squared, dec!(0.95));
443
444 assert_eq!(metrics.risk.volatility, dec!(0.15));
445 assert_eq!(metrics.risk.sharpe_ratio, dec!(2.5));
446 }
447
448 #[test]
449 fn test_compute_surface_metrics_error() {
450 let curve = ErrorMockCurve;
451 let result = curve.compute_surface_metrics();
452
453 assert!(result.is_err());
454 assert_eq!(
455 result.unwrap_err().to_string(),
456 "Basic Error: Basic metrics computation failed"
457 );
458 }
459
460 #[test]
461 fn test_surface_metrics_matches_curve_metrics() {
462 let curve = MockCurve;
463 let surface_metrics = curve.compute_surface_metrics().unwrap();
464 let curve_metrics = curve.compute_curve_metrics().unwrap();
465
466 assert_eq!(surface_metrics.basic.mean, curve_metrics.basic.mean);
467 assert_eq!(surface_metrics.shape.skewness, curve_metrics.shape.skewness);
468 assert_eq!(surface_metrics.range.range, curve_metrics.range.range);
469 assert_eq!(surface_metrics.trend.slope, curve_metrics.trend.slope);
470 assert_eq!(
471 surface_metrics.risk.volatility,
472 curve_metrics.risk.volatility
473 );
474 }
475 }
476}