Alpaca.
The Python-first US equities broker. REST + WebSocket APIs, a free paper account, and the smallest gap between idea and live trade — at the cost of being US-equities-only.
The honest pitch.
Alpaca is the easiest place to put your first Python automation into production. Sign up, get a paper key in 30 seconds, place your first market order in 5 lines of code. The trade execution surface is comfortable for a developer — JSON in, JSON out, with WebSocket trade-updates that mirror exactly what fires in your account. Where Alpaca falls short is breadth: US equities and a subset of crypto only. If you need futures, options, or non-US markets, you'll be running a multi-broker stack.
Auth, orders, limits.
| Auth | API key + secret, Basic auth headers; separate paper + live key pairs. |
|---|---|
| Order API | REST /v2/orders + WebSocket trade_updates stream; supports market, limit, stop, stop-limit, trailing, bracket, OCO. |
| Rate limits | 200 requests/minute on most endpoints; data depends on subscription tier (Free, Algo, Unlimited). |
| Sandbox | Paper account at paper-api.alpaca.markets — free, real market data, fake fills. |
| Settlement / fractional | T+1 stocks; fractional shares supported; crypto 24/7. |
| Geo | US-only retail accounts (some non-US via Alpaca Broker API). |
Hello-world, but real.
The 'minimum viable bot'. Connects to the paper endpoint, asserts the account is in good standing, places a market buy, and streams trade_updates so you see the fill in real time.
| 1 | # pip install alpaca-py |
| 2 | import os |
| 3 | from alpaca.trading.client import TradingClient |
| 4 | from alpaca.trading.requests import MarketOrderRequest |
| 5 | from alpaca.trading.enums import OrderSide, TimeInForce |
| 6 | from alpaca.trading.stream import TradingStream |
| 7 | |
| 8 | client = TradingClient( |
| 9 | api_key=os.environ["ALPACA_KEY"], |
| 10 | secret_key=os.environ["ALPACA_SECRET"], |
| 11 | paper=True, # paper-api.alpaca.markets |
| 12 | ) |
| 13 | |
| 14 | acct = client.get_account() |
| 15 | assert acct.trading_blocked is False, "Account is blocked" |
| 16 | |
| 17 | order = client.submit_order( |
| 18 | MarketOrderRequest( |
| 19 | symbol="SPY", |
| 20 | qty=1, |
| 21 | side=OrderSide.BUY, |
| 22 | time_in_force=TimeInForce.DAY, |
| 23 | ) |
| 24 | ) |
| 25 | print("submitted:", order.id) |
| 26 | |
| 27 | # Optional: stream fills in real time |
| 28 | async def on_update(data): |
| 29 | print("trade_update:", data.event, data.order.status, data.order.filled_qty) |
| 30 | |
| 31 | stream = TradingStream(os.environ["ALPACA_KEY"], os.environ["ALPACA_SECRET"], paper=True) |
| 32 | stream.subscribe_trade_updates(on_update) |
| 33 | stream.run() |
The traps everyone hits.
Real production failure modes. Sev1 = capital loss risk. Sev2 = data integrity / silent wrongness. Sev3 = developer ergonomics that bite later.
Paper account fills are too optimistic
Sev2What happens. The paper engine fills at the NBBO mid (or close to it) without slippage on small size. A strategy that looks great in paper can lose 5-10 bps per trade live.
Fix. Subtract a slippage assumption (5-15 bps on liquid names, 25-50 bps on small caps) when reporting paper expectancy. Validate against a 2-week live forward run before scaling.
Wash trades on rapid reversals
Sev1What happens. Closing a position and re-entering within 30 days of a loss in the SAME taxable account triggers a wash-sale rule. Alpaca doesn't stop this.
Fix. Track open/closed P&L per symbol. Don't re-enter a loser within 31 days unless you accept the wash-sale tax treatment.
Bracket orders cancel each other only after fill
Sev2What happens. Submit a bracket order and the stop+target sit as 'held' orders. If you manually exit the parent, both bracket legs still show as open until you cancel them.
Fix. Always cancel residual bracket legs in your strategy's exit logic. Subscribe to trade_updates and reconcile open orders against position state every minute.
Data tier confusion
Sev3What happens. The Free data tier excludes IEX-only feeds and has a 15-min delay for some symbols. Your live signals diverge from what the chart shows.
Fix. Pick a paid data tier (Algo Trader Plus or higher) before going live, or explicitly handle delayed quotes in your strategy's bar-completion logic.
What to pair it with.
No platform stands alone. These are the layers that — paired with Alpaca — produce production-grade automation.
| Layer | Recommended | Why |
|---|---|---|
| Strategy code | Python 3.11+ with pandas, alpaca-py, websockets | Alpaca-py is the official SDK; pandas does your analysis. |
| Data | Alpaca Market Data API + Polygon.io for backfill | Alpaca's bars are fine live; Polygon has the deep history for research. |
| Hosting | Fly.io / Railway / Hetzner — Linux is fine | Python runs anywhere; pick the cheapest VPS in us-east-1 for lowest latency to Alpaca's API. |
| Risk / monitoring | Custom heartbeat + Sentry + a daily P&L Discord webhook | Alpaca won't tell you your strategy crashed. You have to. |