# Prime Academy audit: correctness and robustness (candidate 1, 2026-10-01)

Scope: the deployed decision of notebook sections 23 to 26 (branch `prime-academy`, worktree
`/root/Projects/trading-framework-prime`), the paper `paper/prime_academy.tex` and its JSON
inputs, `modules/{prime,spread,hedge,fxcontrol,broker_map}.py`, and the strategy and live runner.
Every script cited is in `scripts/`, every log in this folder. Monte Carlo runs are 5,000 paths
each, run one process at a time. Where several seeds are pooled, the runs were sequential.

Labels: **verified** means reproduced by running code or read directly in code. **Measured** means
computed here on the repository's own EURUSD bars. **Inferred** means reasoned from verified pieces
and not run end to end.

## Summary in five lines

1. The live runner does not trade the deployed decision. It risks 4% in both stages and never turns
   the account rules on, so neither clip and no funded risk exists live.
2. The purchase gate's "open at +0.013" is not resolved. Pooled over 30,000 paths it reads
   +0.008 ± 0.003, and costing the variable stop correctly (Jensen) moves it to −0.002 ± 0.003.
3. The strict reading of the 5% daily rule, priced here for the first time, costs the assessment
   1.5 to 2.0 points at 3%, closes the gate (−0.031 to −0.044) and moves the best assessment risk to 2.75%.
4. The 40-pip "richest" front pick wins on cash rate by $2.8 a year, and that margin reverses under
   the β knob's real cost share. Through the floor knob it holds, and there the gate is open with room.
5. The chains, the wallet and the published numbers reproduce exactly. The errors are in the inputs,
   the gate statistic, the paper's duration table, the tooth formula and the live wiring.

---

## Findings, ranked by severity

### F1. The live runner trades none of the deployed decision's account settings (critical, verified)

- **Claim.** `run_live.py` risks 4% in both stages, never calls `set_account`, and cannot express a
  funded risk. If the entry window is opened as wired, the account trades a configuration the paper
  prices as worse than the plan.
- **Evidence.**
  - `src/strategies/prime_academy_strategy/run_live.py:74`: `FROZEN = dict(risk=0.04, ...)`.
    `build()` at lines 273-277 constructs the strategy and returns. `grep set_account run_live.py`
    finds nothing.
  - So `acct_on` stays False (`prime_academy_strategy.py:242`). The target clip, the floor clip
    and the stage risk switch (`prime_academy_strategy.py:713-740`) never run. The paper's plan
    table (tex lines 39-53) lists all three as deployed.
  - There is no CLI flag for risk, so moving to 1.575% after a pass needs a code edit.
  - Deployed environment (read only): `PRIME_SIDE=coin`, `PRIME_WINDOW=0-0`, `PRIME_BANKROLL=3000`.
    The window is shut, so nothing trades today.
  - What 4% would cost (verified, `log_s06b.txt`). Pass rate is 45.6% under the lenient daily
    reading and 39.7% under the strict one. A 4% win is also 44% of the target, which breaks the
    35% rule if the firm applies it in the assessment.
- **Changes a decision?** Yes, the deployment. The code that would trade is not the decision the
  paper gates.
- **Fix.**
  - Call `set_account(STAGE_EVAL or STAGE_FUNDED, 0.03, 0.01575, 0.09, 0.09, 2*commission_per_side, bankroll, ...)`
    from the runner, with a `--stage` flag.
  - Gate it in `acceptance.py`. Today the gate runs at $50,000 and 4% with the account rules off
    (`acceptance.py:53`), so the clipped legs are never parity-checked.
  - Fix the stale README ("4% ... fixed side") and `deploy/prime-academy.env.example` (`PRIME_SIDE=short`).

### F2. The purchase gate's sign is unresolved, and the Jensen cost correction alone erases it (high, verified)

- **Claim.** The gate reads "open at +0.013". That number is about 2 Monte Carlo standard errors
  from zero, and +0.006 on independent paths. The correct cost of the variable stop removes it.
- **Evidence.**
  - `scripts/s02_gate.py` and `s07_gate_inputs.py` (logs `log_s02.txt`, `log_s07.txt`), at the
    deployed cell: 3% assessment, 1.575% funded, cost share 3.353%, 7-trade clock, 541 trades,
    80% honoured.
  - Seeds 5 to 8 at 5,000 paths each read +0.0037, +0.0094, +0.0004 and +0.0093. Pooled:
    +0.0057 ± 0.0041 over 20,000 paths.
  - The published +0.013 is 10,000 paths at seed 5 (notebook cell 108). Its per-path SE is about
    0.0058 (`front.json` gives `log_se` 0.0058 for the same cell). Inverse-variance pooling with
    mine gives +0.008 ± 0.0034.
  - Jensen-correct cost share (F3), on common random numbers: the paired shift is
    −0.0099 ± 0.0009. The level becomes −0.0042 ± 0.0041 on my paths and −0.002 ± 0.003 pooled.
  - Mean final wallet $1,126 to $1,134 after three years, against the $1,196 the cash rate G
    implies. The log statistic leaves the open account and the last fee unvalued at the horizon.
- **Changes a decision?** Yes, the purchase gate. By the declared rule it is open on the published
  run and at zero after the correction. Its sign is a coin flip at this path count.
- **The gate statistic is a choice that decides the verdict.** The section 26 formulation
  (`fable_formulation.md` lines 89 and 312) argued for the arithmetic cash rate G > 0 as the gate.
  It said log growth needs the open account valued and a horizon of at least three years. The
  notebook kept log growth at three years without the valuation. On G the gate is open with
  margin: G is $65 a year and stays positive until the cost share reaches 5.55% (`log_s08.txt`) or
  the honour rate falls below 62%.
- **Fix.**
  - Pick the gate statistic on purpose and write down why.
  - If it stays log growth, value the open account at the horizon, report the per-path SE, and
    require the gate to clear 2 SE.
  - Compute the cost share as c·E[1/ℓ] (F3).

### F3. The cost share is evaluated at the mean stop, but a variable stop pays c·E[1/ℓ] (high, measured)

- **Claim.** Lots are risk over the stop, so a trade's round trip costs r·c/ℓ_t, and the mean is
  r·c·E[1/ℓ]. Sections 24 to 26 use c/E[ℓ]. That understates the deployed cost and makes the two
  40-pip knobs different cells. The paper says they are the same cell.
- **Evidence.** `scripts/s04_pair.py`, log `log_s04.txt`. Commission plus spread is 1 pip, over
  2,084 entry bars of the cohort.

  | stop rule | mean | c / E[ℓ] | c·E[1/ℓ] | gap |
  |---|---|---|---|---|
  | max(0.75σ, 30), deployed | 34.79 | 2.874% | 2.968% | +0.094% |
  | max(0.924σ, 30), 40-pip β knob | 40.00 | 2.500% | 2.662% | +0.162% |
  | max(0.75σ, 38.3), 40-pip floor knob | 40.00 | 2.500% | 2.522% | +0.022% |

  - Paper line 1459 says "the two knobs give the same cell here". They differ by 0.14% of
    cost share and by $4 a year of G (F5).
  - At the deployed stop the +0.094% is about the whole gate margin (F2).
- **Changes a decision?** Yes. It moves the gate (F2), the front comparison (F5) and the choice of knob.
- **Fix.** Average cost26 over the stop distribution of each rule. The swap and step-aside terms
  are linear in ℓ, so their means need no change.

### F4. The strict daily-loss reading closes the gate and moves the assessment risk (high, measured and inferred)

- **Claim.** The paper names the strict reading (open profit at the day boundary counts) as a
  pending measurement and prices nothing. It fires on about 1 in 70 losing trades at 3%. That costs
  1.5 to 2 points of pass rate and closes the gate. Under it, 2.75% beats 3%.
- **Evidence.**
  - `scripts/s04_pair.py` scores every entry day of the cohort on both sides: the 07:00 UTC bar,
    the deployed stop, RR 1, held until a leg fills. That is 1,390 trades, 695 of them stops.
  - Breach rule: a stop-out after a day boundary at which the trade's float exceeded
    (5% − r(1+γ))/r of the stop. That threshold is 0.633 at 3%, the paper's own `\decstrictthree`.
  - Breaches per losing trade at 3%: 1.15% with a 00:00 UTC day and 1.58% with a 21:00 UTC day.
    That is 8 and 11 events, so treat it as a rate known to about ±40%.
  - `scripts/s06_strict_daily.py` (`log_s06.txt`, `log_s06b.txt`) puts that rate into
    `eval_chain`. A losing assessment trade dies with probability q_l.

    | risk | lenient | strict, 00:00 UTC | strict, 21:00 UTC |
    |---|---|---|---|
    | 2.50% | 43.68% | 43.68% | 43.68% |
    | 2.75% | 44.19% | 43.73% | 43.96% |
    | 3.00% | 44.61% | 43.12% | 42.59% |
    | 3.15% | 44.84% | 42.45% | 41.85% |
    | 4.00% | 45.64% | 39.76% | 39.69% |

  - Gate (`log_s07.txt`, `log_s10.txt`, 20,000 paths, paired against the deployed cell):
    - strict at 00:00 UTC: −0.031 ± 0.004
    - strict at 21:00 UTC: −0.044 ± 0.005
    - 2.75% assessment under strict: −0.011 and −0.006 ± 0.004. Closer to zero, still closed.
  - The funded stage is immune at 1.575%. A breach there needs a float of 2.17 stops, and the
    target is at 1.
- **Changes a decision?** Yes, the gate and the assessment risk.
  - Paper line 89 says a 4% assessment "passes a point more often". That holds only under the
    lenient reading. Under the strict one, 4% loses 6 points.
  - Paper line 1551, "the daily loss limit never fires at one trade", is false under the strict
    reading.
- **Fix.** Ask the firm which reading it uses and where its day ends. Until it answers, report the
  gate under both readings.

### F5. The 40-pip "richest" pick sits inside input uncertainty, and the knob matters (medium, verified)

- **Claim.** Under the random side's swap, 40 pips is "richest" over the deployed 34.8 because G is
  $68.0 against $65.2 (`tab_picks.tex`). On T, 34.8 is faster, 148 against 157 days, and D ties.
  The $2.8 margin comes from three inputs section 26 fixed.
- **Evidence.** `scripts/s05_front_G.py` (`log_s05.txt`), exact chains. Both published values
  reproduce to the dollar.

  | case | 40 pips G | 34.8 pips G |
  |---|---|---|
  | as published (40: clock 6, pace 2.282) | $68.0 | $65.2 |
  | ceiling clock 7, funded-cell pace 2.325 at 40 | $65.4 | $64.4 (funded pace 2.048) |
  | β knob with its Jensen cost, clock 7, funded pace | $60.9 | about $61.6 (Jensen, funded pace) |
  | floor knob with its Jensen cost, clock 6 | $67.5 | $62.3 (Jensen) |

  - Clock: round(14/2.28) = 6 at 40 pips. A payout needs the first trade that ends on or after
    day 14, and 6 trades average 13.7 days, so the ceiling (7) is the better conversion. At 34.8
    pips both give 7.
  - Pace: section 26 interpolates assessment cells, whose targets are clipped. Funded cells read
    1.9% slower at 40 pips (`scripts/s03b_pace_by_kind.py`).
  - Gate at 40 pips (`log_s07.txt`): +0.047 ± 0.004 as published, and +0.022 ± 0.004 with the
    β knob's Jensen cost and clock 7. Both are open, and with more room than at 34.8 pips.
- **Changes a decision?** It changes which 40 pips to deploy.
  - Through the floor knob, max(0.75σ, 38.3), 40 stays richest and its gate is robustly open.
  - Through the β knob it is not richest.
  - The code still runs 0.75σ with a 30-pip floor, which is the 34.8-pip cell (F1).
- **Fix.** Use the ceiling clock, cost each knob by c·E[1/ℓ], and choose the floor knob.

### F6. The funded pick is set by the plateau width, not by the plateau (medium, verified)

- **Claim.** The rule takes the plateau within 1 point of the best P(any), then the fewest median
  trades to the first payout. That tie-break falls monotonically with risk, so the pick is always
  the plateau's upper edge, and the width decides it.
- **Evidence.** `scripts/s01_chains.py` (`log_s01.txt`), deployed cost.

  | plateau width | pick | P(any) | cash / fee | G |
  |---|---|---|---|---|
  | 0.25 pt | 1.450% | 53.61% | 1.582 | $54.6 |
  | 0.5 pt | 1.500% | 53.45% | 1.596 | $59.0 |
  | 1 pt (published) | 1.575% | 52.88% | 1.612 | $65.2 |
  | 2 pt | 1.675% | 52.05% | 1.633 | $73.4 |
  | argmax of P(any) | 1.375% | 53.86% | 1.560 | $47.8 |

  - The published pick is 0.98 points under the best, one step inside the edge.
  - In the cost grid of `log_s02.txt` the pick alternates 1.55%, 1.575% and 1.60% for cost
    changes of ±0.05%. Paper line 237 says "the plateau and the pick move much less".
  - E[W] rises with funded risk across the whole line search (best at its 2.0% end), so the rule
    trades cash for payout frequency.
- **Changes a decision?** It moves G by up to ±$10 a year and the gate by a few thousandths.
- **Fix.** State the rule as it acts: the largest risk with P(any) ≥ best − w. Then justify w, or
  pick on G subject to P(any) ≥ 50%.

### F7. The answer table's funded durations come from a 14-trade clock, but the plan runs 7 (medium, verified)

- **Claim.** Section 1 quotes "funded: days to the first payout 46" and "account life 123 days"
  from the broker map. Every broker funded cell runs a 14-trade payout clock, about 28 days. The
  plan runs 7 trades.
- **Evidence.**
  - `modules/broker_map.py:34`: `PAY_WAIT, PAY_CAP, MIN_DAYS = 14, 0.35, 3`, passed to
    `set_account` at line 63.
  - The cached replays (`scripts/s03_broker_cache.py`, `log_s03.txt`) pay no first payout before
    trade 14, and no later payout sooner than 14 trades after the last.
  - `make_numbers.py:982-983` takes `decdaysfirstpay` and `decdayslife` from those grids.
  - On the 7-trade clock the chain's median first payout is 14 trades, about 28 days.
  - Paper line 1146 still says "with the fourteen-trade clock", while Section 1 says 7.
  - The mdgraph entry for section 25 records that the map still runs on 14 trades. The paper
    caption (line 71) does not say so.
- **Changes a decision?** Cosmetic for the gate. It misstates the time to money by about 60% in the
  table a reader stops at.
- **Fix.** Re-run the funded broker cells at `pay_wait` 7, or caption the clock and quote the
  chain's durations at 7.

### F8. The tooth formula ignores parity, and the figure's own falsifier fires (low, verified)

- **Claim.** After n trades at ratio one, the net-win count j has the parity of n. `tooth_edges`
  uses n = wait for every j, but an even j first occurs at wait + 1.
- **Evidence.** `prime.py:360-366`. The fine scan in `log_s01.txt` puts the chain's jump between
  1.276% and 1.278%. The formula gives 1.265%, and the parity-corrected n = 8 gives 1.276%. The
  same holds for j = 6 (0.826% against 0.831%) and j = 8.
  - The caption at paper line 1276 says "one off them would mean the chain and the formula
    disagree about the lattice". Here the formula is wrong and the chain is right.
- **Changes a decision?** No. The line search reads the chain, not the formula.
- **Fix.** n_j = wait + ((j − wait) mod 2). The same applies to the 14-trade teeth in the mdgraph entry.

### F9. `day_split_mc` checks the target, floor and daily rule once a day, not after each trade (low, verified)

- **Evidence.** `prime.py:741-751`. A copy with per-trade barriers (`scripts/s09_daysplit_and_remedy.py`,
  `log_s09.txt`, 5,000 paths, seeds 1 and 2):
  - k = 2, stop after a loss: 40.2% and 38.9% become 41.8% and 40.1%.
  - k = 2, stop after a win: 44.9% and 43.8% become 43.5% and 42.1%.
  - k = 3, stop after a win: 42.8% becomes 41.2%.
  - Days to pass drop by 0.6 to 1.5. k = 1 is identical.
- **Changes a decision?** No. k = 1 is still fastest (12.9 days against 15.2 or more). The
  `tab_ksplit` pass column is off by up to 1.7 points, and the k = 2 win row's lead over k = 1 is
  mostly this artifact.

### F10. `spread.family` gives every account stream the same seed (low, verified by reading)

- **Evidence.** `spread.py:75` and `:83` pass `seed` unchanged to every `stream`. When `coins` is
  supplied, `wallet_mc`'s generator draws only the honour uniforms. So every stream's honour draw on
  day d is the same number, and a refusal on day d hits every account that requests that day.
- **Changes a decision?** Only cells with N ≥ 2. Under the swap all of them are infeasible already.
- **Fix.** Pass `seed + i` per stream.

### F11. Strategy account accounting differs from the model in ways that bite only live (low, verified by reading)

- **35% rule.** `prime_academy_strategy.py:316-318` keeps the largest *net* win (`eq - open_eq`).
  The model reads it gross (`prime.py:663`).
- **Payout event.** On a PAY event the strategy moves `acct_start` to the balance
  (`prime_academy_strategy.py:344`), but nothing is withdrawn. Live, the operator's real payout
  then drops the balance back to B. The strategy reads equity as minus the payout, and its floor
  clip ends the account (`FLOOR_ROOM`, line 738) while the real account still has room. If the
  operator does not request, the strategy's floor sits above the real one. Inferred. It is latent
  because F1 means `set_account` is never called live.
- **Clips and the spread.** The clips are net of commission only (`fee = cost_per_lot * lots`,
  line 726). Live, a clipped win lands short of +9% by about the half-spread, which forces one more
  small trade, and a clipped stop sits past the floor by the same amount.
- **Gaps.** The bar broker books a gapped stop at its level (`historical_bar_broker.py:14`). On the
  cohort, 5.6% of exits come on a bar that opens past the level, at a mean of 0.175 of the stop
  (`log_s04.txt`). If the venue fills gapped stops at the gap but fills targets only at their limit,
  that adds 0.49% of risk to the cost share. That is 4 to 16 times the gate's margin (F2). If
  targets also fill at the better gap price, the effect cancels. Which one happens is the venue's
  fill policy and is unverified. At 3% risk, 2.9% of wins would also gap past the 3.15% cap.

### F12. Stale or contradicted paper text (cosmetic, verified by reading)

- Line 1548, "The target is not clipped... It is not implemented". The plan table (line 46) and
  `set_account` both clip.
- Abstract (line 29): the theory "fixes ... the risk at the daily-loss maximum". The plan risks 3%.
- Line 50: "a second [account] only after a payout is honoured". No section models that policy.
  The front holds N accounts from day 0.
- Line 136 states as fact that the 14-day clock "runs from account opening or from the previous
  request". `rules.json` lists where the clock starts as unverified. If it ran from the assessment's
  opening, the first payout could come at funding, which helps the play.

---

## What checked out (verified)

- **The deployed row reproduces exactly** on the chains (`log_s01.txt`): funded pick 1.575%,
  pass 44.6054%, any payout 52.8846%, cash/fee 1.6121, G $65.2 a year. The 40-pip cell reproduces
  G $68.0 and the 45-pip cell $65.2. The four cost shares 1.44%, 2.87%, 3.35% and 7.18% reproduce.
- **The 3-day minimum cannot bind in the assessment.** The first pass is at trade 4, because three
  clipped 3% wins land short of +9% by the cost. In the funded stage the 7-trade clock exceeds it.
- **The payout clock conversion holds.** 14 / 2.03 = 6.9, giving 7. Funded cells run 2.054 days a
  trade against 2.029 in assessment cells. The gate keeps its sign from 6 to 8 trades (+0.012,
  +0.004, +0.006) and turns negative near 9 to 10 (−0.007 at 10, −0.037 at 14).
- **The clock change caused the gate flip, not the horizon.** At 500 trades the 7-trade clock
  reads +0.001, and at 541 trades the 14-trade clock reads −0.037.
- **`eval_chain` and `funded_chain` clocks and absorption agree with `wallet_mc`.** The payout is
  allowed after the wait-th trade, a clipped loss lands exactly on the floor, and the renewal pieces
  and cycle restart are consistent. `wallet_variant.py`, a copy of the `wallet_mc` loop, reproduces
  it to five decimals.
- **`hedge.shared_wallet` agrees with `wallet_mc` at k = 1.** Purchase days are detected without
  collision (a purchase day never carries a payout) and ruin is defined the same way.
- **The 35% cap arithmetic is right** (3.15% against 3%). It costs 0.23 points of pass rate under
  the lenient reading.

---

## Robustness table: each decision against each uncertain input, and where it flips

Deployed cell: RR 1, 3% assessment, 1.575% funded, 34.8-pip mean stop, random side with swap
(cost share 3.353%), 7-trade clock, $1,000 wallet, $3,000 tier, 80% honoured, three years.

"Log gate" is E[log g] > 0 as implemented. "G gate" is the formulation's cash rate G > 0. Gate
thresholds come from the paired cost-share grid in `log_s02.txt` (slope −0.105 per point of cost
share) applied to the pooled level +0.008 (F2). The range in brackets spans the seed-5 level
(+0.004) to the published level (+0.013).

| decision | input | assumed | flips at | measured or plausible | status |
|---|---|---|---|---|---|
| purchase gate (log) | spread | 0.5 pip | 0.53 pip [0.51 to 0.54] | live quote unknown, RAW "from 0.0" | knife-edge |
| purchase gate (log) | step-asides a trade | 0 (deployed row) | 0.027 [0.01 to 0.04] | 1.33 all-currency calendar | closed at the measured rate |
| purchase gate (log) | extra pips a trade (slippage, swap) | 0 | +0.027 pip [0.01 to 0.04] | gapped stops up to +0.17 pip (F11) | knife-edge |
| purchase gate (log) | cost of the variable stop | c / E[ℓ] | any correction of +0.08% | c·E[1/ℓ] is +0.094% | flips to −0.002 ± 0.003 |
| purchase gate (log) | per-trade win rate at RR 1 | 50% | shortfall of 0.04 pt [0.015 to 0.06] | coin side is exact barring ties and gaps | knife-edge |
| purchase gate (log) | honour rate h | 80% | 79.3% [79 to 80%] | unknowable | flips at the definition |
| purchase gate (log) | a refusal ends the account | no | closed at 80% (−0.186) | Terms 23.1 and 16 allow it | opens only at h ≥ 89% |
| purchase gate (log) | payout clock | 7 trades | about 9 to 10 trades | 6.9 by pace | robust |
| purchase gate (log) | horizon | 3 years | between 2 and 3 years (2y −0.005, 3y +0.004, seed 5) | a choice | flips |
| purchase gate (log) | strict daily reading | lenient | any strict reading | 1.15 to 1.58% of losses breach | closed (−0.031 to −0.044) |
| purchase gate (G) | cost share | 3.353% | 5.55% (spread 1.26 pip, 0.76 asides, 0.76 pip a trade) | asides 1.33 gives 7.18% | open unless the full aside rate holds |
| purchase gate (G) | honour rate h | 80% | 62% | unknowable | open with margin |
| funded pick 1.575% | plateau width | 1 pt | 0.75 pt gives 1.525%, 1.5 pt gives 1.625% | a choice | moves with the width |
| funded pick 1.575% | cost share | 3.353% | ±0.05% gives 1.55% or 1.60% | spread, Jensen | unstable by one step |
| funded pick 1.575% | payout clock | 7 trades | 14 trades gives 1.60% | 7 | robust from 5 to 12 |
| assessment 3% | 35% rule in the assessment | applies | not applied gives 4% (lenient reading only) | unverified | rule reading |
| assessment 3% | strict daily reading | lenient | strict gives 2.75% (43.7 to 44.0% against 42.6 to 43.1%) | 1.15 to 1.58% of losses | flips |
| assessment 3% | the 4% alternative under strict daily | lenient | 4% gives 39.7% | measured | 4% ruled out |
| front: 40 pips richest | payout clock at 40 | 6 trades (round) | ceiling 7 with funded pace: still richer by $1.0 | the ceiling is the right conversion | narrowly holds |
| front: 40 pips richest | knob and Jensen | same cell | β knob: $60.9 against $61.6, flips | measured | flips under β |
| front: 40 pips richest | knob and Jensen | same cell | floor knob: $67.5 against $62.3, holds | measured | holds under the floor knob |
| front: 40 pips gate | Jensen, clock 7 | +0.040 published | still +0.022 ± 0.004 | measured | robust except under strict daily (inferred −0.015) |
| front: 34.8 fastest (T) | inputs above | 148 against 157 days | none tested flips it | | robust |
| one account (N = 1) | cost scenario | swap charged | N = 2 feasible only at commission and spread, 40 to 45 pips (`front.json`) | | holds while the swap is charged (not re-run here) |
| one trade a day (k = 1) | per-trade barrier check | day-end check | k = 2 still slower by 2.3 days or more | verified | robust |

---

## What would settle each open input

- **Strict daily reading and day boundary.** The firm's answer. Or a measurement on the live demo
  account's statement, which shows the day's equity snapshot. This decides F4.
- **Spread and stop slippage at the venue.** The demo soak already running can log the quote at
  each 07:00 to 10:00 entry and the fill against the trigger on any gapped stop. The gate needs
  these to 0.01 pip, which is far finer than anything assumed today.
- **Step-aside rate on a EUR plus USD calendar.** One cached replay of `news_battery.py` with a
  narrowed calendar. That is a broker run, not done here, because no cache exists.
- **The gate statistic.** A decision for the operator, not a measurement. On G the play is open
  with margin. On three-year log growth it sits at zero.
