Warrior_EA/research/wyckoff_windows.py

30 lines
1.9 KiB
Python

"""
Loud-window conditioning of the Wyckoff ledger (2026-10-05). PRE-REGISTERED: per symbol, the 8 highest mean-range
hours and the 3 highest mean-range weekdays are chosen from 2010-2017 ONLY; the four setups are then evaluated
inside those windows on 2018-2024 (OOS). Control = same direction on every box bar inside the same windows.
"""
import os, sys, numpy as np, pandas as pd
sys.path.insert(0, os.path.dirname(__file__))
import discover as D
from wyckoff_scan import thin, tstat
L = pd.read_csv(os.path.join(os.path.dirname(__file__), "wyckoff_ledger.csv"), parse_dates=["t"])
loud_h, loud_d = {}, {}
for s in L.sym.unique():
b = D.load_h1(s); b = b[(b.index < "2018-01-01") & (b.index.year >= D.START[s])]
rg = (b.h - b.l) / b.c
loud_h[s] = set(rg.groupby(b.index.hour).mean().nlargest(8).index)
loud_d[s] = set(rg.groupby(b.index.dayofweek).mean().nlargest(3).index)
print(s, "hours", sorted(loud_h[s]), "days", sorted(loud_d[s]))
L["loud"] = [(t.hour in loud_h[s]) and (t.dayofweek in loud_d[s]) for s, t in zip(L.sym, L.t)]
oos = L[L.t >= "2018-01-01"]
setups = [("SPRING_L", (oos.spring == 1) & (oos.prior == -1), "rL"), ("SOS_L", oos.sos == 1, "rL"),
("UTAD_S", (oos.utad == 1) & (oos.prior == 1), "rS"), ("SOW_S", oos.sow == 1, "rS")]
print(f"\nOOS 2018-2024. {'setup':9s}{'window':8s}{'n':>5s}{'meanR':>8s}{'t':>6s}{'ctl':>8s}{'ctl n':>7s}{'lift':>7s}")
for name, m, col in setups:
for w, sel in (("loud", oos.loud), ("quiet", ~oos.loud)):
tr = pd.concat([thin(g) for _, g in oos[m & sel].groupby("sym")]) if (m & sel).sum() else oos[m & sel]
ctl = pd.concat([thin(g) for _, g in oos[sel].groupby("sym")])
if len(tr) < 3: print(f"{'':20s}{name:9s}{w:8s}{len(tr):5d} too few"); continue
x, y = tr[col].to_numpy(), ctl[col].to_numpy()
print(f"{'':20s}{name:9s}{w:8s}{len(x):5d}{x.mean():8.3f}{tstat(x):6.1f}{y.mean():8.3f}{len(y):7d}{x.mean()-y.mean():7.3f}")