Files
resonance-engine/regime_analyzer.py
T
2026-06-08 13:34:30 +07:00

153 lines
6.6 KiB
Python

"""regime_analyzer.py — Claude Desktop Option B test.
Hypothesis (Claude Desktop, 2026-06-08):
HIGH TENSION = asymmetry > arm A q90 OR coherence < arm A q10
LOW TENSION = asymmetry < arm A q10 AND coherence > arm A q90
NEUTRAL = otherwise
Primary test:
vol_ratio for (HIGH ∩ top-decile trade_count)
must exceed vol_ratio for top-decile trade_count alone (best 2.42x).
Secondary test:
For HIGH TENSION minutes, sign of (taker_buy_z - taker_sell_z) → sign(fwd_60).
Also try (stress_xx - stress_yy) as the lattice-derived direction signal.
Usage: edit RUN below, then `wsl python3 /mnt/d/Resonance_Engine/regime_analyzer.py`
"""
import pandas as pd, numpy as np, glob, sys
RUN = sys.argv[1] if len(sys.argv) > 1 else "/mnt/d/Resonance_Engine/traj/regime_20260608T131732"
print(f"=== regime_analyzer on {RUN} ===")
A = pd.read_parquet(f"{RUN}/arm_A_no_inject.parquet")
T = pd.read_parquet(f"{RUN}/arm_T_3ch.parquet")
print(f"arm A: {len(A):,} arm T: {len(T):,}")
# Load BTC prices (joined to compute fwd_60 returns)
files = sorted(glob.glob("/mnt/d/PaperTrader/research/hl_data/minutes/202604*/*.parquet"))
px_parts = []
for f in files:
d = pd.read_parquet(f, columns=["minute", "coin", "mid_price"])
px_parts.append(d[d.coin == "BTC"][["minute", "mid_price"]])
px = pd.concat(px_parts, ignore_index=True).drop_duplicates("minute").sort_values("minute").reset_index(drop=True)
px["fwd_60_bps"] = (np.log(px["mid_price"].shift(-60)) - np.log(px["mid_price"])) * 10000.0
print(f"prices: {len(px):,} minutes")
# Join price returns into arm T (we measure on T's telemetry + raw fields)
df = T.merge(px[["minute", "fwd_60_bps"]], on="minute", how="inner").sort_values("minute").reset_index(drop=True)
df = df.dropna(subset=["fwd_60_bps", "asymmetry", "coherence", "trade_count"]).reset_index(drop=True)
print(f"joined arm T rows with fwd_60 + telemetry: {len(df):,}")
# ─── thresholds from arm A free-running baseline ───
asym_p10 = A["asymmetry"].quantile(0.10)
asym_p90 = A["asymmetry"].quantile(0.90)
coh_p10 = A["coherence"].quantile(0.10)
coh_p90 = A["coherence"].quantile(0.90)
print()
print("=== arm A baseline thresholds ===")
print(f" asymmetry p10={asym_p10:.4f} p90={asym_p90:.4f} (mean={A.asymmetry.mean():.4f})")
print(f" coherence p10={coh_p10:.4f} p90={coh_p90:.4f} (mean={A.coherence.mean():.4f})")
# Label regimes on arm T using arm A thresholds
high = (df["asymmetry"] > asym_p90) | (df["coherence"] < coh_p10)
low = (df["asymmetry"] < asym_p10) & (df["coherence"] > coh_p90)
neutral = ~(high | low)
df["regime"] = "NEUTRAL"
df.loc[high, "regime"] = "HIGH"
df.loc[low, "regime"] = "LOW"
print()
print("=== regime label distribution (arm T) ===")
print(df["regime"].value_counts())
print(f" HIGH frac: {high.mean()*100:.2f}%")
print(f" LOW frac: {low.mean()*100:.2f}%")
print(f" NEUTRAL frac: {neutral.mean()*100:.2f}%")
# ─── trade_count top decile (the validated raw signal) ───
tc_p90 = df["trade_count"].quantile(0.90)
top_tc = df["trade_count"] >= tc_p90
print()
print(f"=== trade_count top decile (>= {tc_p90:.0f}) ===")
print(f" fraction: {top_tc.mean()*100:.2f}% ({top_tc.sum():,} rows)")
# ─── PRIMARY TEST: vol ratios ───
base_abs = df["fwd_60_bps"].abs().mean()
print()
print("=" * 90)
print("PRIMARY TEST — |fwd_60_bps| vol ratios vs full sample")
print("=" * 90)
print(f" baseline |fwd_60| mean (all minutes): {base_abs:.3f} bps")
print()
def vol_report(mask, name):
n = int(mask.sum())
if n < 50:
print(f" {name:<60} n={n:>6,} too small")
return
sub = df.loc[mask, "fwd_60_bps"].abs()
ratio = sub.mean() / base_abs
print(f" {name:<60} n={n:>6,} mean|y|={sub.mean():>6.2f}bps ratio={ratio:.3f}x")
vol_report(top_tc, "top decile trade_count (validated baseline)")
vol_report(high, "lattice HIGH TENSION (all)")
vol_report(low, "lattice LOW TENSION (all)")
vol_report(high & top_tc, "HIGH ∩ top-decile trade_count <-- THE TEST")
vol_report(low & top_tc, "LOW ∩ top-decile trade_count")
vol_report(neutral & top_tc, "NEUTRAL ∩ top-decile trade_count")
print()
top_ratio = (df.loc[top_tc, "fwd_60_bps"].abs().mean() / base_abs)
hi_top_ratio = (df.loc[high & top_tc, "fwd_60_bps"].abs().mean() / base_abs)
print(f" top-decile trade_count ratio: {top_ratio:.3f}x")
print(f" HIGH ∩ top-decile trade_count: {hi_top_ratio:.3f}x")
diff = hi_top_ratio - top_ratio
print(f" lattice lift over raw top-decile: {diff:+.3f}x ({diff/top_ratio*100:+.1f}%)")
print(f" success criterion (Claude): > 2.42x -> {'PASS' if hi_top_ratio > 2.42 else 'FAIL'}")
# ─── SECONDARY TEST: direction for HIGH TENSION minutes ───
print()
print("=" * 90)
print("SECONDARY TEST — direction for HIGH TENSION minutes")
print("=" * 90)
sub = df.loc[high].copy()
sub = sub.dropna(subset=["taker_buy_usd_z", "taker_sell_usd_z", "stress_xx", "stress_yy"])
print(f" n HIGH minutes (after dropna z+stress): {len(sub):,}")
if len(sub) > 100:
# direct injected-signal direction
buy_minus_sell = sub["taker_buy_usd_z"] - sub["taker_sell_usd_z"]
stress_xx_minus_yy = sub["stress_xx"] - sub["stress_yy"]
print()
print(f" Spearman (buy_z - sell_z, fwd_60): "
f"{buy_minus_sell.corr(sub.fwd_60_bps, method='spearman'):+.4f}")
print(f" Spearman (stress_xx - stress_yy, fwd_60): "
f"{stress_xx_minus_yy.corr(sub.fwd_60_bps, method='spearman'):+.4f}")
# directional accuracy of each sign-based predictor
def dacc(predictor, y):
valid = predictor.notna() & y.notna() & (predictor != 0) & (y != 0)
if valid.sum() == 0:
return None, 0
return (np.sign(predictor[valid]) == np.sign(y[valid])).mean() * 100, int(valid.sum())
d, n = dacc(buy_minus_sell, sub.fwd_60_bps)
print(f" dacc sign(buy_z - sell_z) vs sign(fwd_60): {d:.2f}% (n={n:,})")
d, n = dacc(stress_xx_minus_yy, sub.fwd_60_bps)
print(f" dacc sign(stress_xx - stress_yy) vs sign(fwd_60): {d:.2f}% (n={n:,})")
# mean signed return when each predictor is positive vs negative
pos = buy_minus_sell > 0
neg = buy_minus_sell < 0
print()
print(f" mean fwd_60 when buy_z > sell_z: {sub.loc[pos, 'fwd_60_bps'].mean():+.3f} bps (n={pos.sum():,})")
print(f" mean fwd_60 when buy_z < sell_z: {sub.loc[neg, 'fwd_60_bps'].mean():+.3f} bps (n={neg.sum():,})")
pos_s = stress_xx_minus_yy > 0
neg_s = stress_xx_minus_yy < 0
print(f" mean fwd_60 when xx > yy: {sub.loc[pos_s, 'fwd_60_bps'].mean():+.3f} bps (n={pos_s.sum():,})")
print(f" mean fwd_60 when xx < yy: {sub.loc[neg_s, 'fwd_60_bps'].mean():+.3f} bps (n={neg_s.sum():,})")