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
| Layer | Role |
|---|---|
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. |
BarExecutor | One-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). |
PaperBroker | Simulated fills at the bar open with the Phase-5 cost math (cost = px*(1+fee)), every order recorded; no network. |
AlpacaBroker | Thin 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)
- The entry order is decided at bar t close BEFORE bar t+1's open is known — nothing peeks forward.
- Fills reproduce Phase-5 math exactly:
pnl = (exit*(1-fee) - entry*(1+fee)) × qty. - Long/flat only:
side != buyorqty <= 0raises at the boundary. - Non-canonical symbols (
BTC/USD,ETHUSD) rejected in the executor AND the broker.
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.