#!/usr/bin/env python3 """ Generate HMM regime chart as single-file HTML with pure Canvas rendering. No external dependencies — works everywhere. """ import json, sys, os, glob import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from hmmlearn.hmm import GaussianHMM import warnings warnings.filterwarnings("ignore") SEED = 42 N_REGIMES = 3 STEP = 5 def load_snapshots(filepath): snaps = [] with open(filepath) as f: for line in f: line = line.strip() if line: snaps.append(json.loads(line)) return snaps def extract_features(snapshots): records = [] for s in snapshots: ts = s["_collect_ts"] bids = sorted(s["bids"], key=lambda x: x[0], reverse=True) asks = sorted(s["asks"], key=lambda x: x[0]) bb = bids[0][0] if bids else 0 ba = asks[0][0] if asks else 0 mid = (bb + ba) / 2 if bb and ba else 0 spread = ba - bb if bb and ba else 0 bd = sum(b[1] for b in bids[:10]) ad = sum(a[1] for a in asks[:10]) depth = bd + ad bv = sum(b[0] * b[1] for b in bids[:10]) av = sum(a[0] * a[1] for a in asks[:10]) imbalance = (bv - av) / (bv + av + 1e-12) records.append({"ts": ts, "mid": mid, "spread": spread, "depth": depth, "imbalance": imbalance}) return pd.DataFrame(records) def engineer_features(df, win=20): s, d, imb = df["spread"].values, df["depth"].values, df["imbalance"].values rv = pd.Series(df["mid"]).pct_change().rolling(win, min_periods=1).std().fillna(0).values ofi = pd.Series(df["mid"]).diff().rolling(win, min_periods=1).mean().fillna(0).values return np.column_stack([s, d, imb, rv, ofi]) def fit_hmm(X): best_score, best_model = -np.inf, None for k in range(12): m = GaussianHMM(n_components=N_REGIMES, covariance_type="full", n_iter=400, tol=1e-7, random_state=SEED + k, init_params="stmc", params="stmc") try: m.fit(X) sc = m.score(X) if sc > best_score: best_score, best_model = sc, m except Exception: continue if best_model is None: raise RuntimeError("HMM fitting failed.") return best_model HTML = """