FIN-4 — every gate asks "is this real?" and none asks "is this better than the free version?": the benchmark is SPY, and no criterion anywhere requires beating a passive momentum ETF
status: open · raised 2026-09-10 (external financial review F5, confirmed — see research/2026-09-09_financial_review_verification.md) · class: decision relevance · judgement: YES — highest value per unit of effort in the review · effort ~0.5 pd · horizon: before December; a NO here is a permanent cheap NO
Plain-language summary. For a 12-2 momentum book on the Russell 1000, "does it beat SPY, beta-adjusted" is a question with a thirty-year published answer that says nothing about whether THIS implementation is worth running. The decision-relevant question is whether it beats MTUM — an ETF that harvests the same factor for 0.15%/yr with no cron, no reconciliation, no broker state, and no leak risk. As written, the project could clear every gate it has and still be strictly dominated by a one-line ETF purchase, and nothing in the harness would say so.
Ground truth
grep -rI "MTUM"returns nothing in the repository except one 2026-05-29 AFK transcript. It has never been a benchmark.config/settings.yaml:117and:303:benchmark: "SPY".backtest/metrics.pycomputes beta, Jensen's alpha, information ratio and capture ratios — all against that benchmark.- The SHIP gate (CLAUDE.md) is walk-forward Sharpe, PBO, IS-OOS correlation, survivorship-free confirmation, per-window regression, DSR. No factor-relative criterion at any step.
Fix shape (propose-only)
Add a momentum proxy as a SECOND benchmark (MTUM, or a long-only momentum index series if a fund proxy is unacceptable for licensing), report factor-relative metrics beside the SPY ones, and propose adding "beats the passive factor proxy net of costs" to the SHIP gate. The SHIP-gate amendment is an owner decision and this row does not presume it — the build may produce the measurement and the proposed wording; adopting it into the gate is a separate approval.
Why it is worth doing even though it may say NO
A NO is the valuable outcome: it is a cheap, permanent, correctly-registered "this specific thing is dominated" — the A3-pattern result NORTHSTAR §1 counts as an asset rather than a loss. It costs half a day and closes a question that would otherwise sit open through every future gate.
Test design + negative control
Factor-relative metrics computed over the frozen window must reproduce the known SPY-relative numbers when the benchmark is set back to SPY. Negative control: point the "momentum proxy" at SPY and assert the factor-relative panel becomes identical to the existing one — a benchmark parameter that is ignored is the usual way this kind of build fools itself.
Kill criterion (pre-registered)
If MTUM history cannot be obtained on the frozen-store terms this project uses (owned, acquire-once, no standing vendor dependency), the row drops to a long-only momentum index series; if neither is obtainable, it dies and records that the comparison is unavailable rather than substituting a proxy silently.
Triage note — 2026-09-14 (annotation only; the body above is untouched)
Two build-brief constraints found this run that should be written into the pre-registration before anyone starts, because both bound what the comparison can claim:
- MTUM's inception is April 2013. The comparison window is therefore ~2013→2026 and cannot cover the full frozen research window. Whether a 13-year window is sufficient for the pinned criterion is a registration question, and answering it after seeing the result is the failure this row exists to prevent.
- The fallback "long-only momentum index" is not free here.
data/factors.py:112 download_momentum_factoryields UMD, a long-short factor — not a substitute for a long-only investable proxy. MTUM itself is acquirable on the existing acquire-once terms (the same Tiingo path as the frozen SPY store).
Verified still true at HEAD 340bc5f: MTUM appears nowhere in src/,
config/, tests/ or data/ — only in the review and queue markdown and their
web/public/notes/ twins. config/settings.yaml:117 and :303 are both
benchmark: "SPY"; config/meanrev.yaml:110,:162 likewise. No
factor_benchmark key exists.
Partial overlap, against a different question, so not a substitute:
backtest/oos_monitor.py:328 compute_benchmark_companion gives a report-only
excess-over-SPY panel (live window: active Sharpe −2.720, beta 0.41). That
addresses beta contamination, not factor domination.
The 2026-09-09 placebo does NOT overtake this row, and the distinction matters. The placebo's null is 200 random 50-name books over 61 live days ("better than dumb stock-picking?"); FIN-4 asks "better than the cheap factor product?" over the research window. Different alternative, different horizon. What the placebo does change is the prior on the answer — live sat at the 2nd percentile — which raises this row's expected information content while lowering its expected upside to about zero. That is the row's own stated value proposition (a NO is the valuable outcome) and no builder should frame it as a hunt for a pass.
Triage annotation — 2026-10-05 — LAST WEEK'S OVERFLOW DISMISSAL IS NOT CARRIED FORWARD
This row was 12-cap-dismissed by the 2026-09-28 triage (PR #190, still unmerged, which is why the file is still here). That dismissal is declined today, on the record, for one reason: its stated rationale was retracted by this shop's own panel 24 hours after it was written.
PR #190's dismissal argued that "the question simply moved to a
better-instrumented home" — the hypothesis lab. Panel run 30
(research/2026-09-29_panel_run_30.md:216-220, 2026-09-29) retracted exactly
that: the lab's confirmatory reserve closed 5 of 5 the same day the lab
landed, so an MTUM study would now have to run through the exploratory pool
and carry a ×672 lab-wide multiple-testing correction. The lab is a
worse home for this question than it was when #190 was written, not a
better one. A 12-cap overflow dismissal is a capacity judgement and remains
legitimate as such — but it must be ranked on reasons that still hold, and
this one's did not survive the week. Reinstated and re-ranked below.
The defect is untouched at HEAD 502690b. The benchmark is SPY everywhere
that matters: config/settings.yaml:117 and :303, config/meanrev.yaml:110
and :162. No factor_benchmark or second_benchmark key exists in src/,
config/ or tests/ (grep: zero hits). The only benchmark-relative machinery
is SPY-only and report-only (src/thales/backtest/oos_monitor.py:328 compute_benchmark_companion, :309, :633-634). No gate anywhere requires
beating a passive momentum ETF.
TWO GROUND-TRUTH CLAIMS IN THIS FILE ARE NOW FALSE — corrected here rather than left to mislead a builder.
- The body at
:18asserts thatgrep -rI "MTUM"returns nothing but one 2026-05-29 AFK transcript. False at HEAD. MTUM is atconfig/lab.yaml:97—MTUM: {name: "MSCI USA Momentum", class: equity, region: US}, inside the lab's US style/factor catalog block (:92-99, beside IWD/IWF/IWN/IWO/USMV/QUAL) — and atresearch/lab/README.md:245, which independently corroborates this row's own April-2013-inception constraint ("Mixing late-starting ETFs (MTUM 2013, HYG 2007)…"). - The 09-14 annotation at
:78-80("Verified still true at HEAD340bc5f: MTUM appears nowhere insrc/,config/,tests/ordata/") is false at HEAD — it is inconfig/. Both claims became false with the lab commits547950a/e53fa72(2026-09-27); the citation drift:68 → :97was already recorded inresearch/2026-09-29_panel_run_30.md:213-215.
What that does to the row: it helps it. The acquisition objection is
partly pre-cleared — MTUM is now catalogued for acquire-once ingestion. It is
not acquired: data/lab/ is gitignored and absent from this container, so
whether MTUM price history has actually been fetched could not be verified
here and a builder must check before scoping. The UMD caveat stands:
src/thales/data/factors.py download_momentum_factor yields the long-short
academic factor, which is not the thing a retail alternative buys.
Rank this week: 9 of 12. It survives the cap on the two arguments that did not move: the external financial review called it the highest value per unit of effort in the review (0.5 pd), and its horizon — "before December; a NO here is a permanent cheap NO" — is 57 days out. With the live book at the 2nd percentile of the 2026-09-09 placebo null and the forward Sharpe at −1.32, "is this strictly dominated by a 0.15%/yr ETF" is a question the December gate can be cleared without ever asking. BLOCKED on: owner approval (and, for tidiness, on #190 being closed or superseded — see this week's report).
Triage annotation — 2026-10-19 (annotation only; the body above is untouched)
Unchanged at HEAD, and the body's own ground-truth claim is still wrong in
the way the 10-05 note recorded. config/settings.yaml:117 and :303 are
benchmark: "SPY" (and config/meanrev.yaml:110, :162 likewise);
grep -rn "factor_benchmark\|second_benchmark" src/ config/ tests/ → zero
hits; backtest/metrics.py is single-benchmark throughout (:52 beta, :66
information ratio, :80 capture ratio, :127 benchmark_returns feeding
:141-148). The only benchmark-relative machinery is SPY-only and report-only
(oos_monitor.py:328 compute_benchmark_companion).
MTUM appears in src/, config/ and tests/ exactly once —
config/lab.yaml:97 — so the body's :18 ("grep returns nothing except one
AFK transcript") and the 09-14 annotation's "MTUM appears nowhere" are both
still false, as corrected on 10-05. data/lab/ is absent in this container,
so whether the MTUM history has been acquired cannot be checked from here.
Live state: blocked on approval (the steward's, under the 2026-10-07 delegation; its 10-16 list does not name this row) · ~0.5 pd · deadline not fired; 43 days to the December gate, where a NO here is a permanent cheap NO.