1use linreg::linear_regression;
22
23pub(crate) mod utils;
24
25use utils::*;
26
27pub fn rssimple(x: &[f64]) -> f64 {
40 let n = x.len();
41 if n == 0 {
42 return 0.0;
43 }
44 let n_f64 = n as f64;
45 let inv_n = 1.0 / n_f64;
46
47 let x_mean: f64 = x.iter().sum::<f64>() * inv_n;
49
50 let mut cumsum = 0.0_f64;
56 let mut cum_min = 0.0_f64;
57 let mut cum_max = 0.0_f64;
58 let mut sum_sq = 0.0_f64;
59
60 for &val in x {
61 let d = val - x_mean;
62 cumsum += d;
63 cum_min = cum_min.min(cumsum);
64 cum_max = cum_max.max(cumsum);
65 sum_sq += d * d;
66 }
67
68 let std_dev = (sum_sq * inv_n).sqrt();
69 if std_dev < f64::EPSILON {
70 return 0.5; }
72
73 let rs = (cum_max - cum_min) / std_dev;
74 if rs < f64::EPSILON {
75 return 0.5;
76 }
77
78 rs.log2() / n_f64.log2()
79}
80
81pub fn rs_corrected(x: Vec<f64>) -> f64 {
93 let mut cap_x: Vec<f64> = vec![x.len() as f64];
94 let mut cap_y: Vec<f64> = vec![rscalc(&x)];
95 let mut n: Vec<u64> = vec![0, x.len() as u64 / 2, x.len() as u64];
96
97 while n[1] >= 8 {
99 let mut xl: Vec<f64> = vec![];
100 let mut yl: Vec<f64> = vec![];
101 for i in 1..n.len() {
102 let rs: f64 = rscalc(&x[((n[i - 1] + 1) as usize)..(n[i] as usize)]);
103 xl.push((n[i] - n[i - 1]) as f64);
104 yl.push(rs);
105 }
106 cap_x.push(mean(&xl));
107 cap_y.push(mean(&yl));
108 n = half(&n, x.len() as u64);
110 }
111
112 let cap_x_log: Vec<f64> = cap_x.iter().map(|a| a.ln()).collect();
114 let cap_y_log: Vec<f64> = cap_y.iter().map(|a| a.ln()).collect();
115 let (slope, _): (f64, f64) = linear_regression(&cap_x_log, &cap_y_log).unwrap();
116 slope
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_rssimple_trending_series() {
125 let prices: Vec<f64> = (0..100).map(|i| 100.0 + i as f64 * 0.5).collect();
127 let h = rssimple(&prices);
128 assert!(h.is_finite(), "Hurst must be finite: {}", h);
129 assert!(h > 0.4, "Trending series should have H > 0.4: {}", h);
130 }
131
132 #[test]
133 fn test_rssimple_constant_series() {
134 let prices = vec![100.0; 50];
135 let h = rssimple(&prices);
136 assert_eq!(h, 0.5, "Constant series → H = 0.5");
137 }
138
139 #[test]
140 fn test_rssimple_empty() {
141 let h = rssimple(&[]);
142 assert_eq!(h, 0.0, "Empty series → 0.0");
143 }
144
145 #[test]
146 fn test_rssimple_single_element() {
147 let h = rssimple(&[100.0]);
148 assert_eq!(h, 0.5, "Single element → H = 0.5");
150 }
151
152 #[test]
153 fn test_rssimple_alternating_series() {
154 let prices: Vec<f64> = (0..200).map(|i| if i % 2 == 0 { 100.0 } else { 101.0 }).collect();
156 let h = rssimple(&prices);
157 assert!(h.is_finite(), "Hurst must be finite: {}", h);
158 assert!(h < 0.6, "Alternating series should have low H: {}", h);
159 }
160
161 #[test]
162 fn test_rssimple_five_elements_doc_example() {
163 let prices = vec![100.0, 101.0, 99.5, 102.0, 101.5];
164 let h = rssimple(&prices);
165 assert!(h >= 0.0 && h <= 1.5, "Hurst should be in reasonable range: {}", h);
166 }
167
168 #[test]
169 fn test_rssimple_two_elements() {
170 let h = rssimple(&[100.0, 200.0]);
171 assert!(h.is_finite(), "Two-element series must be finite: {}", h);
172 }
173
174 #[test]
175 fn test_rssimple_nan_in_series() {
176 let prices = vec![100.0, f64::NAN, 102.0, 101.0, 99.0];
178 let h = rssimple(&prices);
179 let _ = h;
181 }
182
183 #[test]
184 fn test_rssimple_negative_prices() {
185 let prices: Vec<f64> = (0..50).map(|i| -10.0 + i as f64 * 0.1).collect();
187 let h = rssimple(&prices);
188 assert!(h.is_finite(), "Negative prices must produce finite H: {}", h);
189 }
190}