TurnOfMonthSeasonalOverlaySpyQqqUseq
Hypotheses
Turn-of-the-Month Seasonal Long Overlay on SPY + QQQ (USEQ, Daily Bars, Own each ETF Only During the [-1,+3] Month-End Window Above its 200-Day Trend, Flat in T-Bill Otherwise, Commission-Free, 3-Parameter)
Hypotheses
Implements the hypothesis exactly: SPY.USEQ + QQQ.USEQ on daily USEQ bars, owned only inside the [-1,+3] month-end window and only while each ETF is above its own 200-day trend, flat (cash/T-bill) otherwise, with exactly three free parameters (trend_sma_days, entry_days_before_end, exit_trading_day) plus a structural notional cap. The window is anchored to CALENDAR TIME derived from bar.ts_event (civil-date arithmetic + a count of sessions already observed in the current calendar month), never to a bar counter, so the schedule is identical across restarts, backtests and paper replay; entry is submitted one session before the window opens because a market order placed on a bar close fills at the next session's open, which makes the realised holding period exactly the open of day -1 to the open of day +4. Venue is USEQ per the hypothesis (equity, commission-free); the book is long-only and unlevered so leverage stays 1.0 and no leverage term appears in sizing. Trend history is 200 sessions (min_bars_required=202, inside the 500-bar buffer), the entry gate is a single seasonal condition (not a rare conjunction) so it fires ~12 times per year per leg - about 68 windows / 130+ round trips inside the ~2400-day sandbox window - and expected per-window index moves (~0.3-0.5%) clear the USEQ spread+impact cost (~0.02-0.05% round trip) with room to spare. One caveat noted for the record and implemented as asked: US market holidays are not modelled inside the sandbox (no exchange calendar is importable), so the only failure mode is Memorial Day landing on a month's last weekday, which shifts that single entry by one session a few times a decade.
Hypotheses
Overfit with no out-of-sample edge: walk-forward OOS Sharpe 0.019 (< 0.5 validity floor) vs IS 0.87, per-window OOS 0.235/-0.017/-0.162, PBO 0.77 (> 0.5), and failed deflated Sharpe: DSR=0.020, expected-max=0.47 over 225 trials (43 effective) — the selected Sharpe is indistinguishable from best-of-N noise, does not survive programme FDR (4 of 272), is_significant=false. Three HARD gates fail (OOS<0.5, is_overfitted, PBO>0.5) so promotion is impossible. Per-trade edge clears the USEQ fee floor and the holdout Sharpe is nominally positive (0.36), but the turn-of-month effect is too thin to generalize as a tradeable overlay — the strategy fit in-sample noise. This matches a persistent family failure (turn-of-month seasonality abandoned on QQQ, BTC, BNB and adjacent USEQ trend variants); reframing the same mechanism onto yet another target has repeatedly re-overfit, so there is no salvageable reframe.
Implementation
Turn-of-the-month seasonal long overlay on SPY + QQQ (USEQ, 1-DAY session bars, long-only, no leverage, commission-free). On every session close the strategy derives the bar's civil date from its own timestamp (pure integer civil-date arithmetic, no process-relative counters) and computes (a) how many weekdays remain in the month after today and (b) which session of the current month today is. It goes on risk at the close of the session `entry_days_before_end` (default 1) weekdays before the month's last weekday - so the next-open fill owns trading day -1 - holds through the turn, and closes out at the close of the `exit_trading_day`-th (default 3) session of the new month, capturing the [-1,+3] open-to-open block. Roughly 80% of the calendar is spent flat in cash (the 'T-bill' leg is not traded). Each ETF is an independent equal-weight half-sleeve gated by its OWN long-term trend: a leg is bought only when its close is above its own `trend_sma_days` (default 200) SMA, otherwise that half stays in cash, so bear-market month-ends (2008, 2022) are simply not bought. Sizing is whole shares (USEQ size precision 0) at 95%/2 of account equity per leg, computed from get_account_equity() at each entry; both legs are managed manually via _submit_entry_instrument/_submit_exit_position with a 'nothing pending anywhere' gate, and the base hooks (should_enter/should_exit) stay inert so the base never submits a competing SPY order.
Verification Results
Optionally derive the last-weekday anchor from the observed session bars (as _session_index already does) rather than a pure weekday count, so entry timing is holiday-aware like the exit. Not required.
Verification Results
Entry timing is anchored to calendar weekday arithmetic (_weekdays_left) which does NOT model US market holidays, whereas the exit is anchored to the actual session-bar count (_session_index) and is therefore holiday-robust. A month-end holiday landing on one of the last two weekdays (e.g. Memorial Day) shifts that single entry by one session. The developer documents this explicitly and it never changes the sign of the trade; src/data/market_calendar.py (XNYS) does exist and could tighten the entry, but the current approximation is acceptable and does not affect measurability.
Verification Results
No change needed if the base template invokes the subclass override for warm-up gating (it does). Left as informational.
Verification Results
min_bars_required(self) overrides a framework method (static-analysis base_shadow warning). This is deliberate here — it returns trend_sma_days + 2 to gate warm-up — and behaves correctly, but confirm the base honors the override rather than its own implementation.
Verification Results
Sandbox smoke test is marginal: Sharpe 0.107 (CI [-0.68, 0.94] straddles zero), avg_trade_return_pct 0.1535% (barely above the ~0.02-0.05% USEQ spread/impact floor), PF 1.15, win_rate 0.60. Regime split is unflattering — profit is concentrated in the 'normal' vol tercile (+10.44%) while 'calm' (-0.57%) and 'stressed' (-4.35%) both lose, and impact_cost_pct is 16.4% of gross with a modest $3.7M capacity. This is a real, well-documented calendar anomaly and the implementation is faithful, so it deserves a full backtest — but the analyst should confirm the per-window edge survives full-history spread+impact and is not confined to a single vol regime before optimizing.
Backtest Review
Faithful implementation of a well-documented calendar anomaly (turn-of-month) on a 33-year full-history sample (1993-2026), 500 long trades / 0 shorts / ~28.5% exposure — trades match the hypothesis exactly (long-only seasonal overlay, flat in cash otherwise).
Backtest Review
Edge clears USEQ costs comfortably: avg_trade_return_pct 0.338% (>> 0.05% abandon floor and >0.10% preference; passes L75), profit_factor 1.43, win_rate 0.60, impact only 7.9% of gross, capacity ~$15.9M.
Backtest Review
QA's key concern is NOT borne out on full history: regime_performance is positive in ALL three vol terciles (calm +5.1%, normal +37.3%, stressed +35.4%, only_one_regime=false) — the calm/stressed losses were a truncated-sandbox artifact.
Backtest Review
Low max_drawdown 12.2%, ample trade count for parameter fitting.
Backtest Review
Headline Sharpe is low (0.194, CI [-0.12, 0.53] straddles zero) — but this is a mostly-cash overlay (28% time-in-market) measured on the full mtm equity curve, so idle cash dilutes it; judge risk-adjusted quality against exposure at the promotion stage, not here.
Backtest Review
CAGR only 2.64% — an overlay by design; capital is idle ~72% of the time, so there is headroom to combine or scale rather than a defect.
Backtest Review
Some down years (2008 -6.7%, 2022 -8.3%, 2016 -4.1%) but the anomaly is positive in the majority of calendar years.
Analysis
Faithful, low-turnover implementation of a well-documented calendar anomaly (turn-of-month) on a 33-year full-history sample; trades match the hypothesis (500 long / 0 short, ~28% exposure, seasonal-window entries above own 200d trend).
Analysis
Per-trade edge clears the USEQ fee floor (avg_trade_return_pct 0.21% optimized, floor 0.05%); no sensitivity cliffs; drawdown low (6.1%); holdout Sharpe technically positive (0.36).
Analysis
Sensitivity surface is flat/smooth across trend_sma_days and exit_trading_day — no fragile parameter.
Analysis
Walk-forward is decisively overfit: IS Sharpe 0.87 vs OOS 0.019, and per-window OOS is 0.235 / -0.017 / -0.162 — the edge does not survive out of sample.
Analysis
PBO 0.77 (> 0.5) — the parameter selection is more likely than not overfitting.
Analysis
Deflated Sharpe 0.020 and PSR/is_significant=false; does not survive programme-level FDR (keeps 4 of 272; candidate p=0.98). The optimized Sharpe is indistinguishable from best-of-N noise over 225 trials (43 effective).
Analysis
OOS Sharpe 0.019 << 0.5 validity floor — three HARD, non-waivable failures.
Analysis
Preregistered predictions missed on the ones that matter: OOS Sharpe 0.019 vs declared 0.9, PF 1.39 vs 1.5, avg_trade_return 0.21% vs 0.30%.
Analysis
Family prior is strongly negative: turn-of-month seasonality has been abandoned repeatedly on adjacent targets (QQQ TOM fee_edge, BTC TOM fee_edge, BNB TOM other, QQQ vol-managed trend overfit). The anomaly is real historically but too thin to clear costs/deflation on a tradeable overlay.
Analysis
Benjamini-Hochberg at q=0.10 over 272 programme candidates keeps 4. A candidate that does not survive here is not distinguishable from the programme's own noise, however good its individual statistics look.
Outcome Summary
TurnOfMonthSeasonalOverlaySpyQqqUseq-764370a27c
Outcome Summary
This strategy faithfully implemented a well-documented calendar anomaly — owning SPY and QQQ only around the month boundary and only above their own long-term trend — and its backtest looked clean enough on 33 years of data (PF 1.43, 60% win rate, per-trade edge above the USEQ fee floor) that the analyst approved it for optimization. Optimization was where it fell apart: the in-sample Sharpe of 0.87 collapsed to 0.019 out of sample, PBO hit 0.77, and the deflated Sharpe of 0.020 showed the result was statistically indistinguishable from noise across 225 trials. Three non-waivable hard gates failed simultaneously, so the analyst abandoned it after a single iteration. It joins a persistent family of abandoned turn-of-month variants, confirming the effect is too weak to generalize into a live overlay.
Outcome Summary
The turn-of-month anomaly is real historically but too thin to clear costs and multiple-testing deflation as a tradeable overlay; reframing the same seasonality onto yet another target (after QQQ, BTC, BNB failures) keeps re-overfitting rather than yielding a generalizable edge.
Outcome Summary
The analyst abandoned it after optimization: three HARD validity gates failed (OOS Sharpe 0.019 < 0.5 floor, is_overfitted true, PBO 0.77 > 0.5), the deflated Sharpe was indistinguishable from best-of-N noise over 225 trials, and it did not survive programme-level FDR, making promotion impossible.
Outcome Summary
A long-only turn-of-month seasonal overlay owning SPY and QQQ (USEQ, daily bars) only across the [-1,+3] month-end window and only when each ETF was above its own ~200-day trend, sitting in cash otherwise.
Outcome Summary
On the full 1993-2026 sample it made 500 long trades with a 0.60 win rate, profit factor 1.43, avg per-trade return 0.34% (0.21% optimized), CAGR 2.64% and max drawdown 12.2%, but headline Sharpe was only 0.194 (CI straddling zero). Optimization exposed decisive overfitting: walk-forward IS Sharpe 0.87 vs OOS 0.019 (per-window 0.235/-0.017/-0.162), PBO 0.77, and deflated Sharpe 0.020.
Backtest and paper results are hypothetical. Trading involves risk of loss.