malkhut(T2): Code World Model — deterministic exchange simulator
CWM core (core.py): price-time priority, sequential level consumption, partial fills, queue position, latency injection, maker/taker fees. Numba acceleration (numba_core.py): JIT hot loops, 1.8x fill speedup. Replay verification (replay_verify.py): binary search, trajectory recording. Supporting: adverse_selection, correlation, latency_model, multi_level, queue_model, spread_dynamics, volatility, hftbacktest_validator.
This commit is contained in:
118
MALKHUT/malkhut/cwm/volatility.py
Normal file
118
MALKHUT/malkhut/cwm/volatility.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
Volatility Clustering Model — model how volatility clusters over time.
|
||||
|
||||
Improves risk management by predicting volatility regime changes.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
from numba import njit
|
||||
|
||||
|
||||
@njit(cache=True)
|
||||
def compute_volatility_regime(
|
||||
current_vol: float,
|
||||
long_term_vol: float,
|
||||
vol_of_vol: float,
|
||||
recent_returns: np.ndarray,
|
||||
) -> float:
|
||||
"""
|
||||
Compute volatility regime score (0-1).
|
||||
|
||||
Model:
|
||||
- High current vol relative to long-term → regime = 1
|
||||
- Low current vol relative to long-term → regime = 0
|
||||
- vol_of_vol adjusts sensitivity
|
||||
|
||||
Returns regime score (0=low vol, 1=high vol).
|
||||
"""
|
||||
if long_term_vol <= 0:
|
||||
return 0.5
|
||||
|
||||
vol_ratio = current_vol / long_term_vol
|
||||
# Sigmoid mapping: vol_ratio=1 → 0.5, vol_ratio>1 → >0.5, vol_ratio<1 → <0.5
|
||||
regime = 1.0 / (1.0 + math.exp(-2.0 * (vol_ratio - 1.0)))
|
||||
return regime
|
||||
|
||||
|
||||
@njit(cache=True)
|
||||
def predict_volatility(
|
||||
current_vol: float,
|
||||
long_term_vol: float,
|
||||
vol_of_vol: float,
|
||||
time_horizon_s: float,
|
||||
mean_reversion_rate: float = 0.05,
|
||||
) -> float:
|
||||
"""
|
||||
Predict volatility after time_horizon_s.
|
||||
|
||||
Model: GARCH-like mean reversion toward long-term volatility.
|
||||
"""
|
||||
if long_term_vol <= 0:
|
||||
return current_vol
|
||||
|
||||
# Mean reversion toward long-term
|
||||
predicted = current_vol + (long_term_vol - current_vol) * (1 - math.exp(-mean_reversion_rate * time_horizon_s))
|
||||
|
||||
# Add vol-of-vol noise
|
||||
noise = vol_of_vol * math.sqrt(time_horizon_s / 86400.0) # annualized
|
||||
predicted += noise * (2.0 * ((hash(str(current_vol)) % 1000) / 1000.0) - 1.0)
|
||||
|
||||
return max(0.001, predicted)
|
||||
|
||||
|
||||
class VolatilityClusteringModel:
|
||||
"""
|
||||
Volatility clustering model for the CWM.
|
||||
|
||||
Tracks volatility regime and predicts future volatility.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._vol_history: list[float] = []
|
||||
self._long_term_vol: float = 15.0 # default
|
||||
self._vol_of_vol: float = 5.0 # default
|
||||
|
||||
def update(self, volatility: float) -> None:
|
||||
"""Update with current volatility."""
|
||||
self._vol_history.append(volatility)
|
||||
if len(self._vol_history) > 1000:
|
||||
self._vol_history = self._vol_history[-500:]
|
||||
# Update long-term estimate
|
||||
if len(self._vol_history) > 50:
|
||||
self._long_term_vol = sum(self._vol_history[-200:]) / len(self._vol_history[-200:])
|
||||
|
||||
def regime(self) -> float:
|
||||
"""Get current volatility regime (0=low, 1=high)."""
|
||||
if not self._vol_history:
|
||||
return 0.5
|
||||
current = self._vol_history[-1]
|
||||
return compute_volatility_regime(current, self._long_term_vol, self._vol_of_vol, np.array([]))
|
||||
|
||||
def predict(self, time_horizon_s: float = 60.0) -> float:
|
||||
"""Predict volatility after time_horizon_s."""
|
||||
if not self._vol_history:
|
||||
return self._long_term_vol
|
||||
current = self._vol_history[-1]
|
||||
return predict_volatility(current, self._long_term_vol, self._vol_of_vol, time_horizon_s)
|
||||
|
||||
@property
|
||||
def current_volatility(self) -> float:
|
||||
return self._vol_history[-1] if self._vol_history else 0.0
|
||||
|
||||
@property
|
||||
def long_term_volatility(self) -> float:
|
||||
return self._long_term_vol
|
||||
|
||||
@property
|
||||
def vol_of_vol(self) -> float:
|
||||
if len(self._vol_history) < 20:
|
||||
return 0.0
|
||||
recent = self._vol_history[-50:]
|
||||
mean = sum(recent) / len(recent)
|
||||
variance = sum((x - mean) ** 2 for x in recent) / len(recent)
|
||||
return math.sqrt(variance)
|
||||
Reference in New Issue
Block a user