The basics, before any experiment in this project is designed or run.
Every rule below is here because it was broken, at cost, in a run that is already documented. The cost is named in each one. This is not general quant wisdom borrowed from elsewhere — it is this project's own scar tissue.
Scope, fixed: price data only. BTC bars and anything derivable from them. No funding rates, no cross-asset series, no order book, no sentiment. Sharpe stays the core measure. An annual trade minimum stays. The dashboard is live from generation one.
The standard error of an annualised Sharpe is roughly 1/sqrt(years). On a 3-month test window that is ±2.00. The 14-fold walk-forward ran to completion, at hours of compute, to produce a 95% confidence interval of roughly [−2.34, +1.16] — an interval containing a true Sharpe of 1.0. The experiment was arithmetically unable to answer its own question before it started.
Rule: write the detectable effect size at the top of the design doc. If the effect you are hunting is smaller than the interval, the design is dead on arrival — change the design, not the interpretation.
Averaging per-fold Sharpes let a single fold at −5.13 drag the headline. And a 6-month step against a 3-month test leaves gaps: only 3.5 of 9 years was ever scored.
Rule: step length equals test length — no gaps, no overlapping test bars. Concatenate every out-of-sample return into one series and score it once. Per-fold numbers exist for diagnosis, never for the headline.
A minimum of 100 trades/yr at 20bps round trip is 20% of equity per year in fees at the lowest turnover the rules even permit. Median observed was 55%/yr, worst fold 203%/yr. The floor was not protecting the Sharpe estimate; it was taxing it.
Rule: 12 to 100 trades per year. The number that used to be the mandatory minimum is now the hard ceiling.
few events to mean anything.
in fees. Past it, turnover is eating the edge faster than any signal is producing it.
And the band is not the same test as statistical validity. The band governs what a search is allowed to select. Whether a result is readable is a separate question, answered by total trades over the sample — 30 minimum. Conflating those two jobs is exactly what made the first version of this commandment wrong.
The Turtle test is the case in point: 20/10 trades 5.4 times a year, below the band, yet accumulates 49 trades over 9.1 years and is perfectly readable. A slow trend system is not disqualified by the band — it is simply outside the regime the band was written for, and gets judged on total trades instead.
706 parameters, 155,520 training bars, and a fitness function that was the p25 of Sharpe over six sub-windows. Six numbers. The bars never reached the optimiser.
Rule: the objective is scored over at least 24 chunks (monthly, not quarterly), and it carries a drawdown term. If the fitness function can be written down as fewer numbers than the model has parameters, it is a bottleneck, not an objective.
Max hold was 8 hours against a 16.7-hour context window, while the only strategy in this entire project that has ever survived validation is a daily EMA trend rule held for weeks. The search could not have found it if it were sitting there in plain sight.
Rule: the bar size and hold limits must span the horizon of the best known result, with room either side. Multi-day holds are permitted by default. If a design excludes the incumbent champion's shape, that design is answering a different question than the one asked.
1,196 parameters against 16 windows; 706 against ~5 effective samples. Then fixing the search made out-of-sample five Sharpe points worse — because a better search against a fixed training set with no generalisation pressure finds a better fit to noise.
Rule: start at a linear policy over ~10 price-derived features — 11 parameters. Capacity is earned by beating the linear version on the concatenated out-of-sample series, never assumed at the start.
The turnover-matched random-genome null is the single most informative thing ever built here: it showed the GA beats 10,000 random draws in 13 of 14 folds, which turned "the search is broken" into "the search is competent and the objective is broken." The unmatched null was contaminated — random genomes trade ~4,400/yr, so beating them only proved a genome had learned to stop churning.
Rule: every result is reported against (a) a turnover-matched random search over the identical scoring path, and (b) buy-and-hold BTC over the identical bars. Excess over buy-and-hold, never raw return. A single-window result with no null is not a result.
125 pre-specified strategies were generated without fitting anything, and the top of the ranked list still landed almost exactly where pure noise predicts: expected max Sharpe from N draws is about SE x sqrt(2 ln N) ≈ 1.0 for N=100 at 9 years — the observed best was 1.13–1.38. Separately, a validation criterion correlated +0.414 with test performance at 11 folds and +0.011 at 14. Interim correlations at small N are not findings.
Rule: maintain a looks ledger — every variant, every re-run, every generation checked against a held-out slice counts as a look. Publish SE x sqrt(2 ln N) beside the best result, always. And never report an interim correlation as a result before the sample is complete.
A champion once posted a good Sharpe with an −85% drawdown and nobody saw it until Calmar was added to the table. The live sim's 1.5bps is not a real cost.
Rule: cost at MEXC taker rates, because that is the venue. Entries on a breakout are marketable orders, so maker rebates do not apply and must never be assumed:
| per side | round trip | |
|---|---|---|
| MEXC futures maker | ~0.01% (1bp) | 2bps — only for genuinely resting orders |
| MEXC spot taker | ~0.05% (5bps) | 10bps |
| MEXC futures taker — the default | ~0.06% (6bps) | 12bps |
These are tier-0 published rates and move with VIP tier and promotions; confirm against the live schedule before any number here is quoted outward.
And never let one fee number carry a conclusion. Report the result across a fee range and show that the finding survives it. Turtle 20/10 runs Sharpe 1.26 at zero fees and 1.22 at 40bps round trip — the assumption is not load-bearing, which is itself the finding: a system trading 5 times a year is structurally fee-insensitive, while the old 100-trades/yr floor made fees the dominant term. If a result does depend on the fee assumption, that dependency is the headline, not a footnote.
Every performance table shows Sharpe, return, max drawdown, Calmar, win rate, and trades/year. A Sharpe without a drawdown beside it is not a result. Excess return over a cratering benchmark is not an edge.
validation_progress.json was overwritten on each fold, so when the run finished, the per-generation history for 13 of 14 folds was gone — the data needed to diagnose why it failed had been thrown away by the code that produced the failure.
Rule: the dashboard is live from generation one and shows the full metric set, both nulls, and the looks counter as they evolve. Every generation of every fold is written to disk and never overwritten. A run you cannot watch while it happens, or diagnose after it ends, is a run you will simply have to repeat.
| old | new | |
|---|---|---|
| Bars / max hold | 5-minute / 8 hours | daily / up to 60 days |
| Test window | 3 months | 12 months |
| Step | 6 months (gaps, 67% train overlap) | 12 months (no gaps, no test overlap) |
| Headline score | mean of 14 per-fold Sharpes | one Sharpe on the concatenated OOS series |
| Objective width | p25 over 6 quarterly chunks | p25 over 24+ monthly chunks, drawdown term |
| Trade frequency | floor of 100/yr (≈20% equity/yr in fees) | band of 12–100/yr, plus ≥30 trades total |
| Parameters | 706–1,196 | 11 (linear), capacity earned |
| Nulls | none in the headline | turnover-matched random + buy-and-hold |
| Fees | 5bps | 14bps round trip |
| Inputs | 5m-derived indicators | price only, raw window + daily context |
Pre-registered kill criteria — fixed before the run, not after:
~7 out-of-sample years). Below that is a negative result, whatever the per-fold table looks like.
beating it in-sample only proves the optimiser works, which is already established and is not the question.
not investable and does not count.
Signed off 2026-09-11: Commandment III turns MIN_TRADES_PER_YEAR = 100 from a mandatory minimum into a band of 12–100 trades/year, with a separate ≥30-total-trades test for whether a result is readable at all. This is a real change to the trading mechanics, not a tuning tweak. The first draft of the rule (a 12–24/yr floor) was wrong and was caught by the Turtle test, which it would have disqualified — the fix is recorded here rather than quietly patched.