Add Alpaca broker support (paper-trading by default)#61
Merged
Conversation
Adds an Alpaca adapter for single-leg equity options orders so the engine has a second broker path next to Robinhood / IBKR. Defaults to paper trading (https://paper-api.alpaca.markets) because the Alpaca account is not yet funded -- live trading is gated behind an explicit ALPACA_PAPER=false flip and is intentionally NOT wired into the CLI yet. Surface mirrors the new option methods on SafeCashBot: same banner format ('='*70 dividers), same dry_run gating, same [OK]/[ERR]/[WARN] prefixes. Single-leg only -- spreads can be added later. Changes: - utils/alpaca_broker.py: AlpacaBroker class with get_account, get_option_quote, place_option_buy_limit_order, place_option_sell_limit_order, get_open_option_positions. Builds OPRA symbols (e.g. XLY260918C00120000) from (symbol, expiration, strike, type) so callers don't have to know the OCC encoding. Pre-trade validation: buying-power check on buys, position-held check on sells. - scripts/place_option_order_alpaca.py: CLI wrapper mirroring scripts/place_option_order.py. --live means "send to the paper endpoint" (i.e. actually call submit_order). A --real-money flag is left as a TODO until the live account is funded. - tests/test_alpaca_broker.py: smoke tests with mocked TradingClient asserting (a) dry_run=True never calls submit_order and (b) dry_run=False calls submit_order exactly once with the right OPRA symbol. Stubs alpaca-py at import time so CI without the dep still collects. - requirements.txt: pin alpaca-py>=0.30.0 (was an opt-in comment). - .env.example: document ALPACA_API_KEY_ID / ALPACA_API_SECRET_KEY / ALPACA_PAPER under a new "Alpaca broker" block; keep the legacy ALPACA_API_KEY / ALPACA_SECRET_KEY aliases for older fill-audit code. Env vars are loaded with python-dotenv to match utils/rh_auth.py:21. No changes to safe_cash_bot.py or any RH code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
utils/alpaca_broker.AlpacaBrokerfor single-leg equity options orders via Alpaca, so the engine has a second broker path next to Robinhood / IBKR.https://paper-api.alpaca.markets). The live Alpaca account is intentionally not funded yet, so live trading is gated behindALPACA_PAPER=false(env) orAlpacaBroker(paper=False)and is not wired into the CLI flag set.SafeCashBot. Single-leg only -- spreads can be added later.What changed
utils/alpaca_broker.py--AlpacaBrokerwithget_account,get_option_quote,place_option_buy_limit_order,place_option_sell_limit_order,get_open_option_positions. Builds OPRA symbols (e.g.XLY260918C00120000) so callers don't have to know the OCC encoding. Pre-trade validation: buying-power on buys, position-held on sells.scripts/place_option_order_alpaca.py-- CLI wrapper that mirrorsscripts/place_option_order.py.--livemeans "send to the paper endpoint" (i.e. actually callsubmit_order). A--real-moneyflag is left as a TODO until funded.tests/test_alpaca_broker.py-- smoke tests with a mockedTradingClientasserting (a)dry_run=Truenever callssubmit_orderand (b)dry_run=Falsecalls it exactly once with the right OPRA symbol, qty, and limit price. Stubsalpaca-pyat import time so CI without the dep still collects.requirements.txt-- pinsalpaca-py>=0.30.0(was an opt-in comment before)..env.example-- documentsALPACA_API_KEY_ID/ALPACA_API_SECRET_KEY/ALPACA_PAPERunder a new "Alpaca broker" block; legacyALPACA_API_KEY/ALPACA_SECRET_KEYaliases preserved.No changes to
safe_cash_bot.py,rh_auth.py, or any IBKR code.Test plan
pytest tests/test_alpaca_broker.py-- 4/4 pass locallyXLY 2026-09-18 $120 CALL->XLY260918C00120000andSPY 2026-12-19 $432.5 PUT->SPY261219P00432500python scripts/place_option_order_alpaca.py XLY 2026-09-18 120 call 1 4.50 buyprints the banner and exits without callingsubmit_order.env, runpython scripts/place_option_order_alpaca.py XLY 2026-09-18 120 call 1 4.50 buy --liveto confirm the order lands in the Alpaca paper dashboardsafe_cash_bot.pyand Robinhood paths are unchangedFollow-ups (intentionally out of scope)
--real-moneyCLI flag once the live Alpaca account is fundedAlpacaBrokerinto the higher-level execution engine alongside the RH/IBKR paths