Warrior_EA/research/test_context3.py

93 lines
4.3 KiB
Python
Raw Permalink Normal View History

"""Settling the context score with nine instruments instead of four.
On the honest engine the Wyckoff context slope landed at +0.0239 R/trace, t +1.37, sign held
in 6 of 8 cells - too weak to trade, too consistent to dismiss. `sqxbars.py` has since made
five more instruments readable, sharing no data path with the original four: FTSE100, UK100,
WTI (two independent feeds) and USDCAD.
The test is unchanged and was fixed before any of this ran: the ORIGINAL LPS configuration
(market entry at the next bar's open, stop at the retest extreme, 2R target), scored by the
five traces of book 2 2.3 / 7.1, with the single pre-specified prediction that expR rises
monotonically with the number of agreeing traces. Nothing is tuned per instrument.
WHAT WOULD SETTLE IT EITHER WAY
-------------------------------
real the pooled slope holds near +0.024 with t comfortably past 2, and the new
instruments - which had no hand in choosing anything - carry their share of it
nothing the slope drifts toward zero as power rises, and the new instruments split evenly
The second is what a small sample of noisy cells looks like when it is finally given enough
data to speak. The five new instruments are the honest out-of-sample here: every parameter in
this test was set on the original four.
Bases on the breadth instruments use a SYNTHESISED spread and are approximate; the slope is
not, because cost is nearly uncorrelated with the context score (measured: +0.0008 R/trace).
"""
import numpy as np, sys, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
import fills, book, breadth, test_lps2
NATIVE = ('EURUSD', 'USDJPY', 'XAUUSD', 'SP500')
def cells(tfs=('M15', 'H1')):
for tf in tfs:
for s in NATIVE:
bk = fills.Book(s)
yield s, tf, bk, book.frame(s, tf, bk), 'original'
for s in breadth.SPREAD_BP:
bk, f = breadth.get(s, tf)
yield breadth.NICE[s], tf, bk, f, 'new'
if __name__ == '__main__':
tfs = tuple(a for a in sys.argv[1:] if a in ('M15', 'H1', 'H4')) or ('M15', 'H1')
print("=== CONTEXT SLOPE: NINE INSTRUMENTS ===")
print(" same LPS configuration, unchanged. The five 'new' instruments had no hand")
print(" in choosing any parameter, so they are the out-of-sample arm.\n")
print(f" {'sym':>9}{'tf':>5}{'set':>10}{'n':>7} "
+ "".join(f"{k:>12}" for k in range(5)) + f"{'slope':>9}{'t':>7}{'base':>9}")
PR, PA, PT, tag = [], [], [], []
for s, tf, bk, f, kind in cells(tfs):
try:
a = test_lps2.events(s, tf, bk=bk, f=f)
except Exception as ex:
print(f" {s:>9}{tf:>5}{kind:>10} failed: {ex}")
continue
if a is None:
print(f" {s:>9}{tf:>5}{kind:>10} - too few")
continue
R, ag = a['R'], a['ag']
txt = [f"{R[ag==k].mean():+6.3f}({int((ag==k).sum()):>4})"
if (ag == k).sum() >= 25 else f"{'-':>12}" for k in range(5)]
sl, tt = book.slope_t(R, ag)
print(f" {s:>9}{tf:>5}{kind:>10}{a['n']:>7} " + "".join(txt)
+ f"{sl:>+9.4f}{tt:>+7.2f}{R.mean():>+9.4f}")
PR.append(R); PA.append(ag); PT.append(a['t']); tag.append((kind, sl, R.mean()))
if not PR:
sys.exit()
def pooled(sel, label):
R = np.concatenate([r for r, k in zip(PR, tag) if sel(k)])
A = np.concatenate([a for a, k in zip(PA, tag) if sel(k)])
sl, tt = book.slope_t(R, A)
pos = sum(1 for k in tag if sel(k) and k[1] > 0)
tot = sum(1 for k in tag if sel(k))
print(f" {label:<26} n={len(R):>7,} slope {sl:>+8.4f} t {tt:>+6.2f}"
f" base {R.mean():>+8.4f} positive-slope cells {pos}/{tot}")
return R, A
print()
Rn, An = pooled(lambda k: k[0] == 'original', 'ORIGINAL four')
Rb, Ab = pooled(lambda k: k[0] == 'new', 'NEW five (out of sample)')
R, A = pooled(lambda k: True, 'ALL nine')
print()
for k in range(6):
m = A == k
if m.sum() < 25:
continue
se = R[m].std(ddof=1) / np.sqrt(m.sum())
print(f" {k} agree n={int(m.sum()):>6} expR {R[m].mean():+7.4f} +/- {se:.4f}"
f" t {book.tstat(R[m]):+6.2f}")
print("\n The out-of-sample line is the one that matters: those five instruments")
print(" had no hand in choosing the trigger, the traces, or any threshold.")