Files
resonance-engine/raw_tension_baseline.py
T
2026-06-07 19:34:30 +07:00

82 lines
3.4 KiB
Python

"""Raw tension regression baseline. NO lattice. Three feature regression on fwd_60 return."""
import pandas as pd, numpy as np, glob
from pathlib import Path
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from scipy.stats import spearmanr, pearsonr
# load tensions (deterministic per minute — arm_T and arm_A have identical tension columns)
T = pd.read_parquet("/mnt/d/Resonance_Engine/traj/tension_20260607T130445/arm_T_tension.parquet")
T = T[["minute","funding_bps","bs_ratio_signed","activity_excess","cvd_divergence"]].copy()
print(f"tensions: {len(T):,} minutes")
# load BTC mid_price from raw HL data
files = sorted(glob.glob("/mnt/d/PaperTrader/research/hl_data/minutes/202604*/*.parquet"))
print(f"loading {len(files)} hl files...")
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)
print(f"price rows: {len(px):,} span: minute {px.minute.min()} -> {px.minute.max()}")
# fwd_60 in bps
px["mid_60"] = px["mid_price"].shift(-60)
px["fwd_60"] = (np.log(px.mid_60) - np.log(px.mid_price)) * 10000.0
# join
df = T.merge(px[["minute","fwd_60"]], on="minute", how="inner")
df = df.dropna(subset=["funding_bps","bs_ratio_signed","activity_excess","cvd_divergence","fwd_60"])
print(f"after dropna + fwd_60: {len(df):,} rows")
X_cols = ["funding_bps","bs_ratio_signed","activity_excess","cvd_divergence"]
X = df[X_cols].values
y = df["fwd_60"].values
# scale features to unit std so coef magnitudes are comparable
Xz = (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-12)
print()
print("="*88)
print("RAW TENSION REGRESSION — fwd_60 (bps) no lattice")
print("="*88)
print(f" n={len(df):,} y mean={y.mean():+.2f}bps y std={y.std():.2f}bps")
# 1) univariate Spearman + Pearson per tension
print()
print("=== univariate ===")
print(f"{'feature':<22} {'spearman':>10} {'pearson':>10} p (spearman)")
for c,name in zip(X.T, X_cols):
rho_s,p_s = spearmanr(c, y)
rho_p,p_p = pearsonr(c, y)
print(f" {name:<20} {rho_s:>+10.4f} {rho_p:>+10.4f} {p_s:.2g}")
# 2) full OLS on standardized features
print()
print("=== OLS (standardized features) ===")
reg = LinearRegression().fit(Xz, y)
pred = reg.predict(Xz)
print(f" R^2 (in-sample, all April) = {r2_score(y, pred):.6f}")
print(f" intercept = {reg.intercept_:+.4f}")
for name, coef in zip(X_cols, reg.coef_):
print(f" beta[{name:<22}] = {coef:+.4f} bps per 1-sigma")
# 3) chronological 70/30 split (out-of-sample)
n=len(df); split=int(n*0.7)
reg_oos = LinearRegression().fit(Xz[:split], y[:split])
pred_oos = reg_oos.predict(Xz[split:])
r2_oos = r2_score(y[split:], pred_oos)
print()
print("=== OLS chronological 70/30 ===")
print(f" train={split:,} test={n-split:,}")
print(f" R^2 train = {r2_score(y[:split], reg_oos.predict(Xz[:split])):.6f}")
print(f" R^2 test = {r2_oos:.6f}")
print(f" directional accuracy test = {(np.sign(pred_oos)==np.sign(y[split:])).mean()*100:.2f}%")
# 4) for context — autocorrelation of fwd_60 (the trivial AR(1) baseline to beat)
print()
print("=== triviality check ===")
print(f" y autocorr lag1 = {pd.Series(y).autocorr(1):+.4f}")
print(f" y autocorr lag60 = {pd.Series(y).autocorr(60):+.4f}")
print(f" if R^2 above is in the same ballpark as autocorr^2, raw tensions add nothing.")