← all research

Status: DONE (2026-08-15) · Suite: 294 passed, 2 skipped (+13 tests in tests/test_alpaca_risk.py)

What was built

data/alpaca/risk.py — vol-targeted sizing + wall-clock trading guardrails for the long/flat bar system.

Sizing (no leverage, spot only)

risk_budget = equity * risk_per_trade            (config 2%)
qty         = risk_budget / ATR                  (coin units)
notional    = min(qty * price, equity * max_position_pct)   (cap: 10%)
  • A 1-ATR adverse move costs ≤ risk_per_trade of equity before the position cap; the cap truncates further concentration.
  • min_notional floor (default $10): undersized orders are SKIPPED, never rounded up (a crypto min-trade guardrail).
  • qty_for_notional rejects non-positive notional/qty — long/flat enforced at the boundary.

Guardrails (RiskManager, state machine)

GateBehavior
daily loss limit (max_drawdown_daily, 5%)halts until the next UTC day
total drawdown (max_drawdown_total, 15%)halts until equity recovers above peak × (1-thresh); computed from running peak, not sticky
consecutive-loss cooldown (cooldown_after_loss, 4)after N losses in a row, skip N trades (a win serves one)
max daily trades (max_daily_trades, 12)halts for the day
no equityhard halt
  • record_trade(pnl, ts) updates equity, running peak, daily counters, and cooldowns; can_trade(ts) returns (go, reason) with the specific gate in reason; halt_reason() strips it for logs.
  • Timestamps are wall-clock aware: day boundaries roll in UTC; cooldowns are trade-counted, not time-based.

Tests

13 offline tests: exact notional math (cap binding, 1-ATR cost = risk budget when uncapped), min-notional skip, degenerate inputs → 0, non-negative qty, long-only realized PnL, daily gate + UTC day roll, cooldown arming/serving, computed (recoverable) drawdown halt, max-daily-trades, peak tracking, config defaults, halt_reason helper.

Integration

Phase 7's paper executor consumes RiskManager + size() per signal before ordering: gate → size → market/limit order. The Phase-8 monitor reports the halt reasons; the same guards apply to live trading.