I built the validation harness that killed my own strategy.
Swing Screener started as a CLI that scans a watchlist for the setups I trade. Then I built a backtest engine on top of it. Then I spent a week trying to destroy my own headline result — and largely succeeded. This page is about that second part, because it is the more useful engineering story.
The Problem
A backtest that confirms you is worth nothing.
My walk-forward said the anchored-VWAP-reclaim setup earned +0.23R over 101 trades. That is exactly the kind of number people put in a bio and then trade on. It came from eight correlated large-cap tech names I picked with hindsight, over one arbitrary window alignment. Before putting money behind it, the honest move is to build the test designed to kill it, not the one designed to confirm it.
What I Built
Three layers: screener, backtester, audit.
The screener
A terminal CLI over free yfinance data. Scans a watchlist for three setups — anchored-VWAP reclaim, Minervini-style volatility contraction with volume dry-up, and gap/opening-range levels — and prints a table of what triggered with the key levels. Saves a markdown report for the day.
The backtester
Replays the same detectors bar-by-bar with no lookahead: at each bar it only sees data up to that bar. When a setup fires it opens a trade using that setup's own entry and stop levels, targets a configurable R multiple, and walks forward to stop, target, or timeout. Reports hit rate, average R and expectancy per setup.
The audit harness
Universe expansion, survivorship bucketing, a 150-cell parameter sweep, regime splits, correlation-adjusted significance, block bootstraps, and — the one that mattered — signal-free control benchmarks. Six scripts, all reproducible from the repo.
What The Audit Found
The headline number did not survive.
Reproduced verbatim from my own report. The recommendation to myself: cap risk at 0.25% of equity, run a 60-trade forward test with a −10R kill switch, and do not scale until forward expectancy clears +0.10R. Trade it small or not at all.
1. It wasn't reproducible
The 126-bar window grid start is arbitrary. Sliding it across 21 alignments on the same eight names gives a median of +0.004R, ranging −0.045R to +0.287R. The +0.23R headline sat near the top of that distribution — a coin flip that landed well.
2. It's mostly long drift
Bucketing symbols by their own 10-year buy-and-hold return, expectancy is a clean monotonic function of it: +0.159R on winners, +0.038R (t = 1.22, not significant) on names that actually went down. That is the signature of harvesting market drift, not of a pattern edge.
3. The signal carries no information
The killer control: entries taken when price is above the anchored VWAP earn +0.100R; entries taken when price is below it earn +0.098R. Identical. The setup's entire premise — that reclaiming the anchored cost basis means something — tests as pure noise.
4. What's left, costs eat
Against a benchmark matched decile-by-decile on risk-as-%-of-price, the signal's incremental contribution is +0.030R, 95% CI [−0.014, +0.077] — crossing zero. Round-trip slippage of 5–10 bps costs 0.018–0.036R, i.e. 60–120% of it.
The Stack
Free data, no black boxes.
Indicator math — anchored VWAP, VCP contraction and volume dry-up, gap arithmetic — is unit-tested against small synthetic OHLCV fixtures with no network calls, so the tests are deterministic and fast.
Honest Status
A local tool and a research artifact.
What it is
A CLI I run on my own watchlist, plus a validation harness and a full written audit. Both work. The audit is reproducible from scripts in the repo.
What it is not
Not a profitable trading system, and I'm not claiming one. There is no live money behind it. The execution engine that would trade this setup exists and is tested, but it stays in paper mode precisely because of what this audit found.