3.9 KiB
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-> Tradiertag(lost-submit reconcile works via tag lookup).- Tradier positions carry no mark;
get_positionsprices them off/markets/quotes(sandbox quotes are 15-min delayed — fine for redundancy checks, and live tokens get real-time). replace_ordermaps to PUT (moves stop/limit without cancelling — OCO stays intact); Tradier cannot change qty on a working order (raises, cancel+resubmit).get_ordersis 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.
Parallel shakeout stack (TrueNAS)
deploy/docker/truenas-compose.tradier.yaml is the committed template; the
filled copy lives (git-excluded) at truenas-compose.tradier.local.yaml.
Runs beside the paper stack, fully isolated:
- own postgres (host 5434, fresh
pgdata-tradiervolume), own dashboard (host 8768), loop withBROKER=tradieragainst the sandbox account - market data still Alpaca; Telegram + deadman OFF (main stack keeps alerts)
- main data dir mounted read-only — inherits nightly
learned_params.json/overrides.env, cannot clobber them; if a service ever crashes on a read-only-filesystem error, switch that mount to a writable copy - fresh DB starts empty (knobs at code defaults). For research-baseline +
knob parity with the paper stack, reuse the live-staging playbook:
./bootstrap_live_db.sh <nightly-backup.sql.gz> <tradier-postgres-container> - fresh DB also lacks the research tables the dashboard polls
(
experiment_verdictsetc. — postgres logsrelation ... does not existon every poll). Harmless, but either bootstrap as above or create the missing tables schema-only; done by hand on the first deploy 2026-07-27 (18 tables), plus a copy of the paper stack'sconfig_overridesrows (75 knobs — without them the stack runs factory defaults and entries pile up in the manual approval queue) - needs an image built from >=
1cefb51(tradier_broker/in image) - sandbox fills are simulated and can be slow or absent — an order parked in
pending_newis usually the sandbox, not the code
Before flipping live money to Tradier
- Rotate the sandbox token (it transited chat) and mint a live token.
- Run a full paper week on sandbox with
BROKER=tradierin the paper stack. - 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.