feat: Tradier broker mirror — config-switch redundancy for Alpaca execution

tradier_broker/ mirrors every trading call the live loop makes on
AlpacaPaperClient (account, clock, positions, orders, submit incl.
bracket->OTOCO, cancel, replace, close) with identical signatures and
Alpaca-shaped returns, so trading_execution / exit_manager / flatten /
execution / sync run unchanged. Market data always delegates to Alpaca.

- BROKER=tradier (+ TRADIER_* env) swaps execution client in run_live_paper;
  default path untouched
- client_order_id -> tag preserves the lost-submit reconcile
- TradierApiError subclasses AlpacaApiError so the executor's
  rejection-vs-transport fork works as-is
- sandbox-verified: balances/clock/positions/orders, OTOCO preview
  (result:true), place -> tag lookup -> cancel round trip
- 18 unit tests (translation + client paths); executor integration checked
- docs/TRADIER-FALLBACK.md switch runbook

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Fable 5 committed 2026-07-26 11:01:50 -05:00
1 parent 48d75816b6
commit 1cefb51c63
8 files changed
+960 -1

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
# Tradier broker fallback
Redundancy path if the Alpaca live account stays blocked. `tradier_broker/`
mirrors every trading call the live loop makes on `AlpacaPaperClient` — same
method names, signatures, and Alpaca-shaped return dicts — so
`trading_execution`, `runtime/exit_manager`, `runtime/flatten`,
`alpaca_paper.execution` and `alpaca_paper.sync` run unchanged.
## Switch (config only)
```
BROKER=tradier
TRADIER_ACCESS_TOKEN=... # sandbox or live token
TRADIER_ACCOUNT_ID=... # e.g. VA71549758
TRADIER_ENV=sandbox # 'live' for real money — explicit opt-in
```
`BROKER` unset/`alpaca` = today's behavior, byte-for-byte. Alpaca keys stay
required either way: **market data (bars, screeners, quotes, news) always
stays on Alpaca** — the Tradier client only replaces order execution and
account/position state, delegating all data calls to the Alpaca client.
## Verify before trusting
```
python3 -m tradier_broker.smoke # balances/clock/positions/orders
python3 -m tradier_broker.smoke --preview AAPL # broker-validates bracket form, never submits
python3 -m tradier_broker.smoke --place-cancel AAPL # full submit -> tag lookup -> cancel
```
Smoke refuses order exercises against the live base URL.
## Mapping notes
- Alpaca bracket -> Tradier OTOCO (entry + OCO take-profit limit / stop);
oto -> OTO; standalone gtc stop sells map directly.
- `client_order_id` -> Tradier `tag` (lost-submit reconcile works via tag lookup).
- Tradier positions carry no mark; `get_positions` prices them off
`/markets/quotes` (sandbox quotes are 15-min delayed — fine for redundancy
checks, and live tokens get real-time).
- `replace_order` maps to PUT (moves stop/limit without cancelling — OCO stays
intact); Tradier cannot change qty on a working order (raises, cancel+resubmit).
- `get_orders` is session-scoped at Tradier (today's orders) and status/symbol
filters are client-side; the live loop only queries today + held symbols, so
behavior matches.
## Before flipping live money to Tradier
1. Rotate the sandbox token (it transited chat) and mint a live token.
2. Run a full paper week on sandbox with `BROKER=tradier` in the paper stack.
3. Re-run the naked-stop drill from docs/LIVE-CUTOVER.md — leg-stripping
semantics differ per broker; verify a filled OTOCO's stop leg survives an
entry-order cancel attempt.