Download scripts/_analyze_normal_high.py from Dev2506/flash-crash-watchdog: direct link, hf CLI and curl.
- Browser
- Download file 3.78 kB
-
https://huggingface.co/Dev2506/flash-crash-watchdog/resolve/main/scripts/_analyze_normal_high.py
- Command line
-
hf download hf://Dev2506/flash-crash-watchdog/scripts/_analyze_normal_high.py
-
curl -L -o _analyze_normal_high.py https://huggingface.co/Dev2506/flash-crash-watchdog/resolve/main/scripts/_analyze_normal_high.py
3.78 kB
| #!/usr/bin/env python3 | |
| """Find which normal-day windows score > 0.45 with the prod TCN and characterize | |
| their price action (small dip? noise?) vs the full normal-day distribution.""" | |
| import argparse, logging, sys | |
| from pathlib import Path | |
| import numpy as np, pandas as pd, torch | |
| ML_DIR = Path(__file__).resolve().parent.parent / "ml" | |
| sys.path.insert(0, str(ML_DIR)) | |
| from flash_crash_watchdog.data.historical_loader import df_to_ticks | |
| from flash_crash_watchdog.features import FEATURE_NAMES, FeatureExtractor | |
| from flash_crash_watchdog.models.stage3_tcn import TCNConfig, TCNDetector | |
| torch.serialization.add_safe_globals([TCNConfig]) | |
| logging.basicConfig(level=logging.WARNING) | |
| W = 200 | |
| NORM = 500 | |
| CH = 4096 | |
| def normalize_z(f, window): | |
| pdf = pd.DataFrame(f) | |
| mean = pdf.rolling(window, min_periods=1).mean() | |
| std = pdf.rolling(window, min_periods=1).std() | |
| return ((pdf - mean) / std).where(std.abs() > 1e-8, 0.0).fillna(0.0).to_numpy(np.float32) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--data", required=True) | |
| ap.add_argument("--model", default="models/stage3_tcn_prod.pt") | |
| ap.add_argument("--thr", type=float, default=0.45) | |
| ap.add_argument("--lookahead", type=int, default=500) | |
| ap.add_argument("--max-ticks", type=int, default=0) | |
| a = ap.parse_args() | |
| dev = "cuda" if torch.cuda.is_available() else "cpu" | |
| df = pd.read_parquet(a.data) | |
| if a.max_ticks and len(df) > a.max_ticks: df = df.iloc[:a.max_ticks] | |
| ticks = list(df_to_ticks(df, symbol="EVAL")) | |
| ex = FeatureExtractor(); F = np.zeros((len(ticks),17), np.float32); mid = np.zeros(len(ticks), np.float64) | |
| ts = np.zeros(len(ticks), np.float64) | |
| for i,t in enumerate(ticks): | |
| fd = ex.extract(t); F[i]=[float(fd.get(k,0.0)) or 0.0 for k in FEATURE_NAMES[:17]] | |
| mid[i]=float(t.book.mid_price) if t.book.mid_price else 0.0 | |
| ts[i]=float(t.book.timestamp_ms) | |
| st = torch.load(a.model, map_location='cpu', weights_only=True) | |
| cfg = st["config"] if isinstance(st["config"], TCNConfig) else TCNConfig(**st["config"]) | |
| model = TCNDetector(cfg).to(dev); model.load_state_dict(st["model_state"]); model.eval() | |
| norm = normalize_z(F, NORM) | |
| n = len(norm)-W+1 | |
| scores = np.empty(n, np.float32) | |
| for s in range(0,n,CH): | |
| e=min(s+CH,n) | |
| win=np.stack([norm[i:i+W] for i in range(s,e)]) | |
| x=torch.from_numpy(np.ascontiguousarray(win)).permute(0,2,1).float().to(dev) | |
| with torch.no_grad(): scores[s:e]=model(x)[:, -1].cpu().numpy() | |
| hi = np.where(scores > a.thr)[0] | |
| print("total windows=%d >%.2f: %d" % (n, a.thr, len(hi))) | |
| print("score dist p50=%.3f p90=%.3f p99=%.3f max=%.3f"%( | |
| np.percentile(scores,50),np.percentile(scores,90),np.percentile(scores,99),scores.max())) | |
| # characterize the high windows | |
| print("\nidx score win_drop% fut_drop% mean_|1s return|%") | |
| drops=[]; big=0 | |
| for j in hi: | |
| w0 = mid[j]; wend = mid[j+W-1] | |
| fut = mid[j+W:j+W+a.lookahead] | |
| win_drop = (w0-wend)/w0*100 if w0 else 0 | |
| fut_drop = (wend-min(fut))/wend*100 if wend>0 and len(fut) else 0 | |
| # intra-window volatility: mean abs per-tick return | |
| rets=np.abs(np.diff(mid[j:j+W])); r=rets.mean()/wend*100 if wend else 0 | |
| drops={"win":win_drop,"fut":fut_drop} | |
| big += 1 if (win_drop>1.0 or fut_drop>1.0) else 0 | |
| print("%5d %.3f %8.2f %8.2f %7.3f"%(j+W-1, scores[j], win_drop, fut_drop, r)) | |
| print("high windows with >1%% window or future drop: %d of %d"%(big,len(hi))) | |
| # overall normal-day drop stats for context | |
| ov=np.abs(np.diff(mid)); print("normal-day median abs-per-tick%%=%.4f p99=%.4f"%(np.mean(ov)/np.mean(mid)*100, np.percentile(ov,99)/np.mean(mid)*100)) | |
| if __name__=="__main__": main() |