optionstratlib_math/geometrics/analysis/statistics.rs
1/******************************************************************************
2 Author: Joaquín Béjar García
3 Email: jb@taunais.com
4 Date: 26/8/24
5******************************************************************************/
6use crate::geometrics::{BasicMetrics, ShapeMetrics};
7use optionstratlib_core::{impl_json_debug_pretty, impl_json_display};
8use serde::Serialize;
9
10/// Contains comprehensive analysis results for a curve or dataset.
11///
12/// # Overview
13/// The `AnalysisResult` structure encapsulates both basic statistical metrics and
14/// shape-related characteristics of a dataset or curve. It serves as a complete analysis
15/// output that can be used to understand both the statistical properties and the
16/// geometrical behavior of the data.
17///
18/// This structure is particularly useful for comprehensive data analysis in financial modeling,
19/// statistical analysis, and geometric curve evaluation. It combines fundamental statistical
20/// insights with shape-based properties to provide a holistic view of the data.
21///
22/// # Fields
23///
24/// - **statistics**: [`BasicMetrics`]
25/// - Contains fundamental statistical metrics like mean, median, mode, and standard deviation.
26/// - Provides insights into the central tendency and dispersion characteristics of the dataset.
27/// - Used for understanding the overall statistical properties of the analyzed data.
28///
29/// - **shape_metrics**: [`ShapeMetrics`]
30/// - Contains shape-related properties such as skewness, kurtosis, and critical points
31/// (peaks, valleys, and inflection points).
32/// - Provides insights into the geometrical behavior and distribution characteristics of the curve.
33/// - Used for understanding the asymmetry, tailedness, and notable features in the curve's shape.
34///
35/// # Applications
36/// The `AnalysisResult` structure is utilized in various domains requiring both statistical
37/// and geometrical analysis:
38///
39/// - **Financial Analysis**:
40/// - Evaluate price distributions and identify critical price levels.
41/// - Detect patterns in market data through both statistical and shape-based observations.
42///
43/// - **Data Science**:
44/// - Provide comprehensive analysis of datasets, combining statistical insights with
45/// distributional shape characteristics.
46///
47/// - **Geometric Modeling**:
48/// - Analyze curve properties for more accurate modeling and representation.
49/// - Identify key points that define the geometric behavior of a curve.
50///
51#[derive(Clone, Serialize)]
52#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
53pub struct AnalysisResult {
54 /// The fundamental statistical metrics of the dataset, including measures of
55 /// central tendency (mean, median, mode) and dispersion (standard deviation).
56 pub statistics: BasicMetrics,
57
58 /// The shape-related metrics of the curve or dataset, including distribution
59 /// characteristics (skewness, kurtosis) and critical geometric points
60 /// (peaks, valleys, inflection points).
61 pub shape_metrics: ShapeMetrics,
62}
63
64impl_json_debug_pretty!(AnalysisResult);
65impl_json_display!(AnalysisResult);
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::curves::Point2D;
71 use crate::geometrics::analysis::metrics::{BasicMetrics, ShapeMetrics};
72 use rust_decimal_macros::dec;
73
74 /// Creates a sample BasicMetrics for testing
75 fn create_sample_basic_metrics() -> BasicMetrics {
76 BasicMetrics {
77 mean: dec!(10.5),
78 median: dec!(10.0),
79 mode: dec!(9.0),
80 std_dev: dec!(1.2),
81 }
82 }
83
84 /// Creates a sample ShapeMetrics for testing
85 fn create_sample_shape_metrics() -> ShapeMetrics {
86 ShapeMetrics {
87 skewness: dec!(0.25),
88 kurtosis: dec!(3.2),
89 peaks: vec![
90 Point2D::new(dec!(1.0), dec!(10.0)),
91 Point2D::new(dec!(5.0), dec!(15.0)),
92 ],
93 valleys: vec![Point2D::new(dec!(3.0), dec!(5.0))],
94 inflection_points: vec![
95 Point2D::new(dec!(2.0), dec!(7.5)),
96 Point2D::new(dec!(4.0), dec!(12.0)),
97 ],
98 }
99 }
100
101 #[test]
102 fn test_analysis_result_creation() {
103 // Create an instance of AnalysisResult
104 let result = AnalysisResult {
105 statistics: create_sample_basic_metrics(),
106 shape_metrics: create_sample_shape_metrics(),
107 };
108
109 // Verify that it was created successfully
110 assert!(result.statistics.mean == dec!(10.5));
111 assert!(result.statistics.median == dec!(10.0));
112 assert!(result.statistics.mode == dec!(9.0));
113 assert!(result.statistics.std_dev == dec!(1.2));
114
115 assert!(result.shape_metrics.skewness == dec!(0.25));
116 assert!(result.shape_metrics.kurtosis == dec!(3.2));
117 }
118
119 #[test]
120 fn test_analysis_result_field_access() {
121 let result = AnalysisResult {
122 statistics: create_sample_basic_metrics(),
123 shape_metrics: create_sample_shape_metrics(),
124 };
125
126 // Test accessing statistics fields
127 assert_eq!(result.statistics.mean, dec!(10.5));
128 assert_eq!(result.statistics.median, dec!(10.0));
129 assert_eq!(result.statistics.mode, dec!(9.0));
130 assert_eq!(result.statistics.std_dev, dec!(1.2));
131
132 // Test accessing shape_metrics fields
133 assert_eq!(result.shape_metrics.skewness, dec!(0.25));
134 assert_eq!(result.shape_metrics.kurtosis, dec!(3.2));
135 assert_eq!(result.shape_metrics.peaks.len(), 2);
136 assert_eq!(result.shape_metrics.valleys.len(), 1);
137 assert_eq!(result.shape_metrics.inflection_points.len(), 2);
138 }
139
140 #[test]
141 fn test_analysis_result_point_access() {
142 let result = AnalysisResult {
143 statistics: create_sample_basic_metrics(),
144 shape_metrics: create_sample_shape_metrics(),
145 };
146
147 // Test specific point values in shape_metrics
148 // Check first peak
149 assert_eq!(result.shape_metrics.peaks[0].x, dec!(1.0));
150 assert_eq!(result.shape_metrics.peaks[0].y, dec!(10.0));
151
152 // Check second peak
153 assert_eq!(result.shape_metrics.peaks[1].x, dec!(5.0));
154 assert_eq!(result.shape_metrics.peaks[1].y, dec!(15.0));
155
156 // Check valley
157 assert_eq!(result.shape_metrics.valleys[0].x, dec!(3.0));
158 assert_eq!(result.shape_metrics.valleys[0].y, dec!(5.0));
159
160 // Check inflection points
161 assert_eq!(result.shape_metrics.inflection_points[0].x, dec!(2.0));
162 assert_eq!(result.shape_metrics.inflection_points[0].y, dec!(7.5));
163 assert_eq!(result.shape_metrics.inflection_points[1].x, dec!(4.0));
164 assert_eq!(result.shape_metrics.inflection_points[1].y, dec!(12.0));
165 }
166
167 #[test]
168 fn test_analysis_result_with_empty_points() {
169 // Create shape metrics with empty vectors
170 let shape_metrics = ShapeMetrics {
171 skewness: dec!(0.1),
172 kurtosis: dec!(2.5),
173 peaks: vec![],
174 valleys: vec![],
175 inflection_points: vec![],
176 };
177
178 let result = AnalysisResult {
179 statistics: create_sample_basic_metrics(),
180 shape_metrics,
181 };
182
183 // Verify the empty vectors
184 assert_eq!(result.shape_metrics.peaks.len(), 0);
185 assert_eq!(result.shape_metrics.valleys.len(), 0);
186 assert_eq!(result.shape_metrics.inflection_points.len(), 0);
187
188 // Other fields should still be accessible
189 assert_eq!(result.shape_metrics.skewness, dec!(0.1));
190 assert_eq!(result.shape_metrics.kurtosis, dec!(2.5));
191 }
192
193 // If AnalysisResult implements Debug (which it likely should)
194 #[test]
195 fn test_analysis_result_debug() {
196 let result = AnalysisResult {
197 statistics: create_sample_basic_metrics(),
198 shape_metrics: create_sample_shape_metrics(),
199 };
200
201 // This just ensures that debug formatting doesn't panic
202 let debug_str = format!("{result:?}");
203 assert!(!debug_str.is_empty());
204 }
205}