Every rule the engine enforces, in plain language and in math. Nothing here is adjustable per customer, per tier, or per request — that is the point.
exit_resolution field — a coarser fallback is disclosed, never
silent. Competitors either guess the intra-bar path from OHLC or sell this
resolution as a paid add-on; here it is the default.| Gate | Plain English | Math |
|---|---|---|
| Count floor | Too few trades = no verdict, in either direction. Nulls are not run, and an underpowered attempt costs you nothing. | n_trades ≥ 100 for a full verdict; ≥ 10 for any statistics |
| The mirror image | The exact opposite strategy — same entries, reversed direction — must do worse, or your timing is backwards or irrelevant. | net_R(real) > net_R(flipped) |
| The Noise Twin test | 20 versions of your strategy with randomly shifted entry timing — same trade frequency, same exits, same costs. You must stand 3 standard deviations above that noise. | z = (R − mean(nulls)) / sd(nulls) ≥ 3, crc32-seeded,
reproducible forever |
| Every year positive | A real edge pays in every year, not one lucky regime. When 2020 is in the window, the ex-2020 result is computed too — the COVID year flatters almost anything. | net > 0 ∀ years; ex-2020 reported |
| Recent-third positive | Edges decay. The most recent 30% of days must be profitable. | Σ daily_R (final 30%) > 0 |
| The Luck Bar | The more trials you run, the better your best result looks by pure chance. Your Sharpe must beat the bar at YOUR cumulative look count — every run and every swept variant joins your ledger and none of it resets. Since engine v0.5.0 the count is effective: variants of one strategy (same market + entry mechanism + direction) pool as a family and enter as the square root of the family's size — a 44-cell TP/SL sweep on one idea adds ~6.6 effective looks, not 44 — while genuinely distinct ideas count in full. The discount is pre-registered and decoy-validated (pure-noise costume families stay below the discounted bar more reliably than independent noise stayed below the old full-count bar); receipts show raw and effective side by side. | Sharpe > max(√(2·ln Neff), 1) · √(1/T),
Neff = Σ √(family size) over your strategy families (mega
sweeps: √(configs × cells)), T = years of data. The
max(·, 1) is the cold-start floor: at N=1 the raw term is
0, but one draw of noise still has a typical magnitude of ~1 standard
deviation, so the very first look is gated at z=1 (e.g. 1/√14 ≈ 0.267
on 14 years) instead of zero. From N=2 the √(2·ln N) term already
exceeds it. |
| Beats buy & hold | Holding the instrument is free. Your timing must add at least +0.10 Sharpe over the better of holding long or short. | Sharpe − |Sharpe(hold)| ≥ 0.10 |
| Survives a one-bar delay | One extra bar of delay must not kill the edge — if it does, retail execution will never capture it live. This is a HARD gate on fast bars (15-minute and under); on slower bars it is reported but a slow strategy is not failed for it. | delayed net > 0 and ≥ 50% of undelayed |
| PASS | cleared every gate. Rare by design. |
| FAINT | beat its Noise Twin and the 2-sigma floor, but short of a full pass — promising, with a named gap. |
| DEAD | killed at a named gate, with the reason. |
| NOT JUDGED | too few trades (VETO_UNDERPOWERED, COUNT_FAIL), missing data (NO_DATA), couldn’t compile (UNCOMPILABLE), or a rule that never fires (VOID_DEGENERATE_SPEC) — “we couldn’t honestly test this” is never conflated with “it failed,” and it never costs credits. |
The max drawdown a backtest shows is one draw from a distribution: the same trades in a different order produce different worst paths. Every result includes a stationary block bootstrap (1,000 resamples, mean block 10 days — preserving short-range autocorrelation, seeded and reproducible) of the daily P&L, reported as drawdown percentiles with your observed path located inside the distribution. Size to the 95th percentile, not to the path you happened to get. Two honest notes: this is sizing information and gates nothing (reshuffling a series cannot change its mean), and regime clustering longer than the block length is partially broken by any bootstrap — so the tail percentiles are a floor, not a ceiling.
A mechanized adversarial battery run against a positive result: day/year concentration, drop-the-best-year jackknife, the TP/SL plateau grid, sibling-instrument placebos, a cost ladder to 2× the floor, and delay fragility. The verdict is REFUTED or NON-REFUTED with mandatory downgrades — restrictions on how the result may honestly be described. The audit re-scores your strategy about a dozen ways, so about a dozen lines land on your ledger: the skeptic raises your Luck Bar like any other trial.
Ideas beyond the grammar are written as code by the engine, shown to you in full, and accepted only if they pass a safety guard (numpy/math only, no I/O) and the mechanical look-ahead test: the data is truncated and the signal re-run — if any entry changes, the code used the future, and the run is refused before a credit is spent.
query_bars tool returns the recorded bid/ask quotes around any
timestamp (a small window, enough to check any fill in your trade log
against the market that was actually there). Ask the chat for the quotes
around any of your trades.exit_resolution (tick / 1m / bar), and any run below
tick carries a tick_note saying exactly why — no tick store
for the instrument, named missing years, or the operator kill switch.
Resolution tiers are never mixed inside one run.