Warrior_EA/research/test_controlbias.py
AnimateDread f1b7dcf7f3 fix: correct MI sample alignment and improve BN weight diagnostic report
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.
2026-08-02 08:12:47 -04:00

89 lines
3.8 KiB
Python

"""Is "breakouts underperform nearby random entries" a finding, or a property of the control?
The drift run reported real minus local control at -0.045 R, negative for longs AND shorts,
and it did not go away when the barriers were re-sized from a slow ATR. The tempting reading
is that breakout continuation is anti-predictive and the fade is worth +0.045.
Before believing that, look at what the control actually is. A breakout bar is BY
CONSTRUCTION a local price extreme - price has just traded beyond the top of a range that
contained it for many bars. A control bar drawn uniformly from +/-250 bars around it is
therefore drawn from a window in which the real entry sits near the high (for an upward
breakout) or near the low (for a downward one).
Within any window, buying at the top and selling at the bottom is the worst possible entry
for a mean-reverting series. So real-minus-control would be negative for both directions even
if the breakout carried no information at all. The two arms differ in ENTRY PRICE, not only
in timing.
THE CHECK
---------
For each event, the percentile of the entry price within the +/-250 bar window, for the real
arm and the control arm, oriented so that HIGH means "a worse place to enter for this trade's
direction". A fair control sits near 50 for both. If the real arm sits near 90 while the
control sits near 50, the -0.045 is the control's geometry and there is nothing to fade.
"""
import numpy as np, sys
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
import fills, book, wyckoff
SYMS = ('EURUSD', 'USDJPY', 'XAUUSD', 'SP500')
def pct_in_window(c, bars, span):
"""Percentile of c[bar] within c[bar-span : bar+span], per event."""
out = np.empty(len(bars))
n = len(c)
for k, b in enumerate(bars):
lo, hi = max(0, b - span), min(n, b + span + 1)
w = c[lo:hi]
out[k] = 100.0 * (w < c[b]).mean()
return out
def run(sym, tf='H1', span=250, seed=3):
bk = fills.Book(sym)
f = book.frame(sym, tf, bk)
ev = wyckoff.breakouts(f, score=False)
if ev is None:
return None
rng = np.random.default_rng(seed)
rows = []
for d0 in (+1, -1):
m = ev['d'] == d0
if m.sum() < 150:
continue
bars = ev['i'][m] + 1
bars = bars[(bars > 300) & (bars < f.n - 5)]
ctl = np.clip(bars + rng.integers(-span, span + 1, len(bars)), 301, f.n - 6)
pr = pct_in_window(f.c, bars, span)
pc = pct_in_window(f.c, ctl, span)
#--- orient so HIGH = a worse place to enter for this direction
if d0 < 0:
pr, pc = 100 - pr, 100 - pc
rows.append((d0, len(bars), pr.mean(), pc.mean()))
return rows
if __name__ == '__main__':
syms = [s for s in sys.argv[1:] if s in SYMS] or list(SYMS)
print("=== IS THE LOCAL CONTROL FAIR? ===")
print(" entry-price percentile within the +/-250 bar window, oriented so HIGH = a")
print(" worse entry for that direction. A fair control sits near 50 on both arms.\n")
print(f" {'sym':>7}{'tf':>4}{'side':>6}{'n':>6}"
f"{'real pct':>10}{'ctrl pct':>10}{'difference':>12}")
d = []
for sym in syms:
for tf in ('H1', 'H4'):
r = run(sym, tf)
if not r:
continue
for d0, n, a, b in r:
d.append(a - b)
print(f" {sym:>7}{tf:>4}{'long' if d0>0 else 'short':>6}{n:>6}"
f"{a:>10.1f}{b:>10.1f}{a-b:>+12.1f}")
d = np.array(d)
print(f"\n mean real-minus-control percentile: {d.mean():+.1f} points "
f"({int((d>0).sum())}/{len(d)} cells worse)")
print("\n A large positive number means the two arms are not comparable: the breakout")
print(" arm is entering at a systematically worse price within the same window, so")
print(" real-minus-control measures the control's geometry, not the signal.")