ZSB-1 v1.0: Zero-Skill Baseline — Methods paper

Key points

  • Compare with simulated no-edge traders – same risk, rules, costs.
  • Rank 95 does not mean 95 % skill.
  • Null baseline by tilting trading days.

What this cannot tell you: whether you have skill.

For the technically minded

ZSB-1 tells you how a trader with no edge, using your risk, your rules, your trade shape and your costs, would have fared, and where your record sits among 10,000 such traders. A good rank is consistent with skill and with luck. A bad rank is consistent with no skill and with costs. The method never outputs a "probability of skill", never certifies a track record, and never predicts your next trade.

Status: ZSB-1 v1.0, text prepared 2026-10-17, web version 2026-10-18.

For the technically minded

Draft for publication: the released version will carry a git commit, an OpenTimestamps proof and a Zenodo DOI (one DOI per method version). Reference implementation in Python ( stats_core , propsim ) and the test-vector generator are published with the release; test vectors sha256 ef0ff76cf29f0f069e078651766d97865edf7f3a3de609a97e631aad9f552b3f . German one-page summary: see the German version of this page. Marking: (B) backed by simulation or proof in the reference scripts, (A) assumption, (U) unclear, (L) literature cited from knowledge; every DOI was resolved on 2026-10-17.

Summary in five lines

  1. The null model keeps your realized R-values and your day structure. Trading days are reweighted by exponential tilting so that a no-edge trader has gross expectation exactly zero; 10,000 simulated no-edge traders are drawn as whole days; costs are subtracted per trade. (§2)
  2. Three closed-form pass rates let anyone check the simulator by head: static floor D/(D+A), intraday trailing e^(−A/D), trailing with lock e^(−1)·D/A. (§3)
  3. The rank is a percentile among simulated no-edge traders with a Phipson–Smyth p-value and a Monte-Carlo standard error. It is not the probability of skill. (§4)
  4. The edge band is a Normal–Normal posterior with a skeptical prior τ₀ = 0.25 R, shown next to a whole-day block bootstrap band; verdicts are given exclusively at O'Brien–Fleming checkpoints (K = 10). (§5)
  5. Every published number carries engine version, seed and golden-test ID; the test vectors in Appendix A must be reproduced to 1e−6 before a number is shown. (§10)

1. What we measure and what we do not

Input. A trade list with, per trade: gross result in R (result divided by the risk defined at entry), the trading day of the exit (exchange time), cost per trade in R (commission + exchange fee + slippage, as entered by the user; a mandatory field), and, if available, MAE and MFE in R.

For the technically minded

Optional: the evaluation rules of a prop-style account (target A, max-loss D, trailing type, daily loss limit, lock, trades per day).

Output (TapeResult v1).

Not measured, by design. No probability of skill.

For the technically minded

No forecast of future returns beyond a stamped predictive band that we later score against the zero-skill forecast (§9). No ranking of users against each other. No firm-specific presets; rules are generic templates the user edits. No claim that any firm's published pass rate is comparable to the chance-pass rate unless rules, costs and trade shape are matched.

Why "Zero-Skill Baseline". The term places the method in the tradition of skill scores in forecast verification, where a reference forecast with zero skill defines the zero point of the score (Murphy 1988; Wilks 2011).

For the technically minded

We do not claim to have coined the term; it is used in that sense across the verification literature.

2. Definition of the zero-skill trader (tilting with day structure, costs)

Definition. A zero-skill trader trades with the same risk per trade, the same payoff shape (stop, target, time exit, realized R-values), the same day structure (which trades fall on the same day) and the same costs as the user.

For the technically minded

The gross expectation per trade is exactly 0. Net, the expectation is −cost.

Why not simpler nulls. Mean-shifting (R − R̄) creates impossible values (−1.04 R behind a 1 R stop).

For the technically minded

Sign permutation (±|R|) creates −2 R losses behind a 1 R stop. Random entries and exits on market data contain the market drift (a random long in a bull market is not zero skill) and require price data. Exponential tilting keeps the realized values and changes just their probabilities so that the expectation is zero; it is the distribution closest to the empirical one in Kullback–Leibler divergence under that constraint (Efron 1981; Davison & Hinkley 1997, ch. 4). For a 1:k bracket on {−1, +k} the tilted probability of +k is exactly 1/(1 + k), which is the martingale (optional stopping) solution; for 1:2 this is 1/3 (B, test vector TV-2).

Day structure (ZSB-1 day-tilt v1, mandatory for the rank). Trades on the same day are dependent.

For the technically minded

iid tilting per trade holds the 1 % level under day regimes at about 1.3–1.4 % (B, G2). The null is therefore tilted on day sums and drawn as whole days :

  1. Day sums S_d = Σ_{i∈d} r_gross,i, trades per day n_d, D days.
  2. Support check: if min S_d ≥ 0 or max S_d ≤ 0 the rank is not computable (rank: null, reason: "all days same sign"). If D < 10, fall back to iid tilting per trade with the warning "day structure not usable (< 10 days)".
  3. Find t with Σ_d exp(t·(S_d − S̄))·S_d = 0 (bisection on [−60, 60], tolerance 1e−12; shift exponents by their maximum before exp to avoid overflow). Weights w_d = exp(t·(S_d − S̄)) / Σ exp(·). Then Σ w_d S_d = 0.
  4. Effective number of days ESS = 1/Σ w_d². If ESS < 20: compute the rank but flag "rank uncertain (ESS < 20)".
  5. Draw N = 10,000 simulated no-edge traders: each is D days drawn with replacement with probabilities w_d. Gross sum = Σ S of the drawn days, trade count = Σ n of the drawn days, net = gross − c · trade count.
  6. Observation obs = Σ r_gross − c·n.
  7. p = (1 + #{net_draw ≥ obs}) / (N + 1) (Phipson & Smyth 2010); rank = share of draws with net < obs; MC-SE = √(p(1 − p)/N).
  8. "Best of N_var" (§6): per draw, the maximum over N_var independent day-draws with the same weights.
  9. Output fields: null.method = "ZSB-1 day-tilt v1", null.t, null.ess_days, null.days, null.n, null.N, null.seed, null.flags[].

Why day weights and not trade weights with days as blocks: when whole days are drawn, the day's draw probability determines the expectation of the sum; E[Σ] = Σ_d P(d)·S_d is zero exactly when P(d) equals the day-tilting weights. The trade count per draw varies around n; this is intended (a simulated no-edge trader with your day structure does not trade exactly n times). The statistic is the net sum, not the mean per trade.

Costs. Commission, exchange fee and slippage enter as $ per contract × contracts / $ per R, and are subtracted after tilting, per trade.

For the technically minded

Evaluation fees and resets are handled separately in "cost until initial payout" (§7).

Exactness. For pure stop/target brackets the tilted null is exact.

For the technically minded

For time exits, trailing exits and partial exits it is an approximation: the KL-closest zero-mean distribution on the observed support. Under a null with a heavy right tail the 1 % level is held at 1.1–1.2 % with day tilting (B, G2). This is stated on the methods page.

3. Three formulas you can check by head

Continuous Brownian limit, no costs, no time limit, risk measured in R, target A above the start, max-loss D below the start (B; Taylor 1975; Lehoczky 1977):

RulePass probability of a zero-skill traderExample A = 12 R, D = 8 R (3,000 $ / 2,000 $ at 250 $ per R)
Static floorD/(D + A)8/20 = 0.40
Pure intraday trailing floor (floor = running high − D)e^(−A/D)e^(−1.5) = 0.2231
Trailing floor locked at the starting balancee^(−1)·D/A0.3679·8/12 = 0.2453

The static formula does not depend on the strategy at all (gambler's ruin with fair steps; Feller 1968, ch. XIV). Head check used on the card: 2,000/5,000 = 40 %, e^(−1.5) = 22 %. Discrete references for the simulator (step 0.1 R, 1:1 bracket): (80/81)^120 = 0.2252 for trailing, (80/81)^80·8/12 = 0.2468 with lock, expected trades to absorption k(N − k) = 96 (test vector TV-4).

Two consequences that users find surprising and that follow from these formulas (B): a soft daily loss limit is a stopping time and leaves the martingale property intact, so it changes the chance-pass rate by almost nothing (27.5 → 27.4 %) and just costs time; larger risk per trade raises the chance-pass rate because fewer trades mean fewer costs (5.6 % at 125 $ to 29.3 % at 1,000 $ per trade in the base case). The second is shown as a warning, never as advice.

4. Rank and p-value, with the misreading box

Construction. 10,000 day-tilted draws with the same costs and day structure (§2).

For the technically minded

Statistic: net sum in R. Output: "better than 9,412 of 10,000 simulated no-edge traders". p by Phipson–Smyth, never 0; MC-SE printed (at p = 5 %: ± 0.22 percentage points).

Rank versus p. Numerically rank ≈ 1 − p. The rank says where you stand among simulated no-edge traders. It says nothing about the share of skilled traders in the population, so it cannot be a probability of skill.

Misreading box (mandatory text on every rank card). "Rank 95 means: 5 of 100 simulated no-edge traders with your risk, your rules and your costs did as well as you.

For the technically minded

It does not mean you have skill with 95 % probability." Illustration (B, simulation with edge 0.10 R, 1:2 bracket): at n = 50 the test has 11 % power; if 5 % of traders are skilled, P(skill | rank ≥ 95) ≈ 11 %; if 20 % are skilled, ≈ 36 %. At n = 300: power 34 %, posteriors 26 % and 63 %. The number on the card is the rank, not these posteriors.

A second mandatory line on the card: "simulated no-edge traders with your day structure, D trading days, ESS e". With ESS < 20: "rank uncertain".

5. Band, countdown and sequential checkpoints

5.1 Bootstrap band (whole days)

Trades are resampled as whole trading days (dependence within the day), then a stationary bootstrap over days (Politis & Romano 1994) with the automatic block length of Politis & White (2004) and the correction of Patton, Politis & White (2009). Our implementation matches the arch package on AR(1) test series: b = 1.58 / 5.52 / 10.45 for φ = 0 / 0.3 / 0.6 (B). Because losses are bounded by the stop and gains are not, BCa or bootstrap-t intervals are used, and a kurtosis above 10 triggers "band uncertain".

5.2 Shrunken band (main display), τ₀ = 0.25 R

Normal–Normal posterior: prior μ ~ N(0, τ²), likelihood x̄ ~ N(μ, σ̂²·VIF/n) with the user's own σ̂ (never a fixed 1.4). VIF inflates the variance of the sample mean when trades are autocorrelated. Data weight n/(n + σ̂²·VIF/τ²) is printed on the card.

VIF estimator (reproducible). Let r₁,…,r_n be the user's trade R-multiples in time order, x̄ = (1/n)Σ_{t=1}^n r_t, and x_t = r_t − x̄.

Sample scale (reference implementation vifFromAcf in tape-core):

σ̂² = (1/n) Σ_{t=1}^n x_t²

Autocovariance at lag k (k = 1,…,L):

γ̂k = (1/n) Σ{t=1}^{n−k} x_t x_{t+k}

Autocorrelation:

ρ̂_k = γ̂_k / σ̂² (if σ̂² > 0; else VIF = 1)

Lag length (automatic rule as coded):

L = ⌊4 (n/100)^{2/9}⌋

Bartlett weights (Bartlett 1946; same kernel as Newey–West HAC):

w_k = 1 − k/(L + 1)

Variance inflation factor for the sample mean:

VIF = max(1, 1 + 2 Σ_{k=1}^L w_k ρ̂_k)

This is the Bartlett-kernel long-run variance inflation applied to x̄ (Newey & West 1987, Equation (3) with weights w_k; lag L as above). The shrunken band uses Var(x̄) = σ̂²·VIF/n in the Normal–Normal update; third parties can recompute VIF from the published r-sequence and check the posterior against the card.

Why 0.25 R and not 0.08–0.10 R (B, G3, 4,000 users per cell, seed 707): a 90 % band with τ = 0.08 covers the true edge in 90 % of users when the true spread is of user edges is 0.08 R; at a spread of 0.15 R it covers 68–73 %, at 0.25 R, 47–53 %. τ₀ = 0.25 R is the smallest value that holds ≥ 89 % at all three spreads (89–93 %) and is still 7–10 % narrower than the unshrunken-data band and pulled toward zero. From ≥ 200 stamped users with n ≥ 100 the prior is estimated by the method of moments (τ̂² = max(Var(x̄_i) − mean(se_i²), 0.10²), cap 0.40 R), monthly, each change a new method version; the estimator recovers 0.076 / 0.144 / 0.245 at true 0.08 / 0.15 / 0.25 (B). The estimation window and the calibration window must be disjoint stamps.

Card text: "Estimate with skeptical prior (τ = 0.25 R). Your trades carry w % of the estimate, the prior 100 − w %. Without prior: lo to hi R." The posterior mass above zero is shown nowhere on cards (it reads as a probability of skill); it may appear on the methods page as "posterior mass above 0 under prior τ".

5.3 Countdown

Preliminary. The values in this section use α = 5 % one-sided.

For the technically minded

The countdown in the calculator already uses the strict rule (α 0.005 two-sided); the O'Brien–Fleming checkpoint constants by simulation follow, so the checkpoints here are still preliminary. n_fixed = ⌈((z_{1−α} + z_{1−β})·σ̂/δ)²·VIF⌉ with α = 5 % one-sided, power 80 %, and δ the smallest relevant edge chosen by the user (default 0.10 R). The observed mean never enters δ (that would be circular). Looking at a naive 95 % band every 10 trades up to n = 600 gives 33 % false alarms instead of 5 % (B). We therefore use group-sequential checkpoints after O'Brien & Fleming (1979), K = 10 looks at 10 %, 20 %, …, 100 % of n_max, one-sided constant c = 1.806 (Lan & DeMets 1983 for the boundary family; Jennison & Turnbull 2000). The inflation n_max/n_fixed is 1.056 (B). Anytime-valid confidence sequences (Robbins 1970; Howard et al. 2021) would need 2.3–2.6 times more trades and are not the default. σ̂ and n_max are frozen when the user stamps; checkpoints do not move afterwards.

σ̂ per tradeδ = 0.10 R0.15 R0.20 R0.30 R
1.0 (1:1)65429116473
1.4 (1:2)1,280570320143
2.0 (time exits)2,6131,162654291

(n_max after OBF inflation, test vector TV-6.) Test vector TV-6 still uses α 5 % one-sided; the numbers on the method page use α 0.005 two-sided. The card always uses the user's σ̂; the 1:2 row is the illustrative example.

5.4 Verdict levels (fixed thresholds)

Basis: p_N = Phipson–Smyth p after "best of N" (§6), computed with day tilting, evaluated exclusively at checkpoints k with boundary α_k = 1 − Φ(c·√(K/k)):

k12345678910
α_k (one-sided, %)0.00000.00270.04880.2150.5320.9861.5442.1732.8483.546
LevelNameCondition (all)Display
0Not yet decidablen < 30 or ESS < 20 or no checkpoint reached or rank not computablechance-pass rate and detection limit always; rank as "preview"
1Looks like chancecheckpoint reached, n ≥ 30, ESS ≥ 20, p_N ≥ 0.10rank, mandatory sentence
2A signal is emergingcheckpoint reached, p_N < 0.10 and p_N ≥ α_krank, "no verdict yet, next checkpoint at n = …"
3Hard to explain by luck alonecheckpoint reached, p_N < α_k, n ≥ 150, ESS ≥ 30, costs enteredrank + "Chance alone rarely explains this (p < α_k). This is not proof of skill."

Levels are held until the next checkpoint; between checkpoints the band is labelled "preview, no verdict". Level 3 is practically unreachable before checkpoint 5; that is intended. Levels can go down. After checkpoint 10: "test complete: level x at n_max". Below rank 10 an extra line appears: "Worse than 9 of 10 simulated no-edge traders with your risk. Usually that is costs: c R per trade."

6. Correction for the number of variants tried

Mandatory question: "How many fundamentally different settings did you try?" Default 10; no answer is not accepted. For lay users the correction is "best of N simulated no-edge traders": the null statistic becomes the maximum of N independent day-draws. For independent variants this is exactly the Šidák correction 1 − (1 − p)^N (Šidák 1967); for positively correlated variants it is conservative. Example (test vector TV-8). Synthetic example trader, not a real account: 1:2 bracket, true net edge +0.02 R, costs 0.05 R per trade, n = 300 (seed 80130001). Observed: 103 winners, net sum −6.0 R (mean −0.02 R). Null: tilting to a gross expectation of 0 (with a 1:2 bracket the simulated no-edge trader wins exactly 1 in 3), 20,000 simulated no-edge traders per step, seeds 80130003, 80130004, 80130005, tape-core 0.2.0. Result: rank 62 at N = 1 (p = 0.382), rank 9 against the best of 5 (p = 0.908), rank 0 against the best of 20 (p = 1.000). Rank and p. The rank is the share of simulated no-edge traders whose result is strictly below yours, in per cent. p follows Phipson–Smyth: (1 + number with a result ≥ yours) / (N + 1). Ties count towards p as "at least as good as you" (conservative) and not towards the rank. Hence p = 1 − (rank/100) · N/(N + 1); at N = 20,000, p equals 1 − rank/100 to within 0.00005. With brackets, ties are common: here 931 of 20,000 simulated no-edge traders (4.7 %) land exactly on your result; with a mid-rank rule the figures would be rank 64 and p 0.358. Monte-Carlo error of p: 0.003. Exact check via the binomial distribution (q = 1/3): p = 0.377 at N = 1, 0.906 for the best of 5, 0.99992 for the best of 20; this is Šidák 1 − (1 − p)^N. Card text for the last case: "Of 100 groups of 20 simulated no-edge traders each, your result beats the best of the group in 0 cases" (about 0.008 % of draws). The earlier value p = 0.207 does not match rank 62 and has been replaced. Two silent assumptions are stated on the methods page: the N variants are assumed to share your R-distribution and your n.

For power users the methods page also shows the Deflated Sharpe Ratio (Bailey & López de Prado 2014; PSR from Bailey & López de Prado 2012) and the Bonferroni/Šidák haircut of Harvey & Liu (2015). Example SR per trade 0.12, n = 300, skew 0.7, kurtosis 3.5: DSR 0.985 / 0.821 / 0.573 / 0.319 for N = 1 / 5 / 20 / 100; haircut 0 % / −37 % / −86 % / fully removed (B). The probability of backtest overfitting (Bailey, Borwein, López de Prado & Zhu 2017) requires the return series of all variants and is not offered to lay users.

7. Prop path model and information levels

Simulator (propsim, reference). Each trade is a bracket (stop 1 R, target k R) whose price path inside the trade is a lattice random walk with step h = 0.1 R; drift per step encodes edge (zero for zero skill).

For the technically minded

Rules in R relative to the start: target A, max-loss D, floor type static / end-of-day trailing / intraday trailing, lock at the start balance, daily loss limit soft or hard, trades per day. Costs c are subtracted at each trade close.

Information levels. The same true path can be evaluated three ways: path (floor and high checked at every step: the truth), MAE (floor checked every step, high at trade close alone: an export with MAE but no MFE), at-close (everything at trade close).

For the technically minded

For intraday trailing in the base case (A = 12 R, D = 8 R, 1:2, 3 trades per day) at-close overstates the chance-pass rate by about a quarter (18.5 % vs 14.6 % at zero skill, 34.1 % vs 27.5 % at 0.1 R edge; B). "MAE = stop, MFE = target" is the worst case and understates by 37–38 %. Without MAE and MFE we therefore show a band [worst case; at-close] and mark the point value as optimistic. The bias depends on D in R units: smaller risk per trade (larger D/R) gives a smaller bias, larger risk a larger one; the card shows the user's own value, never the base-case number as a constant. With MAE the error is small; for end-of-day trailing and static floors an export with MAE is exact.

Cost until initial payout. Evaluation fee, activation fee, resets and the payout rule (minimum winning days, payout share, cap) are simulated as a separate stream so that "expected spend until the initial payout" can be shown without mixing it into the per-trade null.

For the technically minded

It is always shown together with the share of paths that do not pass within the period; days until the initial payout are always shown together with the share without a payout in the period.

8. Multiplier table

Versioned; each row has a golden test ("2 × MNQ × 30 points = 120 $"). Status: B = checked against the exchange specification page, A = from own knowledge, not yet checked against the exchange definition records.

Contract$ per pointTickTick valueStatus
ES / MES50 / 50.2512.50 / 1.25 $A / B
NQ / MNQ20 / 20.255.00 / 0.50 $B / B
RTY / M2K50 / 50.105.00 / 0.50 $A
YM / MYM5 / 0.5015.00 / 0.50 $A
CL / MCL1,000 / 1000.0110.00 / 1.00 $B (1/10 ratio) / A (tick)
GC / MGC100 / 100.1010.00 / 1.00 $B (1/10 ratio) / A (unit)

9. Calibration: preregistration, coverage, PIT, CRPSS, selection

Stamp. On request the user receives a stamp: hash of the R-stream, method version, seed, τ, the predictive 80 % band for the next 20 (or 50) trades (posterior of the edge plus the spread of the next trades), time stamp; no e-mail, no IP.

For the technically minded

The operator's own stream is pre-registered (method ZSB-1 v1.0); its timestamps are published at launch.

Why not a plug-in band. An 80 % band for the next 50 trades built by naive bootstrap of the history covers just 58 % at n = 30 and 72 % at n = 100 (B); a predictive band with parameter uncertainty covers 81–85 %; the Bayes-shrunken band is the main display.

For the technically minded

At n = 30 the history-based forecast is worse than the zero-skill forecast (CRPSS −0.60 under the scenario edge ~ N(0.03, 0.08²), A). This number depends on the assumed spread of user edges and is marked B(A).

Scoring. Coverage with the Kupiec proportion-of-failures test (Kupiec 1995) and the independence test of Christoffersen (1998), on non-overlapping windows alone.

For the technically minded

PIT histogram in quintiles. CRPS (Gneiting & Raftery 2007) as a skill score against the zero-skill forecast (CRPSS). To detect a drop from 80 % to 70 % coverage with 80 % power needs 137 independent windows; to 75 % needs 528 (B, TV-10 example: 228 of 300 in band gives Kupiec p = 0.09). The calibration page therefore reads from day one: "So far the operator stream alone (pre-registered). User calibration from 137 independent windows, full from 300. Status: n windows."

Selection at re-measurement. If return depends solely on features recorded at stamping (MAR), inverse-probability weighting (Horvitz & Thompson 1952) corrects the estimate (naive 0.767, IPW 0.798, truth 0.799, B).

For the technically minded

If losers report less (MNAR), IPW makes it worse (0.872). Consequences: reward independent of the result, automatic sources, and Manski bounds (Manski 2003) printed next to the IPW value; with a return rate of just under one third (example from calibration run calibration.py , run ID calibration_mar_seed11 , MAR case, seed 11; not the multi-account figure) those bounds are [0.24, 0.93] and automatic return alone (> 90 %) makes them informative.

10. Validation (test suite, golden tests, seeds)

T1–T5 (propsim against closed forms, all passed, B). T1 gambler's ruin p = ½: 0.3997 vs 0.4000.

For the technically minded

T2 p = 0.55: 0.8164 vs 0.8139. T3 discrete Lehoczky (80/81)^120: 0.2260 vs 0.2252. T4 with lock: 0.2461 vs 0.2468. T5 expected duration: 96.3 vs 96. Independent re-run with seeds 101–105, z between −1.6 and +1.0.

G1 (deterministic, TV-1). 12 days, 30 trades; day weights, Σ w_d S_d, ESS and obs must match Appendix A to 1e−6 / 1e−9 / 1e−3.

G2 (statistical). Four zero-mean generators (iid bracket; day regime p_d = 1/3 ± 0.12; heavy right tail; both), n = 150 and 300, M ≥ 2,500 synthetic traders, N ≥ 2,000 draws, seed 606: target: share of p < 0.01 ≤ 1.2 % and share of p < 0.05 ≤ 6.0 % in every cell.

For the technically minded

Result with day tilting: 0.92–1.24 % (one cell is slightly above the target; Monte-Carlo error per cell ≈ 0.2 percentage points). Per-trade tilting gave 1.28–1.36 % in the day-regime cells. The difference is small and not certain at 2,500 runs per cell.

G3 (coverage). G3a: τ = 0.25, true spread ∈ {0.15, 0.25}, n ∈ {150, 300}, 4,000 users, seed 707: coverage ≥ 0.86 per cell (reference 0.89–0.91).

For the technically minded

G3b (regression): τ = 0.08, spread 0.25, n = 150: coverage ≤ 0.55 must fail the "90 %" claim (reference 0.47). G3c: τ̂ within ± 0.03 of 0.15 (reference 0.144).

Common random numbers. Sensitivity tables use one seed for all rules so that differences are not Monte-Carlo noise.

For the technically minded

Golden files: 20 frozen R-streams; CI fails on any deviation above 3 SE. Planned: every card shows engine version, seed and golden-test ID, and a "check us" button reproduces the result in the browser from seed and version. Today, code version and seed are on the proof receipt; recomputation is possible once the engine is published open source.

11. Known limits

  1. The tilted null is exact for brackets and approximate for time, trailing and partial exits (§2). A driftless path reconstruction from MAE/MFE would be the better null for time exits; this is research, not v1.0.
  2. The chance-pass rate has two error sources: MC error (± 0.3 pp at 20,000 paths) and the estimate of the user's R-distribution from 50–150 trades (larger). It is shown as a range.
  3. τ₀ = 0.25 R is a published assumption until the empirical-Bayes estimate exists; the data weight is printed so that the reader sees how much is prior.
  4. The CRPSS and the coverage numbers of §9 were simulated under one scenario of user edges; they are illustrations, not estimates of the user population.
  5. Rank correction assumes the variants share your R-distribution and n (§6).
  6. The multiplier table has rows marked A until read back against exchange definitions (§8).
  7. The Politis–White block length was validated against arch in one environment alone (B); the sorted-form CRPS and the Kupiec LR were cross-checked analytically.
  8. Nothing here is a statement about any particular firm's rules or pass rates; the chance-pass rate is rule-specific and is recomputed for the rules the user enters.

12. Change log

VersionDateChange
0.12026-10-16Initial calibration draft: iid tilting per trade, τ = 0.10 R, propsim/stats_core reference.
1.0 (this paper, draft)2026-10-17Day tilting on day sums with whole-day draws and ESS flag (G1, G2); τ₀ = 0.25 R with coverage tests (G3); fixed verdict levels at O'Brien–Fleming checkpoints; card texts replaced (new term "simulated no-edge traders with your risk"; "detectable with 80 % probability" instead of "visible from"); at-close bias shown per user, not as a constant; posterior mass above zero removed from cards.

Each method version receives its own DOI; results always cite the version that produced them.

13. How to cite

Pelzer, M. (2026). ZSB-1: Zero-Skill Baseline for evaluating trading records under prop-style rules, Version 1.0. Methods paper, Irrational Tape. Zenodo DOI: [to be assigned on release]. Git commit and OpenTimestamps proof: [to be inserted at publication]. Test vectors sha256 ef0ff76cf29f0f069e078651766d97865edf7f3a3de609a97e631aad9f552b3f.


Appendix A — Test vectors (JSON, excerpt; full file published with the release)

{
 "method": "ZSB-1", "version": "1.0",
 "TV1_day_tilting": {
  "days": [[2,-1,-1],[-1,-1],[2,2,-1],[-1],[-1,-1,2],[2,-1,-1,-1],[-1,2],[-1,-1,-1],[2,-1],[-1,-1,2,-1],[2],[-1,-1]],
  "n": 30, "D": 12, "cost_R": 0.05,
  "day_sums_S": [0,-2,3,-1,0,-1,1,-3,1,-1,2,-2],
  "trades_per_day": [3,2,3,1,3,4,2,3,2,4,1,2],
  "t_day": 0.0860142857,
  "w_day": [0.084239,0.070926,0.109039,0.077296,0.084239,0.077296,0.091806,0.065080,0.091806,0.077296,0.100052,0.070926],
  "check_sum_wS_abs_max": 1e-9, "ESS_days": 11.7413, "obs_net_R": -4.5,
  "iid_reference": {"w_plus2": 0.037037, "w_minus1": 0.031746, "sum_w_plus2": 0.333333, "ESS_iid": 29.8421},
  "tolerances": {"w_day": 1e-6, "ESS_days": 1e-3}
 },
 "TV2_bracket_martingale": {"values": {"+2R": 9, "-1R": 21}, "sum_w_plus2": 0.333333},
 "TV3_closed_form": {"D_R": 8, "A_R": 12, "static": 0.4, "intraday_trailing": 0.22313, "trailing_lock": 0.245253},
 "TV4_discrete_references": {"T1": 0.4, "T2": 0.8139, "T3": 0.2252, "T4": 0.2468, "T5": 96, "QUANT_k4_N10_p055": 0.638},
 "TV5_OBF_boundaries": {"K": 10, "c": 1.806, "alpha_k_percent": [0.0000,0.0027,0.0488,0.2148,0.5324,0.9863,1.5441,2.1735,2.8475,3.5459], "inflation": 1.056},
 "TV6_countdown": {"alpha": "0.05 one-sided (preliminary, see §5.3)", "MDE_factor": 2.4865,
  "n_max_OBF_K10": {"sigma1.0": [654,291,164,73], "sigma1.4": [1280,570,320,143], "sigma2.0": [2613,1162,654,291]},
  "delta_R": [0.10,0.15,0.20,0.30], "MDE_n87_sigma1.4": 0.373, "MDE_n150_sigma1.4": 0.284,
  "MDE_factor_strict": 3.6486, "MDE_n87_sigma1.4_strict": 0.548, "MDE_n150_sigma1.4_strict": 0.417},
 "TV7_bayes_band": {
  "A_n120_xbar-0.05_s2_2_tau0.10": {"post_mean": -0.01875, "lo": -0.148787, "hi": 0.111287, "data_weight": 0.375},
  "B_n120_xbar-0.05_s2_2_tau0.25": {"post_mean": -0.039474, "lo": -0.228151, "hi": 0.149204, "data_weight": 0.789474},
  "C_n150_xbar0.10_s2_2_tau0.25": {"post_mean": 0.082418, "lo": -0.090010, "hi": 0.254845, "data_weight": 0.824176},
  "D_n300_xbar0.10_s2_2_tau0.25_vif1.3": {"post_mean": 0.087822, "lo": -0.055679, "hi": 0.231323, "data_weight": 0.878220}},
 "TV8_best_of_N": {"seed_trader": 80130001, "seeds_null": [80130003, 80130004, 80130005], "N_null": 20000, "obs_sum_R": -6.0, "p_single": 0.3816, "p_bestof_5": 0.9080, "p_bestof_20": 1.0, "rank_single_pct": 61.845, "rank_bestof_5_pct": 9.205, "rank_bestof_20_pct": 0.0, "ties_single": 931, "engine": "tape-core/0.2.0"},
 "TV9_phipson_smyth": {"N": 10000, "count_ge": 412, "p": 0.041296, "mc_se": 0.00199},
 "TV10_kupiec": {"228_of_300_at_80pct": 0.0906, "210_of_300_at_80pct": 0.0000}
}

Acceptance: TV-1 weights to 1e−6, Σ w_d S_d below 1e−9 in absolute value, ESS to 1e−3; TV-2 to 1e−6; TV-3/TV-4 to 1e−4; TV-5 to 1e−4 percentage points; TV-6 exact integers; TV-7 to 1e−5; TV-8/9/10 to 1e−4. The generator script uses numpy alone (bisection instead of brentq) so that the vectors can be reproduced without scipy.

Appendix B — Seed list

PurposeSeed(s)Script
Golden tests T1–T5 (propsim)11, 12, 13, 14, 15golden_tests.py
Sensitivity tables (common random numbers)7sensitivity.py, sensitivity_dll.py
Null variants, rank, band, peeking, multiple testing42 (demo), 1 / 1000+s (nulls), 9 (example stream)demo_stats.py
Calibration coverage / PIT / CRPSS11calibration.py
Bayes-shrunken forecast band12calibration_shrink.py
O'Brien–Fleming constants and inflation3countdown.py
Worst-case information levels5worstcase.py
Day tilting type-I error (G2)606sim6.py
Bayes coverage (G3a/b)707sim6.py
τ estimator (G3c)808sim6.py
Reference simulator82internal test vectors
Default rank drawsN = 10,000, seed printed on the cardstats_core.rank_vs_null

Appendix C — Literature (DOI resolved on 2026-10-17; bibliographic match from own knowledge)

Check with your own trades: beta from 23 Oct → waiting list · input format → data formats

Back to top