Skip to content

research: a 200-day SMA filter on the daily turtle -- no arm distinguishable from the baseline (#830) - #833

Merged
eaitbrahim merged 4 commits into
mainfrom
feat/turtle-sma200-filter
Sep 27, 2026
Merged

eaitbrahim merged 4 commits into
mainfrom
feat/turtle-sma200-filter

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Closes #830.

Four commits, in the order that matters:

  1. d5e1485 feat(strategy): an optional trend_filter on turtle_breakout (off | above | slope | both, trend_sma_period 200, trend_slope_lookback 5).

    • Default off, so every existing rule behaves exactly as before.
    • Too little history to compute the average declines the entry (trend_filter_history) rather than letting it through.
    • 8 tests, each failing first, using fixtures that assert their own premise. 5 of 5 mutants killed.
  2. 696bc5b The pre-registration. The driver's docstring holds the arms, data, window, statistics, decision rule and my expectation. It was pushed at 00:39 UTC before any arm ran.

  3. 37cc6ac fix(research): the trials ledger now refuses two kinds of summary value it could never read back:

    • A non-numeric string raises on read, making the append-only ledger unreadable from that row on.
    • A float breaks the row's hash, so verify_chain reports it as tampered forever.

    Both were found when the first run's four rows failed to read back. Those rows were never committed. One test each.

  4. 8604e80 The record, data and four ledger rows (ablation, a_priori, diagnostic_only, with R series).

Result (1.20% taker plus per-product slippage, in R, common window)

Arm N Win % E[R] Δ vs off, 95% CI (cluster bootstrap by entry day)
off 202 26.7% +0.270 —
above 181 25.4% +0.218 [−0.641, +0.540]
slope 121 29.8% +0.419 [−0.515, +0.829]
both 121 28.9% +0.409 [−0.524, +0.824]

Pre-registered verdict: no arm is promising. Every arm clears the 100-trade floor, and no lower bound is above zero, so nothing goes to walk-forward.

  • slope and both point the hypothesised way, but inside an interval about 1.3 R wide.
  • above barely binds and does slightly worse.
  • The largest effect in the data is the excluded first ~205 days (the 2022 bear-market onset): 18 trades averaging about −1.0 R.
  • The baseline itself remains descriptive under ADR 0006: +5 win-rate points over break-even against a 14-point detectable edge.

Honesty notes

  • Edits since the pre-registration: the driver changed only in how figures are stored (numeric strings; words moved to params). git diff 696bc5b -- docs/experiments/2026-09-27-turtle-sma200-filter.py shows every line.
  • Reproducibility: the run is deterministic (seed 830), and the committed figures equal the first run's.
  • Prediction miss: 90% of entries were retained for above, not the 40–60% I predicted.

Verification

  • Full suite: 6785 passed, 3 skipped.
  • mypy, ruff check and ruff format --check are clean.
  • verify_chain on the tracked ledger: 98 rows, no problems.

🤖 Generated with Claude Code

eaitbrahim and others added 4 commits September 26, 2026 20:38
…kout (#830)

trend_filter in {off, above, slope, both} gates entries on a simple moving
average of trend_sma_period closes (default 200): the close above it, the
SMA rising over trend_slope_lookback bars (default 5), or both. Default
off: every existing rule behaves exactly as before. Too little history to
compute the SMA declines the entry (trend_filter_history) rather than
passing it, so a filtered arm never trades a bar its filter did not see.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ily turtle (#830)

The driver's docstring is the pre-registration: four arms (off, above,
slope, both) at 200/5, the daily paper rule set, the 2026-09-26 window, a
common eligibility window for every arm, the cluster-bootstrap difference
interval, the N>=100 floor, the decision rule, and the expectation --
committed before any arm has been run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d back

Two write-side gaps in _validate_summary, found recording #830. A
non-numeric string (a verdict, an arm name) is decoded as a Decimal by
read_trials and raises, making the append-only ledger unreadable from that
row on. A float is hashed as a JSON number but read back as a Decimal, so
verify_chain reports the row as tampered forever. Both are now refused at
append time, with a test each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rm distinguishable from the baseline (#830)

Pre-registered (696bc5b, before the run). On a common window, off: 202
trades +0.270 R; above 181 +0.218 R; slope and both 121 at ~+0.41 R, 95%
difference intervals ~[-0.52, +0.83]. No lower bound above zero, so under
the pre-registered rule nothing goes to walk-forward. Four ablation rows
appended to the trials ledger (diagnostic_only, a priori, R series).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@eaitbrahim eaitbrahim added feature New capability (groups under Features) research Measurement, validation or KB work (Research & validation) experiment A pre-declared experiment and its result (Research & validation) labels Sep 27, 2026
@eaitbrahim
eaitbrahim merged commit c8d68ac into main Sep 27, 2026
4 checks passed
@eaitbrahim
eaitbrahim deleted the feat/turtle-sma200-filter branch September 27, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experiment A pre-declared experiment and its result (Research & validation) feature New capability (groups under Features) research Measurement, validation or KB work (Research & validation)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Research: does a 200-day SMA trend filter improve the daily Turtle? (pre-registration)

1 participant