Thales
← research journal
Jul 5, 2026raw markdown ↗

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.

Multi-Sleeve Platform — SPEC (pre-registered 2026-07-05)

**Status update (same day): P1 #10, P2a #11, P3 #12 MERGED — the platform is built; momentum tenant byte-identical throughout (Δ 0.00e+00 after every engine touch). Deviations from the plan, per the header rule: (a) all three phases landed same-day, each still individually branch+PR gated; (b) results/meanrev|vrp formally gitignored (implied by D3, made explicit after a stray git add -A swept a battery log); (c) a latent AFK-guard bash-3.2 parse bug was found and fixed while extending the guard in P1; (d) VRP mleg submits MARKET in the scaffold — the net-credit LIMIT sign convention is a pinned pre-P3-live verification, not assumed. meanrev battery verdict: RESEARCH.md banner.

POST-BUILD AUDIT (adversarial multi-agent, 2026-07-05 — RESEARCH.md banner): 23 findings confirmed, fixed same day. Deviations it forced: (e) momentum's five IDENTITY keys (name/contract/api_key_env/results_dir/processed_dir) made explicit in settings.yaml + keystone-pinned — the refactor made them behavior-controlling on the live cron but only the sleeves were pinned; (f) option safety gate strengthened to recompute worst-case loss from leg strikes (D7's "the strategy never decides what defined-risk means" was only half-true — it trusted a self-reported field) and to a strict per-kind SHAPE contract (the per-short pairing check let a net-naked structure pass); (g) the daily-loss breaker now classifies options by ACTION not side (an OPEN nets 'sell' but is risk-increasing — it was being exempted on crash days); (h) D7's fleet cross-name WARN report and per-underlying option cap DEFERRED (need cross-sleeve / broker-option position data; dead fleet key removed, go-live items pinned in vrp.yaml); (i) meanrev cpcv.purge_days 21→69 (registration under-purge; verdict robust — under-purge only flatters PBO). Original Phase-0 text follows.**

Status: Phase 0 (this document). No code exists yet. This spec is written and committed BEFORE implementation, in the same discipline as every other pre-registration in this repo: the commit timestamp is the registration. Deviations during build are allowed but must be documented here with a why.

The reframe: Thales stops being "a strategy with infrastructure" and becomes "a platform hosting sleeves," where momentum is the first tenant. The actual asset is the falsification apparatus (CPCV, pre-registration, keystone pins, parity harness, fail-closed execution) — all of it strategy-agnostic.

0. Verdict — which sleeves, and which not

SleeveDecisionWhy
meanrev (2–10d equity reversal)BUILD — research first, live only on gate passNear-zero marginal cost (same broker/data/engine/validation); the literal anti-momentum (negatively correlated by construction); the cheapest forcing function for the platform refactor itself. A REJECTED verdict still leaves the platform built — that is most of the point.
vrp (defined-risk SPY option spreads)BUILD — forward-only experimentHighest-conviction premium reachable on existing rails (Alpaca options, verified below). CANNOT pass the SHIP gate as constructed — no owned chain history, no options backtest engine — and we will not fake one. Its validation IS a pre-registered forward gate (documented posture, not a gap). Also generates the options fills/TCA exhaust the skew-timed hypothesis will eventually need, without touching that capture's own 12–18mo activation criterion.
futures trend (micro futures, IBKR)DEFER — pre-registered Phase 4, next in lineBest diversifier on the menu (crisis convexity momentum lacks), but new broker + IBKR gateway automation (headless sessions, 2FA, restarts) + roll logic + short mechanics = a Phase-2-scale project. Do it after the platform is proven on cheap sleeves.
PEAD (estimate-free SUE from owned XBRL)BACKLOG — AFK hypothesisAn equity signal, not an architecture problem. Feed to the AFK loop; promote only if it screens. NB the 3-account cap (below) means promotion requires replacing a killed sleeve or building the deferred sub-book design.

Attention is the binding constraint, not compute or capital. Hard cap: 3 live sleeves for a solo operator. One digest email, ever.

1. Verified pre-flight facts (2026-07-05)

  • Alpaca allows up to 3 simultaneous paper accounts per user profile, each with its own API keys, positions, and equity — confirmed current as of late 2025 sources. One-account-per-sleeve fits exactly, with zero headroom.
  • Multi-leg (Level 3) options are supported in PAPER via Trading API (order_class: "mleg", legs array with per-leg symbol/side/ratio/intent) and alpaca-py (our pinned 0.43.x era supports options). Defined-risk spreads are executable as single atomic orders.
  • Alpaca paper options fills are simulated (same class of caveat as equities: paper measures rule-fidelity, never execution quality). Document, don't fight.
  • To confirm at Phase-3 setup (low risk): options approval level defaults on paper accounts; per-account options buying-power mechanics.

2. Architecture decisions (D1–D14)

D1 — A sleeve is a 6-tuple. (config file, state dir, results dir, broker account, workflow, keystone set). The platform owns everything downstream of the strategy contract: sizing guardrails, execution, state, validation harnesses, monitoring, ops.

D2 — One paper account per sleeve. Attribution solved at the broker: physically separate equity curves, fills, positions; safety gates evaluate per-account equity; reconcile runs per sleeve; no netting logic anywhere. Rejected: virtual sub-books in one account (sleeve-tagged client IDs + fill-attribution ledger + virtual cash + netting when two sleeves disagree on the same name) — complexity only real money in a single account should ever force. The 3-account cap is accepted and pinned (see D14).

D3 — Momentum is grandfathered in place. Momentum keeps config/settings.yaml, data/processed/, results/ exactly as today. New sleeves get config/{sleeve}.yaml, data/state/{sleeve}/, results/{sleeve}/. Rejected: uniform migration of momentum into the new namespace — moving files that CI commits daily breaks committed-history continuity, the workflow persist list, heartbeat paths, the captures registry, backup includes, and docs-consistency tests, all for aesthetics. The platform treats paths as per-sleeve config values; momentum's happen to be the legacy ones. Deliberate, documented, not debt.

D4 — Two strategy contracts, both funneling into the one chokepoint.

  • Weight-based (exists): compute_signals(panel) → generate_weights(date)build_target_weights → orders. Momentum already implements it; meanrev slots in as a second Strategy subclass + registry entry.
  • Order-intent (new, VRP): intents(date, positions, state) → [OptionStructure] — legs, expiries, rolls don't live in weight space; forcing them there corrupts both interfaces.
  • Fleet law (unchanged): every order from every contract passes through pipeline.execute_orders and the fail-closed safety gate. No sleeve gets a bypass path, ever.

D5 — CLI: one global --sleeve option (app-level callback; default momentum) resolving config/state/results/broker-keys. Every existing command, document, and test is unchanged by default. Rejected: per-command --config flags (leaky; trivially cross-wires a backtest against the wrong state dir).

D6 — Keystones per sleeve. config_guard grows a per-sleeve registry (momentum's KEYSTONES unchanged); verify-config/selfcheck check every sleeve; test_keystones_match_real_settings iterates all sleeve configs. Each sleeve's clean-start date, cadence, purge window, and safety limits are pinned in ITS keystone set.

D7 — Safety gate: per-sleeve limits + new structural option REJECTs. Existing HALT/REJECT taxonomy transfers per account (limits scale to sleeve equity). New, enforced in the GATE (not the strategy, so a sleeve bug cannot create undefined risk): every short option leg must be paired within the same submitted structure; structure max loss (width − credit) ≤ pinned % of sleeve equity; no naked short options, period; per-underlying position cap. Plus one fleet-level WARN-only report: combined per-name exposure across sleeve accounts (momentum and meanrev both long AAPL is invisible per-account).

D8 — Halt semantics. data/processed/TRADING_HALT stays the fleet halt (every sleeve checks it — one file stops everything, commit+push semantics unchanged). New per-sleeve halt: data/state/{sleeve}/HALT, written by thales --sleeve X halt (--sleeve is the app-level global, so it precedes the subcommand — the D8-draft thales halt --sleeve X does NOT parse; corrected per the 2026-07-05 audit). Kill-switches are per-sleeve (own equity curve, own state file, own re-entry).

D9 — One workflow per sleeve. Own cron, own concurrency group, own timeout, own secrets (ALPACA_API_KEY_{SLEEVE} / ALPACA_SECRET_KEY_{SLEEVE}); failure emails arrive pre-labeled by sleeve; disjoint state paths make the push races content-free (the rebase-retry covers the rest); heartbeat-monitor.yml extends to check every sleeve's equity file. Each sleeve pins its own cadence (meanrev selects daily; VRP checks rolls daily, trades ~weekly).

D10 — One digest email, per-sleeve sections, fleet header. Three emails [BUILT 2026-07-13, PR #30: thales fleet digest + fleet-digest.yml; momentum's in-workflow digest retired] a day is how alert fatigue kills monitoring. Each sleeve gets its own oos-monitor with its own pinned forward criteria and verdict history. New thales fleet status [--json] (the audit routine's one-stop read) and, once two sleeves accrue, thales fleet correlationforward realized cross-sleeve correlation is data nobody sells, and it is exactly what the allocation rule (D12) will eventually consume. The per-sleeve equity JSONLs are already the capture; the correlation is derived, so it is a report, not a new capture stream.

D11 — Validation parameterized per sleeve.

  • CPCV purge = f(sleeve max lookback): momentum 252d (unchanged); meanrev ≈ 21d — purging a 5-day signal by 252d is massive over-purging that throws away most paths. Pinned per sleeve.
  • Per-sleeve baseline_metrics.json (byte-identical guard), pinned OOS reference, clean-start date, walk-forward windows.
  • VRP has NO backtest by posture: its keystone file documents the forward gate as the validation instrument (absence of a backtest is a documented decision, not a hole).
  • The engine gains a stock_selection: "daily" cadence for meanrev — additive only; momentum's byte-identical guard must show Δ 0.00e+00 after the change, like any engine-adjacent edit.

D12 — Capital allocation rule, PRE-REGISTERED NOW (this commit is the registration): if/when real money ever spans sleeves, allocation is equal-risk — inverse trailing-126-trading-day realized vol of each sleeve's live equity curve — rebalanced quarterly, never performance-chasing, never overridden by whichever sleeve had the best recent quarter. Becomes a pinned fleet keystone when config/fleet.yaml lands in Phase 1. (Boring and right; the alternative is the exact gate-shopping the go_live section exists to prevent.)

D13 — Ops surface extensions. AFK pretool_guard.sh deny list gains data/state/; backup_data.sh include gains data/state and results/{sleeve} evaluation dirs; CAPTURES.md ↔ captures.py gain per-sleeve stream entries (the lockstep test enforces it); RUNBOOK what-runs-where table goes per-sleeve; AUDIT.md daily/weekly checks read thales fleet status --json so the weekly routine's cost stays flat as sleeves are added.

D14 — Attention budget, pinned. Max 3 live sleeves. A 4th strategy (e.g., promoted PEAD, futures trend) replaces a killed sleeve or waits. Single digest. One new live sleeve at a time, phases fully gated.

3. Sleeve specs

S1 — meanrev (Phase 2)

  • Hypothesis family (exact params pre-registered via thales research draft-hypothesis --slug meanrev_v1 at Phase-2a start, BEFORE any run): short-horizon reversal — buy the most oversold decile by 2–10d z-scored return within the liquid top of the existing Russell-1000 panel; hold ~3–5d; equal or inverse-vol weights (likely NO Kelly — the pooled-cohort semantics don't transfer to daily cadence; per-sleeve config decides); per-day turnover cap at face value (cap × 1).
  • Gates = standard SHIP apparatus at meanrev parameters, PLUS two sleeve-specific re-validations:
    1. Cost-model re-check at meanrev turnover. Flat-10bps ≡ Almgren-Chriss was validated at MOMENTUM turnover only; meanrev turns over ~10×. The strategy lives or dies on costs — run the equivalence diagnostic at its turnover before believing any Sharpe.
    2. Timing bound re-run under meanrev config. The next_open-vs-next_close wash was measured on momentum; a 10× turnover strategy is 10× more clock-sensitive.
    3. CPCV with purge ≈ 21d, standard + survivorship-free on golden.
  • Live (2b) ONLY on gate pass — paper account #2, own workflow, digest section, 30+ day shakedown before anyone reads its equity curve as signal. Honest prior: published raw reversal edge has decayed badly since the 2000s; expected outcome is REJECTED — and the platform survives that.

S2 — vrp (Phase 3, forward-only)

  • Mechanical rule (pinned in config/vrp.yaml keystones before the first order; current draft, finalize at Phase-3 start): sell a ~30-delta SPY put credit spread, ~30 DTE, defined width (e.g., $5); manage at 21 DTE or 50% of max profit, whichever first; re-enter next cycle; one structure cohort at a time; risk per structure (width − credit) ≤ pinned % of sleeve equity; defined-risk only, no naked short options ever (also enforced structurally in the safety gate — D7 — so a bug can't do it either).
  • Forward gate (its entire validation): own oos-monitor-style criteria pinned before first trade — e.g., ≥126 live td, Sharpe bootstrap lower bound and max-DD limits, plus the left-tail honesty line: this premium pays BECAUSE it hurts sometimes; a drawdown inside the pinned envelope is the strategy working, not failing.
  • Relation to the skew capture: untimed vanilla VRP is a SEPARATE hypothesis from the skew-timed version (12–18mo accrual, pre-registered activation) — running it does not touch, and must not be used to shop, that criterion.

S3 — deferred (pre-registered so they don't drift)

  • futures trend — Phase 4, next in line after the platform is proven: micro futures (MES/MGC/FX/rates) time-series trend on IBKR; the known costs are gateway automation, roll logic, short mechanics, new data.
  • PEAD — AFK hypothesis backlog: estimate-free SUE from the owned PiT-correct XBRL store (seasonal random-walk earnings model, no analyst feed). Equity signal on the weight contract if it ever screens.

4. Phase plan (each phase = its own branch + PR, gated by the existing guards)

PhaseContentExit gate
P0This specCommitted (done)
P1Platform extraction, ZERO behavior change: --sleeve resolution, sleeve/keystone registries, config/fleet.yaml (incl. D12 allocation keystone), fleet status command skeleton. Momentum = sole tenant, grandfathered pathsByte-identical baseline Δ 0.00e+00; full parity harness green; entire suite green; docs-consistency green; momentum CI workflow diff ≈ zero
P2ameanrev research on the platform: strategy class, engine daily cadence (additive), pre-registration, evaluate + CPCV(purge 21) + survfree-golden + cost/timing re-validationsVerdict recorded either way; baseline guard still Δ 0 after the cadence addition
P2bmeanrev LIVE paper (account #2, secrets, workflow, digest section, captures entries) — only on 2a pass30-day shakedown clean; per-sleeve reconcile/heartbeat/backfill proven
P3VRP: order-intent contract, options safety REJECTs, account #3, pinned rule + forward gate; live paper day oneSafety gate proven on synthetic bad structures (naked leg, oversized loss → REJECT); first cycle executes + rolls cleanly
P4futures trend (IBKR) — not scheduled; pre-registered next

5. Risk register

  • Refactor touches engine behavior → the byte-identical guard is the tripwire; any Δ ≠ 0 stops the phase.
  • CI blast radius on the live momentum cron → grandfathering (D3); Phase-1 aims for a near-zero diff to paper-trading.yml.
  • Alert fatigue → one digest (D10); per-sleeve failure emails are pre-labeled; fleet status is one command.
  • Cross-sleeve concentration invisible per-account → D7 fleet exposure warn.
  • 3-account ceiling → accepted + pinned (D14), but keep the framing honest (review 2026-07-05): ATTENTION being the real constraint is the design principle; the NUMBER 3 is an Alpaca paper-account artifact, not a design choice. Futures-trend (deferred) likely needs a 4th account on IBKR — treat 3 as a wall to move then, not "full by design". Revisit with real money (which forces the sub-book design anyway) OR at the 4th-broker step.
  • Paper can't measure execution (equities OR options) → documented; all paper verdicts are rule-fidelity verdicts.

Pre-registered walls for the NEXT contract (audit 2026-07-11)

Mapped by the new-contract stress rehearsal (futures/IBKR) so nobody discovers them by crashing into them. None block today's sleeves; ALL must be cut before a futures/short-capable/non-Alpaca sleeve:

  • safety.py encodes long-only-equity semantics: side=="sell" ≡ de-risking (a SHORT entry would be uncapped and a short cover mis-read), the over-sell guard would reject legitimate short opens, and the daily-loss breaker's all-sells exemption inverts for shorts. The gate needs position-aware direction logic for a shortable contract.
  • Notional is multiplier-blind: order notional assumes qty×price = exposure; futures contract multipliers break every notional-based limit.
  • execution.broker now fails loudly for non-"alpaca" (was a dead key silently trading Alpaca) — a real Broker subclass must be implemented and wired in cli._make_broker.
  • NYSE calendar hardcoded (utils/calendar.py) — futures sessions differ.
  • Weights-vs-order_intent seam: a futures TREND sleeve is weights-shaped but needs short weights + margin sizing; decide extend-vs-third-contract deliberately, don't shoehorn.
  • Solo-operator attention → phase discipline: one live sleeve added at a time, each fully shaken down before the next starts.

6. Open items for Dan (decide before the relevant phase)

  1. Seed capital per paper account — proposal: $10k each (comparability with momentum's track); VRP fine at $10k for $5-wide spreads.
  2. Approve/adjust the meanrev hypothesis family (S1) before Phase-2a pre-registration locks the exact params.
  3. Approve/adjust the VRP rule + forward-gate numbers (S2) before Phase 3 pins them.
  4. Phase-1 go signal.