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 3749a36
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 4 deletions.
3 changes: 3 additions & 0 deletions benches/traquer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ fn criterion_benchmark(c: &mut Criterion) {
)
})
});
c.bench_function("sig-trend-hurst", |b| {
b.iter(|| black_box(trend::hurst(&stats.close, 100, None).collect::<Vec<_>>()))
});
c.bench_function("sig-volume-bw_mfi", |b| {
b.iter(|| {
black_box(volume::bw_mfi(&stats.high, &stats.low, &stats.volume).collect::<Vec<f64>>())
Expand Down
95 changes: 95 additions & 0 deletions src/trend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,3 +1002,98 @@ 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) / x.len() as f64;
let y_avg = y.iter().fold(0.0, |acc, &y| acc + y) / y.len() as f64;
let mut covar = 0.0;
let mut var = 0.0;
for (&xi, &yi) in x.iter().zip(y) {
covar += (xi - x_avg) * (yi - y_avg);
var += (xi - x_avg).powi(2);
}
covar / var
}

/// Hurst Exponent
///
/// Measures the long-term memory of time series, and the rate at which these decrease as the
/// lag between pairs of values increases.
///
/// Configured to accept delta / return values as input rather than price. Window size should be
/// significantly larger than min_win size to ensure enough datapoints are computed for estimation
/// or else values less than 0 or greater than 1 may result.
///
/// ## 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> + '_ {
// create sub window sizes
let mut win_sizes = Vec::new();
let mut win = (min_win.unwrap_or(4) 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;
}
win_sizes.push(window);
let win_size_log10 = win_sizes
.iter()
.map(|&x| (x as f64).log10())
.collect::<Vec<_>>();

iter::repeat(f64::NAN)
.take(window - 1)
.chain(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)
}))
}
8 changes: 4 additions & 4 deletions tests/correlation_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,15 @@ fn test_krcc() {
result[16 - 1..]
);
let result = correlation::krcc(
&vec![7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4],
&vec![2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0],
&[7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4],
&[2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0],
7,
)
.collect::<Vec<_>>();
assert_eq!(0.55, result[7 - 1]);
let result = correlation::krcc(
&vec![7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4],
&vec![2.8, 2.8, 2.8, 2.6, 3.5, 4.6, 5.0],
&[7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4],
&[2.8, 2.8, 2.8, 2.6, 3.5, 4.6, 5.0],
7,
)
.collect::<Vec<_>>();
Expand Down
35 changes: 35 additions & 0 deletions tests/trend_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -975,3 +975,38 @@ fn test_ichimoku() {
result.4[..result.0.len() - b_win - lag_win]
);
}

#[test]
fn test_hurst() {
let stats = common::test_data();
let rets = stats
.close
.windows(2)
.map(|w| w[1] / w[0] - 1.0)
.collect::<Vec<_>>();
let result = trend::hurst(&rets, 16, None).collect::<Vec<_>>();
assert_eq!(rets.len(), result.len());
assert_eq!(
vec![
0.717300003513944,
0.7040433063878732,
0.7870815790649716,
0.5852648888827561,
0.4372401978043943,
0.6284984091135501,
0.5407697086351437,
0.5950129675824667,
0.8070520438846511,
0.7513875353620354,
0.7370004550770194,
0.6775389374178914,
0.7450996515301547,
0.773309179173034,
0.5946959711202044,
0.23772640082555682,
0.20834665213422418,
0.10884312104017504,
],
result[16 - 1..]
);
}

0 comments on commit 3749a36

Please sign in to comment.