93 lines
4.3 KiB
Python
93 lines
4.3 KiB
Python
|
|
"""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.")
|