Skip to content

Commit

Permalink
hurst exponent
Browse files Browse the repository at this point in the history
  • Loading branch information
chungg committed Jul 29, 2024
1 parent 7ea0cc1 commit ed277df
Showing 1 changed file with 88 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/trend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,3 +1002,91 @@ pub fn ichimoku<'a>(
lag.chain(iter::repeat(f64::NAN).take(base_win)),
)
}

fn hurst_rs(data: &[f64]) -> f64 {
let avg = data.iter().sum::<f64>() / data.len() as f64;
let mut max_z = f64::MIN;
let mut min_z = f64::MAX;
let mut dev_cumsum = 0.0;
let mut var = 0.0;
for x in data {
let dev = x - avg;
var += dev.powi(2);
dev_cumsum += dev;
max_z = max_z.max(dev_cumsum);
min_z = min_z.min(dev_cumsum);
}

let r = max_z - min_z;
let s = (var / (data.len() - 1) as f64).sqrt();
r / s
}

fn linreg(x: &[f64], y: &[f64]) -> f64 {
let x_avg = x.iter().fold(0.0, |acc, &x| acc + x as f64) / x.len() as f64;

Check failure on line 1026 in src/trend.rs

View workflow job for this annotation

GitHub Actions / analyze

casting to the same type is unnecessary (`f64` -> `f64`)
let y_avg = y.iter().fold(0.0, |acc, &y| acc + y as f64) / y.len() as f64;

Check failure on line 1027 in src/trend.rs

View workflow job for this annotation

GitHub Actions / analyze

casting to the same type is unnecessary (`f64` -> `f64`)
let mut covar = 0.0;
let mut var = 0.0;
for (&xi, &yi) in x.iter().zip(y) {
covar += (xi as f64 - x_avg) * (yi as f64 - y_avg);

Check failure on line 1031 in src/trend.rs

View workflow job for this annotation

GitHub Actions / analyze

casting to the same type is unnecessary (`f64` -> `f64`)

Check failure on line 1031 in src/trend.rs

View workflow job for this annotation

GitHub Actions / analyze

casting to the same type is unnecessary (`f64` -> `f64`)
var += (xi as f64 - x_avg).powi(2);

Check failure on line 1032 in src/trend.rs

View workflow job for this annotation

GitHub Actions / analyze

casting to the same type is unnecessary (`f64` -> `f64`)
}
covar / var
}

/// Hurst Exponent
///
/// Measures the autocorrelations of a series, and the rate at which these decrease as the
/// lag between pairs of values increases.
///
/// ## Usage
///
/// A value above 0.5 suggests a positive autocorrelation or that there is a trend and
/// it will continue (either up or down). A value below 0.5 suggests it will mean-revert
/// or that it will break trend.
///
/// ## Sources
///
/// [[1]](https://en.wikipedia.org/wiki/Hurst_exponent)
/// [[2]](https://github.com/Mottl/hurst/)
/// [[3]](https://mahowald.github.io/hurst/)
///
/// # Examples
///
/// ```
/// use traquer::trend;
///
/// trend::hurst(&vec![1.0,2.0,3.0,4.0,5.0,6.0,4.0,5.0], 5, None).collect::<Vec<f64>>();
///
/// ```
pub fn hurst(
data: &[f64],
window: usize,
min_win: Option<usize>,
) -> impl Iterator<Item = f64> + '_ {
// map(lambda x: int(10**x), np.arange(math.log10(8), math.log10(window), 0.25))))
let mut win_sizes = Vec::new();
let mut win = (min_win.unwrap_or(10) as f64).log10();
loop {
let win_size = 10.0_f64.powf(win) as usize;
if win_size > window {
break;
}
win_sizes.push(win_size);
win += 0.25;
}
let win_size_log10 = win_sizes
.iter()
.map(|&x| (x as f64).log10())
.collect::<Vec<_>>();

data.windows(window).map(move |w| {
let mut rs_vals = Vec::new();
for &x in win_sizes.iter() {
let chunks = w.chunks_exact(x);
let cnt = chunks.len();
rs_vals.push((chunks.fold(0.0, |acc, x| acc + hurst_rs(x)) / cnt as f64).log10());
}
linreg(&win_size_log10, &rs_vals)
})
}

0 comments on commit ed277df

Please sign in to comment.