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
| Sleeve | Decision | Why |
|---|---|---|
| meanrev (2–10d equity reversal) | BUILD — research first, live only on gate pass | Near-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 experiment | Highest-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 line | Best 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 hypothesis | An 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 secondStrategysubclass + 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_ordersand 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 correlation — forward 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_v1at 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:
- 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.
- 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.
- 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.yamlkeystones 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)
| Phase | Content | Exit gate |
|---|---|---|
| P0 | This spec | Committed (done) |
| P1 | Platform extraction, ZERO behavior change: --sleeve resolution, sleeve/keystone registries, config/fleet.yaml (incl. D12 allocation keystone), fleet status command skeleton. Momentum = sole tenant, grandfathered paths | Byte-identical baseline Δ 0.00e+00; full parity harness green; entire suite green; docs-consistency green; momentum CI workflow diff ≈ zero |
| P2a | meanrev research on the platform: strategy class, engine daily cadence (additive), pre-registration, evaluate + CPCV(purge 21) + survfree-golden + cost/timing re-validations | Verdict recorded either way; baseline guard still Δ 0 after the cadence addition |
| P2b | meanrev LIVE paper (account #2, secrets, workflow, digest section, captures entries) — only on 2a pass | 30-day shakedown clean; per-sleeve reconcile/heartbeat/backfill proven |
| P3 | VRP: order-intent contract, options safety REJECTs, account #3, pinned rule + forward gate; live paper day one | Safety gate proven on synthetic bad structures (naked leg, oversized loss → REJECT); first cycle executes + rolls cleanly |
| P4 | futures 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
notionalassumes qty×price = exposure; futures contract multipliers break every notional-based limit. execution.brokernow fails loudly for non-"alpaca" (was a dead key silently trading Alpaca) — a real Broker subclass must be implemented and wired incli._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)
- Seed capital per paper account — proposal: $10k each (comparability with momentum's track); VRP fine at $10k for $5-wide spreads.
- Approve/adjust the meanrev hypothesis family (S1) before Phase-2a pre-registration locks the exact params.
- Approve/adjust the VRP rule + forward-gate numbers (S2) before Phase 3 pins them.
- Phase-1 go signal.