SPB Git

spb/wp41_uqo

Public
3.4 KB · 78 lines python
1"""Non-neural probabilistic baselines on the same origins and information set as the neural models.2All produce a Student-t (or Gaussian) predictive distribution for the cumulative H-minute log return and a variance3forecast for the realised variance over the horizon. Volatility estimators use only completed bars at the origin."""4from __future__ import annotations56import numpy as np78from ..data.windows import SymbolGrid, N_DAY91011def _hist_returns(g: SymbolGrid, s: int, m: int, n_days: int) -> tuple[np.ndarray, np.ndarray]:12    """Per-minute returns (same-session, present pairs) for the last n_days sessions up to (s, m), with slot ids."""13    rs, slots = [], []14    for d in range(max(0, s - n_days + 1), s + 1):15        last = m if d == s else N_DAY - 116        lc = g.flat_lc[d * N_DAY: d * N_DAY + last + 1]17        ok = ~np.isnan(lc)18        idx = np.flatnonzero(ok)19        if len(idx) < 2:20            continue21        r = np.diff(lc[idx]); rs.append(r); slots.append(idx[1:])22    if not rs:23        return np.zeros(0), np.zeros(0, dtype=int)24    return np.concatenate(rs), np.concatenate(slots)252627class NaiveRandomWalk:28    """Zero mean; scale from the trailing RMS of 1-minute returns in the last `window` minutes; Gaussian."""29    name = "naive_rw"3031    def __init__(self, window: int = 390, nu: float | None = None):32        self.window, self.nu = window, nu3334    def predict(self, g: SymbolGrid, s: int, m: int, H: int, sigma: float) -> dict:35        var = sigma ** 2 * H36        return {"mu": 0.0, "scale": np.sqrt(var), "nu": self.nu or np.inf, "var": var}373839class SeasonalVol:40    """Zero mean; variance = sum over future slots of the past-only time-of-day mean squared return41    (last `n_days` sessions), scaled by today's level ratio (trailing 30-min RV vs its seasonal counterpart)."""42    name = "seasonal_vol"4344    def __init__(self, n_days: int = 20):45        self.n_days = n_days4647    def predict(self, g: SymbolGrid, s: int, m: int, H: int, sigma: float) -> dict:48        r, sl = _hist_returns(g, s - 1, N_DAY - 1, self.n_days)49        if len(r) < 100:50            return NaiveRandomWalk().predict(g, s, m, H, sigma)51        prof = np.bincount(sl, weights=r * r, minlength=N_DAY) / np.maximum(1, np.bincount(sl, minlength=N_DAY))52        prof = np.where(prof > 0, prof, np.mean(r * r))53        fut = prof[m + 1: m + 1 + H].sum()54        rt, st = _hist_returns(g, s, m, 1)55        recent = rt[-30:]56        level = (np.mean(recent ** 2) / max(prof[max(0, m - 29): m + 1].mean(), 1e-12)) if len(recent) >= 10 else 1.057        level = float(np.clip(level, 0.25, 4.0))58        var = fut * level59        return {"mu": 0.0, "scale": np.sqrt(var), "nu": np.inf, "var": var}606162class EWMAVol:63    """RiskMetrics-style EWMA of squared 1-minute returns (lambda on the minute scale), times H, Student-t(nu)."""64    name = "ewma"6566    def __init__(self, lam: float = 0.97, nu: float = 5.0, n_days: int = 3):67        self.lam, self.nu, self.n_days = lam, nu, n_days6869    def predict(self, g: SymbolGrid, s: int, m: int, H: int, sigma: float) -> dict:70        r, _ = _hist_returns(g, s, m, self.n_days)71        if len(r) < 30:72            return NaiveRandomWalk().predict(g, s, m, H, sigma)73        w = self.lam ** np.arange(len(r))[::-1]74        v1 = float((w * r * r).sum() / w.sum())75        var = v1 * H76        scale = np.sqrt(var * (self.nu - 2) / self.nu)77        return {"mu": 0.0, "scale": scale, "nu": self.nu, "var": var}78
Files · main
Menu

Theme