Warrior_EA/research/test_crossfeed.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

111 lines
5.6 KiB
Python

"""Independent validation of the data chain every result rests on.
Everything measured in this project now flows through one pipeline:
SQX tick .dat -> sqx.py decode -> bidask.py M1 bid/ask -> book.py HTF mid bars
Four stages, each with a silent failure mode. The decoder's price SCALE in particular does
not crash when it is wrong, it just rescales every price - the trap that is already recorded
twice in this project's notes. A cross-check needs a source that shares NONE of those stages.
MT5's own exported rate CSVs are exactly that: the5ers' bars, delivered through the terminal,
written by the EA. Same broker and same instrument, so they should agree bar for bar; a
completely different code path, so agreement means the chain is sound.
WHAT IS COMPARED, AND WHY EACH MATTERS
--------------------------------------
bar count and overlap catches a timestamp/epoch or timezone error
close-price correlation catches a scale error (a wrong scale still correlates at 1.0, so
this alone is NOT sufficient - which is the point of the next one)
median |close - close| in POINTS, catches the scale error the correlation misses
high>=low, ask>=bid structural sanity on the derived bars
ATR ratio catches a systematic range distortion from the M1 aggregation
A correlation of 1.000 with a median difference of half a spread is what a healthy chain
looks like. A correlation of 1.000 with a median difference of 10x the spread is a scale bug
wearing a correlation as a disguise.
"""
import numpy as np, sys, os
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
import book, fills
R = "C:/Users/admin/AppData/Roaming/MetaQuotes/Terminal/Common/Files/Warrior_EA/Research/"
#--- MT5 timeframe codes
TFCODE = {'M5': 5, 'M15': 15, 'H1': 16385, 'H4': 16388, 'D1': 16408}
PT = {'EURUSD': 1e-5, 'USDJPY': 1e-3, 'XAUUSD': 0.01, 'SP500': 0.01}
def mt5_bars(sym, tf):
fn = f"{R}{sym}_{TFCODE[tf]}_rates.csv"
if not os.path.exists(fn):
return None
d = np.genfromtxt(fn, delimiter=',', names=True)
return d['time'].astype(np.int64), d['open'], d['high'], d['low'], d['close']
def compare(sym, tf):
m = mt5_bars(sym, tf)
if m is None:
return None
mt, mo, mh, ml, mc = m
f = book.frame(sym, tf)
ot = f.t // 1000 # ours is ms, MT5's is seconds
#--- align on shared timestamps only; neither side is trimmed to fit the other
common, ia, ib = np.intersect1d(ot, mt, return_indices=True)
if len(common) < 200:
return dict(sym=sym, tf=tf, n=len(common), note='no overlap')
a_c, b_c = f.c[ia], mc[ib]
a_h, b_h = f.h[ia], mh[ib]
a_l, b_l = f.l[ia], ml[ib]
pt = PT[sym]
d_c = np.abs(a_c - b_c) / pt
corr = float(np.corrcoef(a_c, b_c)[0, 1])
atr_a = np.mean(a_h - a_l); atr_b = np.mean(b_h - b_l)
#--- MT5 bars close at the BID; ours close at the MID. So a healthy chain shows a
#--- SIGNED difference of about +half a spread and a residual around it that is small.
#--- Distinguishing that constant offset from real disagreement is the whole diagnosis:
#--- a scale or decode error moves the residual, a convention difference moves only the
#--- offset.
sgn = (a_c - b_c) / pt
half_sp = 0.5 * np.median(f.spread[ia]) / pt
resid = np.abs(sgn - np.median(sgn))
return dict(sym=sym, tf=tf, n=len(common), ours=f.n, theirs=len(mt),
corr=corr, med_pts=float(np.median(d_c)),
p95_pts=float(np.quantile(d_c, 0.95)),
rng=float(atr_a / max(atr_b, 1e-12)),
offset=float(np.median(sgn)), half_sp=float(half_sp),
resid=float(np.median(resid)), resid95=float(np.quantile(resid, 0.95)),
bad=int((a_h < a_l).sum()))
if __name__ == '__main__':
syms = [s for s in sys.argv[1:]] or ['EURUSD', 'USDJPY', 'XAUUSD', 'SP500']
print("=== CROSS-FEED VALIDATION: SQX tick chain vs MT5 exported bars ===")
print(" same broker and instrument, completely different code path.")
print(" 'med pts' is the median close difference in POINTS - the scale check that")
print(" a correlation of 1.000 cannot make for you.\n")
print(f" {'sym':>7}{'tf':>4}{'overlap':>9}{'corr':>9}"
f"{'offset':>9}{'half sp':>9}{'o/hs':>7}"
f"{'resid p50':>11}{'resid p95':>11}{'range':>8}{'h<l':>5}")
ok = True
for sym in syms:
for tf in ('M15', 'H1', 'H4'):
r = compare(sym, tf)
if r is None:
print(f" {sym:>7}{tf:>4} - no MT5 export")
continue
if r.get('note'):
print(f" {sym:>7}{tf:>4} - {r['note']} ({r['n']} shared bars)")
continue
#--- the offset is allowed to be a half spread (bid vs mid). What must be small
#--- is the RESIDUAL around it - that is where a decode error would show.
ratio = r['offset'] / max(r['half_sp'], 1e-9)
flag = ''
if r['corr'] < 0.9999 or r['resid'] > 3 * max(r['half_sp'], 1) \
or not 0.8 < r['rng'] < 1.25:
flag = ' <-- CHECK'; ok = False
print(f" {r['sym']:>7}{r['tf']:>4}{r['n']:>9,}{r['corr']:>9.5f}"
f"{r['offset']:>+9.1f}{r['half_sp']:>9.1f}{ratio:>7.2f}"
f"{r['resid']:>11.1f}{r['resid95']:>11.1f}{r['rng']:>8.3f}{r['bad']:>5}{flag}")
print(f"\n offset ~= half spread (o/hs ~ 1) is the expected bid-vs-mid convention.")
print(f" {'chain agrees with an independent source' if ok else 'residual too large somewhere - investigate before trusting that cell'}")