#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use crate::{dfa, analyze, StructuralLaw};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MarketRegime {
Trending,
RandomWalk,
MeanReverting,
}
impl fmt::Display for MarketRegime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MarketRegime::Trending => write!(f, "TRENDING"),
MarketRegime::RandomWalk => write!(f, "RANDOM WALK"),
MarketRegime::MeanReverting => write!(f, "MEAN-REVERTING"),
}
}
}
#[derive(Debug, Clone)]
pub struct RegimeResult {
pub alpha: f64,
pub r_squared: f64,
pub regime: MarketRegime,
pub n_returns: usize,
pub law: StructuralLaw,
}
impl fmt::Display for RegimeResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "α={:.3} R²={:.4} regime={} (n={})",
self.alpha, self.r_squared, self.regime, self.n_returns)
}
}
pub fn returns_from_prices(prices: &[f64]) -> Vec<f64> {
if prices.len() < 2 { return Vec::new(); }
prices.windows(2)
.map(|w| if w[0] > 0.0 { crate::ln(w[1] / w[0]) } else { 0.0 })
.collect()
}
pub fn regime_detect(returns: &[f64]) -> RegimeResult {
let law = analyze(returns);
let dfa_result = dfa(returns);
let regime = if dfa_result.alpha > 0.6 {
MarketRegime::Trending
} else if dfa_result.alpha < 0.45 {
MarketRegime::MeanReverting
} else {
MarketRegime::RandomWalk
};
RegimeResult {
alpha: dfa_result.alpha,
r_squared: dfa_result.r_squared,
regime,
n_returns: returns.len(),
law,
}
}
pub struct RegimeMonitor {
window: Vec<f64>,
capacity: usize,
pos: usize,
filled: bool,
}
impl RegimeMonitor {
pub fn new(window_size: usize) -> Self {
RegimeMonitor {
window: vec![0.0; window_size],
capacity: window_size,
pos: 0,
filled: false,
}
}
pub fn push(&mut self, ret: f64) -> Option<RegimeResult> {
self.window[self.pos] = ret;
self.pos += 1;
if self.pos >= self.capacity {
self.pos = 0;
self.filled = true;
}
if !self.filled { return None; }
let mut ordered = Vec::with_capacity(self.capacity);
ordered.extend_from_slice(&self.window[self.pos..]);
ordered.extend_from_slice(&self.window[..self.pos]);
Some(regime_detect(&ordered))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_from_prices_basic() {
let prices = vec![100.0, 110.0, 121.0];
let rets = returns_from_prices(&prices);
assert_eq!(rets.len(), 2);
assert!((rets[0] - 0.0953).abs() < 0.001); }
#[test]
fn regime_monitor_learns() {
let mut mon = RegimeMonitor::new(256);
let mut state = 42u64;
for _ in 0..256 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let r = (state >> 33) as f64 / (1u64 << 31) as f64 - 0.5;
let result = mon.push(r * 0.02);
if let Some(r) = result {
assert!(r.alpha > 0.0);
return;
}
}
panic!("monitor should have produced a result after 256 samples");
}
}