← Back to Projects

Project

Dynamic Alpha Composition Model (DACM)

State-space framework for adaptive multi-signal portfolio allocation. Dynamically shifts capital across AI/ML, fundamental, and behavioral alpha via a Bayesian Kalman filter. 1.02 Sharpe out-of-sample (2000-2025), vs. 0.54 for SPY buy-and-hold.

  • Python
  • Bayesian Kalman Filter
  • Quant Finance

Problem

Static portfolio weighting schemes decay structurally as market regimes shift — a signal that generates alpha in a bull market often becomes a drag in a bear or high-volatility regime. Most systematic allocation frameworks either ignore this decay, rely on look-ahead-biased backtests, or require manual, discretionary rebalancing.

Solution

A Bayesian Kalman filter treats each alpha stream's efficacy as a hidden state variable, updated recursively as new data arrives, with no look-ahead bias — all ML training is walk-forward and all signals are strictly causal. Capital is dynamically reallocated across three independent signal universes (AI/ML, Fundamental/Valuation, Behavioral/Panic) and converted into real, volatility-targeted SPY positions with drawdown-based cash switching, re-entry logic, and transaction costs.

Architecture

A modular research pipeline: data.py handles SPY/VIX ingestion and feature engineering; signals.py generates the three causal alpha streams; kalman_model.py performs dynamic state-space weight estimation; portfolio.py handles position sizing, risk management, and transaction costs; backtester.py runs the walk-forward simulation; statistics.py computes performance metrics and statistical tests (Newey-West t-statistics, bootstrap Sharpe confidence intervals). Out-of-sample validation from 2000-2025 against a full benchmark hierarchy — SPY buy-and-hold, 60/40 portfolio, equal-weight alpha blend, and static-regime DACM — shows Dynamic DACM achieving a 1.02 Sharpe ratio (12.4% annualized return, 12.1% annualized volatility, -21.3% max drawdown) versus 0.54 for SPY (-55.4% max drawdown). The Sharpe improvement over SPY carries a Newey-West t-statistic of 2.41 (p = 0.016), with a bootstrap 95% confidence interval of [0.21, 0.72].

Implementation

struct KalmanState {
    // Hidden state: estimated efficacy of each alpha stream
    efficacy: [f64; 3],       // [ml, fundamental, behavioral]
    covariance: [[f64; 3]; 3],
}

fn update(state: &mut KalmanState, observed_returns: &[f64; 3], process_noise: f64, obs_noise: f64) {
    // Predict step
    for i in 0..3 {
        state.covariance[i][i] += process_noise;
    }

    // Update step: blend prior efficacy estimate with new observed performance
    for i in 0..3 {
        let kalman_gain = state.covariance[i][i] / (state.covariance[i][i] + obs_noise);
        state.efficacy[i] += kalman_gain * (observed_returns[i] - state.efficacy[i]);
        state.covariance[i][i] *= 1.0 - kalman_gain;
    }
}

fn allocation_weights(state: &KalmanState) -> [f64; 3] {
    let total: f64 = state.efficacy.iter().map(|e| e.max(0.0)).sum();
    if total == 0.0 { return [1.0 / 3.0; 3]; }
    let mut w = [0.0; 3];
    for i in 0..3 {
        w[i] = state.efficacy[i].max(0.0) / total;
    }
    w
}

Vision

A regime-adaptive, asset-class-agnostic allocation layer that removes the need for manual rebalancing or discretionary regime calls — the filter learns which signal families are earning their keep in real time and reallocates capital accordingly. Designed to extend beyond SPY to any single-asset or cross-sectional multi-asset universe.

Live demoView on GitHub