The MI sample builder used `MathAbs(labelBarOffset)` as a padding, causing rows from offset and non-offset builds to be paired with a double shift. This broke the positive control, failed the 5× gate, and voided all reported mutual‑information figures. Replace with the fixed `MiShiftPad` constant to ensure builds enumerate the same set of bars and row-k alignment is preserved. Add `BatchOptionsTotal()` to `CNeuronBatchNormOCL` and split the packed BN weight array in the learning report into separate norms for the outgoing dense matrix, gamma, beta, running statistics, and Adam moment buffers. This turns an ambiguous single‑norm reading into precise diagnostics that distinguish weight divergence from scaling issues.
121 lines
5.4 KiB
Python
121 lines
5.4 KiB
Python
"""The only base that has ever been positive - and whether anything can be bolted onto it.
|
|
|
|
Every trigger tested in this project starts at -0.15 to -0.33 R, so the context modifier has
|
|
never had anything to lift. But one thing HAS survived every test: drift. SP500 and gold rose
|
|
for the whole sample, the engine null re-measured it independently (random longs beat random
|
|
shorts by +0.08 to +0.10 R), and [[project_anomaly_families_tested]] found it was the single
|
|
family to clear cost.
|
|
|
|
So the question step 2 really asks is: **does a long-only breakout on a drifting instrument
|
|
give a base at or above zero, and does anything add to it?**
|
|
|
|
THREE ARMS, AND THE SECOND IS THE ONE THAT MATTERS
|
|
--------------------------------------------------
|
|
REAL range breakout in direction d, market entry at the next bar's open
|
|
LOCAL the same trade at a RANDOM bar within +/-250 bars - same instrument, same regime,
|
|
same drift rate, same barrier geometry, same n. Only the breakout is gone.
|
|
GLOBAL a random bar anywhere in the sample
|
|
|
|
If REAL - LOCAL is zero, the breakout contributes nothing and whatever expectancy the trade
|
|
has is exposure, not timing. That is the honest way to report a drift-powered result: it is
|
|
beta, and beta is available by buying and holding without paying a spread 3,000 times.
|
|
|
|
EURUSD and USDJPY are included as NEGATIVE CONTROLS. They barely drifted, so if the "edge"
|
|
is drift they should show nothing, and if a cell there lights up while the mechanism says it
|
|
should not, the mechanism is wrong rather than the cell being lucky.
|
|
"""
|
|
import numpy as np, sys, time
|
|
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
import fills, book, wyckoff
|
|
|
|
SYMS = ('EURUSD', 'USDJPY', 'XAUUSD', 'SP500')
|
|
MRISK, KR, H = 3.0, 2.0, 200
|
|
|
|
|
|
def trade(bk, f, bars, d, mrisk, kR, H, step):
|
|
"""Market entry at the open of `bars`, stop mrisk*ATR away, target kR*risk."""
|
|
atr = f.atr(14)
|
|
ok = (bars > 300) & (bars < f.n - 5) & np.isfinite(atr[bars]) & (atr[bars] > 0)
|
|
bars, d = bars[ok], d[ok]
|
|
start = f.i0[bars]
|
|
ent = np.where(d > 0, bk.ao[start], bk.bo[start])
|
|
risk = mrisk * atr[bars]
|
|
o = fills.simulate(bk, start, d, ent - d * risk, ent + d * kR * risk, H * step,
|
|
entry=fills.MARKET)
|
|
if o is None:
|
|
return None
|
|
o['indep'] = book.nonoverlap(o['idx'], o['exit_idx'] - o['idx'])
|
|
o['bars'] = bars[np.nonzero(o['filled'])[0][o['kept']]]
|
|
return o
|
|
|
|
|
|
def run(sym, tf, mrisk=MRISK, kR=KR, seed=3, span=250):
|
|
bk = fills.Book(sym)
|
|
f = book.frame(sym, tf, bk)
|
|
step = book.TF_SEC[tf] // 60
|
|
ev = wyckoff.breakouts(f)
|
|
if ev is None:
|
|
return None
|
|
rng = np.random.default_rng(seed)
|
|
out = {}
|
|
for d0, tag in ((+1, 'long'), (-1, 'short')):
|
|
m = ev['d'] == d0
|
|
if m.sum() < 150:
|
|
continue
|
|
bars = ev['i'][m] + 1
|
|
ag = ev['ag'][m]
|
|
real = trade(bk, f, bars, np.full(m.sum(), d0), mrisk, kR, H, step)
|
|
loc = trade(bk, f,
|
|
np.clip(bars + rng.integers(-span, span + 1, len(bars)), 301, f.n - 6),
|
|
np.full(m.sum(), d0), mrisk, kR, H, step)
|
|
glo = trade(bk, f, rng.integers(301, f.n - 6, len(bars)),
|
|
np.full(m.sum(), d0), mrisk, kR, H, step)
|
|
if real is None:
|
|
continue
|
|
sel = np.nonzero(real['filled'])[0][real['kept']]
|
|
out[tag] = dict(real=real, loc=loc, glo=glo, ag=ag[sel])
|
|
return out
|
|
|
|
|
|
def stat(o):
|
|
if o is None:
|
|
return np.array([0.0])
|
|
return o['R'][o['indep']]
|
|
|
|
|
|
def diff_t(a, b):
|
|
se = np.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b))
|
|
return (a.mean() - b.mean()) / max(se, 1e-12)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
syms = [s for s in sys.argv[1:] if s in SYMS] or list(SYMS)
|
|
print("=== DRIFT AS A BASE: does the breakout add anything to the exposure? ===")
|
|
print(f" stop {MRISK} ATR, target {KR}R, market entry. EURUSD/USDJPY are the")
|
|
print(" negative controls - little drift, so they should show little.\n")
|
|
print(f" {'sym':>7}{'tf':>4}{'side':>6}{'n':>6}{'REAL':>9}{'t':>6}"
|
|
f"{'local':>9}{'global':>9}{'R-local':>9}{'t':>6}{'ctx slope':>11}{'t':>6}")
|
|
rows = []
|
|
for sym in syms:
|
|
for tf in ('H1', 'H4'):
|
|
r = run(sym, tf)
|
|
if not r:
|
|
continue
|
|
for tag, v in r.items():
|
|
A = stat(v['real']); Lc = stat(v['loc']); G = stat(v['glo'])
|
|
sl, tt = book.slope_t(v['real']['R'][v['real']['indep']],
|
|
v['ag'][v['real']['indep']])
|
|
ex = A.mean() - Lc.mean()
|
|
rows.append((sym, tf, tag, ex, A.mean(), sl))
|
|
print(f" {sym:>7}{tf:>4}{tag:>6}{len(A):>6}{A.mean():>+9.4f}"
|
|
f"{book.tstat(A):>+6.2f}{Lc.mean():>+9.4f}{G.mean():>+9.4f}"
|
|
f"{ex:>+9.4f}{diff_t(A, Lc):>+6.2f}{sl:>+11.4f}{tt:>+6.2f}")
|
|
if rows:
|
|
ex = np.array([r[3] for r in rows])
|
|
base = np.array([r[4] for r in rows])
|
|
print(f"\n breakout excess over local control: mean {ex.mean():+.4f}, "
|
|
f"positive {int((ex>0).sum())}/{len(ex)}")
|
|
print(f" base expR: mean {base.mean():+.4f}, positive {int((base>0).sum())}/{len(base)}")
|
|
for tag in ('long', 'short'):
|
|
b = np.array([r[4] for r in rows if r[2] == tag])
|
|
print(f" {tag:>5}: base mean {b.mean():+.4f} positive {int((b>0).sum())}/{len(b)}")
|