← all research
REJECTED

"Phase 7 — Paper/live executor (Alpaca crypto)"

2026-08-15

Status: DONE (2026-08-15) · Suite: 304 passed, 2 skipped (+10 tests in tests/test_alpaca_executor.py)

What was built

data/alpaca/executor.py — bar-aligned execution for the long/flat system, matching the Phase-5 backtest book bar-for-bar.

Layers

LayerRole
plan_signal()Pure decision: risk gate (RiskManager.can_trade) → fixed threshold hurdle (threshold_bps, an ORDERED expected-return hurdle — a live threshold cannot be a future-looking quantile) → vol-target sizing → min-notional skip. Returns a Plan.
BarExecutorOne-symbol bar state machine: on_bar_close(ts, pred, price, atr) decides only when flat (no overlapping positions — safety over rate); on_bar_open(ts, open) flattens when ts >= exit_bar_ts (bar t+1+h open) and fills the pending entry at THIS open (bar t+1 open).
PaperBrokerSimulated fills at the bar open with the Phase-5 cost math (cost = px*(1+fee)), every order recorded; no network.
AlpacaBrokerThin adapter on alpaca-py TradingClient (paper=True for paper); shorts and non-canonical symbols rejected BEFORE any API call; client=None degrades to paper behavior.

Honesty properties (enforced + tested)

Tests (10, offline)

signal gating (threshold, negative/NaN pred, risk halt, min-notional skip); full entry→exit cycle with exact bar offsets (t+1 entry, t+1+h exit) and fee reproduced PnL; in-position signals ignored; non-canonical rejection; paper broker long-only; Alpaca broker rejection + submission via a fake client (symbol=="ETHUSD", qty passed through) and client-less degradation; taker fee variant.

Live threshold caveat

Phase 5's backtester thresholds the FULL OOS ranking (quantile). Live, the threshold must be a fixed parameter: threshold_bps. A calibration step (choosing threshold_bps from OOS metrics, then re-validating on a held-out tail) belongs to the deployment checklist in Phase 8.