"""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.")