Thales
← research journal

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

FIN-2 — capacity.py studies the ceiling and nothing studies the FLOOR: no gate says how small an account may be before the traded book stops being the validated book

status: open · raised 2026-09-10 (external financial review F3, numbers re-derived — the review's own figures were wrong, see research/2026-09-09_financial_review_verification.md) · class: execution fidelity · judgement: YES · effort ~0.5 pd · horizon: binds today at $13.7k; binds harder at the December 25% deployment


Plain-language summary. A 50-name book on a small account produces positions too small to matter. They cannot move the P&L, but they still pay a spread every time they are touched. Somewhere below a certain account size the strategy being validated and the strategy being traded stop being the same object — which is exactly the failure execution-fidelity-gap exists to prevent, arriving through a different door. The repository has a study of the capacity CEILING (backtest/capacity.py: at what AUM does flat-10bps break) and no study of the floor.

Ground truth — measured, and NOT as the review reported it

Target weights from results/weights_history.parquet (latest rebalance 2026-03-02 — note this is the frozen research snapshot, six months stale against the live book; the review multiplied it by live equity, which mixes a research artifact with a live account), at $13,740:

  • 50 names, gross 0.4728, median position $95.90
  • smallest SATS $26.67; nothing below $20
  • 24% under $50, 54% under $100

The review's figures (median $61.65, "MRNA $1.82", 26% under $20) do not reproduce, and its SNDK example read a portfolio weight as a share count. Its conclusion that "roughly a quarter of the book is pure cost" is not supported. What is real: a ~$96 median position against a ~47.3 bps TCA median is a meaningful cost fraction, and the live book carries 67 positions against a 50-name target.

Fix shape (propose-only)

Compute the floor properly — from the LIVE book, not stale research weights — and pin it as a pre-registered gate of the form no sleeve trades a target book whose median position is below $X, with X derived from the measured spread cost at which a position's expected contribution is dominated by its round trip. Then either fund to clear it or cut the name count until it clears.

Test design + negative control

A fixture book whose median position sits below the floor must fail the gate; one just above must pass. Negative control: widen the floor to zero and assert the failing fixture stops failing.

Kill criterion (pre-registered)

If the measured floor lands below the current account equity, the gate is built anyway (it costs little and binds later, at the December deployment) but the row records that it was not binding when written — a gate that never fires is worth having only if its threshold was set before anyone looked.

Prior art

backtest/capacity.py (ceiling only); memory/execution-fidelity-gap (the class of failure); the 2026-06-09 audit's min-notional finding (the same failure from the opposite direction — positions too small to OPEN).


Triage note — 2026-09-14 (annotation only; the body above is untouched)

Do not reuse the $95.90 median position in the body — it is measured from the stale research weights (latest rebalance 2026-03-02, 50 names, gross 0.4728). Re-derived from the LIVE book as the body itself instructs, at committed state through 2026-09-11:

equity $13,717.08 x gross_leverage 0.2931 / 67 positions = ~$60 average

That is roughly 37% below the figure the row quotes, over 67 positions rather than 50 — the breadth overshoot is live and is part of the same measurement. Whoever builds this must re-derive from the live book, not from results/weights_history.parquet.

One leg is already closed and should not be re-litigated as if open. The "positions too small to OPEN" failure is guarded: execution/pipeline.py:105 min_notional = exec_cfg.get("min_order_notional", 100) with the loud tripwire at :151-157, and config/settings.yaml:252 lowers it to 10 with :266 fractional_shares: true. FIN-2's economic argument — cost fraction per name, i.e. how small an account may be before the traded book stops being the validated book — is untouched by that and is the whole remaining item.

Registration timing. The deliverable is a pre-registered gate threshold, so it has to be pinned before the December deployment it governs, not after.

DISMISSED — 2026-10-05, triage (12-cap overflow, ranked 13 of 14)

Killed because its own binding moment is AFTER the December gate, not before it, and because its build brief is not fully specifiable today. Reopens on any of the five conditions below. Body untouched; git keeps every word.

The gap is real and still open at HEAD 502690b — this is about sequencing, not validity. No floor gate of any kind exists: grep -rniE "min_equity|minimum_equity|equity_floor|median_position" over src/thales/ and config/ returns zero hits. Ceiling-only prior art stands (backtest/capacity.py:44-46); the one closed leg stays closed (execution/pipeline.py:105, tripwire :154-157).

CURRENT NUMBERS — one of the row's and one of last week's are now false. Committed momentum state at 2026-10-05: equity $13,760.49; n_positions 70, long_mkt_value 3,880.97, gross_leverage 0.282 → average position $55.44. "Binds today at $13.7k" is still true; last week's "$59.75 across 67 names" is stale (the 09-18 snapshot), and the body's $95.90 median is 7 months old. Breadth worsened 67 → 70 at the 10-02 selection against settings.yaml:52 momentum_sleeve_size: 50 — a 40% overshoot, which is what pushed the average down. (That overshoot is not this row's subject; it is flagged to the panel in this week's report.)

BUILD-BRIEF GAP. The body says to derive the floor "from the LIVE book", but no per-symbol holdings are committed — portfolio_snapshots.jsonl carries aggregates only. The mean is derivable; the median it asks for is not, without a credentialed broker pull or a new snapshot field. Approving it as written would hand the implementer an under-specified task.

WHY IT LOSES THE SLOT. Nothing is broken, no gate is wrong, no decision is wrong, no data is lost — on a paper account at 0.28 leverage, $55 positions cost nothing real. Its own horizon says it "binds harder at the December 25% deployment": the floor question goes live when capital is scheduled, which is downstream of a gate 57 days out that the pre-registered memo calls an operational-competence gate, not alpha proof. Every row kept above it is live-path safety (LAB-1, RWG-1, FOS-3), ongoing permanent data loss (PIT-1), or dated inside the December window (FIN-3, BTB-1, FRZ-1, CQA-3, FIN-4).

REOPEN CONDITIONS — any one suffices, no new argument needed:

  1. Real capital is scheduled — the December gate returns PASS, or the owner decides to deploy on any other reading. Reopen in the SAME sitting, before the first live order: go_live.initial_deployment_fraction: 0.25 of a small account is exactly the regime it studies. This is the expected path.
  2. Average position falls below $50, or breadth exceeds 75 names on the committed snapshot — the 67 → 70 drift continuing.
  3. Equity falls below ~$12,000, where a 50-name target and the $10 min_order_notional begin to interact.
  4. The build-brief gap closes — any committed source makes the median position derivable without a broker pull.
  5. The December deployment arithmetic is drafted and needs a floor number.

Not reopened by: the calendar reaching 2026-12-01. The gate maturing is not capital being deployed, and a NOT-PASS is the pre-registered modal outcome.