forked from animatedread/Warrior_EA
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>
59 lines
3.2 KiB
Python
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.")
|