Warrior_EA/research/breadth_seasonal.py
AnimateDread b06fdd2f0e research: point the seasonal screen at the five breadth instruments
seasonal.py gets a frame-based entry (analyse_frame) and a reusable
report() so the identical statistics - circular-rotation family-wise
null, max-|t| bar, split-half - can run on instruments whose book is
synthesised from M1 bars. breadth_seasonal.py runs it on the five
SQX-decoded instruments (FTSE100, UK100, WTI x2 feeds, USDCAD) that
share no data path with the four originals; the duplicate-market pairs
(FTSE100/UK100, WTI_d/WTI_5) double as replication checks. Caveats
stated in the module docstring: synthesised flat spread (move/spread
is approximate, no intraday spread shape) and file-time clock labels;
drift/t columns are spread-free and unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 14:08:05 -04:00

59 lines
3.2 KiB
Python

"""The seasonal screen, pointed at the five instruments it has never seen.
seasonal.py's verdict on the four tick-backed instruments (EURUSD, USDJPY, XAUUSD, SP500) is
in: two directional survivors, both real, both smaller than their spread. This runs the
IDENTICAL statistics - same circular-rotation family-wise null, same max-|t| bar, same
split-half re-measurement - on the five SQX-decoded instruments that share no data path with
the originals: FTSE100, UK100, WTI (two independent feeds), USDCAD. Breadth is the one axis
the seasonal question has not been tested on, and two feeds of the same market (WTI, and
FTSE100/UK100) double as a replication check: a real effect must appear in both.
WHAT IS WEAKER HERE THAN IN seasonal.py, STATED UP FRONT
--------------------------------------------------------
spread synthesised, not quoted: breadth.MidBook applies a flat relative spread
(breadth.SPREAD_BP, rounded UP from measured peers). The move/spread and
spread/ATR columns are therefore APPROXIMATE and carry no hour-of-day spread
shape - a session-open spread spike is invisible. A bucket must clear its
spread by a wide margin before it is worth tick-level follow-up.
clock timestamps are whatever the SQX files carry (Dukascopy exports are typically
UTC, the5ers feeds broker time = UTC+2). Bucket STATISTICS are unaffected -
only the label's wall-clock meaning is; treat "hour k" as file-time and match
sessions per feed, not across feeds.
drift/t UNAFFECTED by both caveats: computed on mid-price log returns, spread-free.
Run: python breadth_seasonal.py (all five, hour/dow/month)
python breadth_seasonal.py UK100_the5ers ... (subset, by raw symbol name)
"""
import sys
import numpy as np
import breadth, seasonal
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if __name__ == '__main__':
syms = [s for s in sys.argv[1:] if s in breadth.SPREAD_BP] or list(breadth.SPREAD_BP)
frames = {}
print("=== loading (M1 -> H1, synthesised book: see module docstring) ===")
for s in syms:
try:
_, frames[s] = breadth.get(s, 'H1')
print(f" {breadth.NICE[s]:>8}: {frames[s].n:,} H1 bars")
except Exception as ex:
print(f" {breadth.NICE[s]:>8}: FAILED - {ex}")
survivors = []
for kind in ('hour', 'dow', 'month'):
print(f"\n{'='*100}\n=== {kind.upper()} - five breadth instruments, identical null "
f"and bar to seasonal.py (clock = file time) ===\n{'='*100}")
for s, f in frames.items():
res = seasonal.analyse_frame(f, breadth.NICE[s], kind=kind)
for k in seasonal.report(res):
survivors.append((breadth.NICE[s], kind, k))
print(f"\n{'='*100}")
if survivors:
print("family-wise survivors across the whole run (pre split-half):")
for s, kind, k in survivors:
print(f" {s} {kind} bucket {k}")
print("replication check: an effect real in FTSE100 must appear in UK100, and "
"WTI_d in WTI_5 - same market, independent feeds.")
else:
print("no bucket on any of the five instruments clears its family-wise bar.")