use crate::traits::Next;
use crate::regimes::MarketRegime;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HMM {
n_states: usize,
a: Vec<Vec<f64>>,
means: Vec<f64>,
stds: Vec<f64>,
pi: Vec<f64>,
last_delta: Vec<f64>,
initialized: bool,
}
impl HMM {
pub fn new(
a: Vec<Vec<f64>>,
means: Vec<f64>,
stds: Vec<f64>,
pi: Vec<f64>,
) -> Self {
let n_states = a.len();
Self {
n_states,
a,
means,
stds,
pi,
last_delta: vec![0.0; n_states],
initialized: false,
}
}
pub fn bull_bear() -> Self {
Self::new(
vec![
vec![0.95, 0.05], vec![0.10, 0.90], ],
vec![0.001, -0.002], vec![0.01, 0.02], vec![0.5, 0.5],
)
}
fn gaussian_pdf(x: f64, mu: f64, sigma: f64) -> f64 {
let variance = sigma * sigma;
let denom = (2.0 * std::f64::consts::PI * variance).sqrt();
let exponent = -((x - mu).powi(2)) / (2.0 * variance);
exponent.exp() / denom
}
}
impl Next<f64> for HMM {
type Output = MarketRegime;
fn next(&mut self, x: f64) -> Self::Output {
let mut next_delta = vec![0.0; self.n_states];
let mut best_state = 0;
let mut max_prob = -f64::INFINITY;
if !self.initialized {
for i in 0..self.n_states {
let emission = Self::gaussian_pdf(x, self.means[i], self.stds[i]);
next_delta[i] = (self.pi[i] * emission).ln();
if next_delta[i] > max_prob {
max_prob = next_delta[i];
best_state = i;
}
}
self.initialized = true;
} else {
for j in 0..self.n_states {
let mut max_prev = -f64::INFINITY;
for i in 0..self.n_states {
let prob = self.last_delta[i] + self.a[i][j].ln();
if prob > max_prev {
max_prev = prob;
}
}
let emission = Self::gaussian_pdf(x, self.means[j], self.stds[j]);
next_delta[j] = max_prev + emission.ln();
if next_delta[j] > max_prob {
max_prob = next_delta[j];
best_state = j;
}
}
}
self.last_delta = next_delta;
match best_state {
0 => MarketRegime::Bull,
1 => MarketRegime::Bear,
_ => MarketRegime::Cluster(best_state as u8),
}
}
}