API Trading: From Exchange API to Execution
An algorithm isn't real until it places orders. Learn how exchange and broker APIs work, how to authenticate, and how to build a safe execution layer.
As ferramentas interativas podem não funcionar na vista traduzida.
API Trading: From Exchange API to Execution
Your backtest doesn't trade. Your script doesn't trade. Only a live order through a real API trades — and getting that step right is harder than the signal.
Every algorithm eventually has to talk to an exchange or broker. The execution layer is where good strategies go to die from latency bugs, rate limits, and accidental fat-finger orders. Treat it as carefully as the strategy itself.
The anatomy of a trading API
Most modern trading APIs share a common shape:
- REST for account, order management, and historical data — easy, but slow (tens of ms)
- WebSocket for real-time market data and order updates — fast, but stateful
- FIX for institutional order routing — verbose, fast, the legacy standard
A typical flow: subscribe to a market data WebSocket, compute a signal, send a REST order, listen on a user-data WebSocket for fills.
Authentication
Almost every API uses a signed-request scheme:
signature = HMAC_SHA256(secret_key, message)
Where message includes the timestamp, method, path, and parameters. The exchange verifies the signature matches your API key.
Critical safety rules:
- Never commit API keys to git or hardcode them — use environment variables
- Use IP allowlists if the exchange supports them
- Disable withdrawal permissions on trading-only keys
- Rotate keys periodically
Order lifecycle
New → Pending → Partially Filled → Filled
↓
Cancelled / Rejected / Expired
Your execution layer must handle every transition:
- Partial fills: re-evaluate the remaining quantity; don't double-submit
- Rejects: log the reason; don't retry blindly
- Timeouts: poll order status; never assume an order is alive
- Disconnects: reconnect, sync state, then resume — never assume anything
Building a safe execution layer
- Idempotency: use client order IDs so retried submissions don't double-fill
- Pre-trade risk checks: enforce max position size, max order count, daily loss limit before sending
- Kill switch: a one-button (or one-API-call) way to cancel all open orders and flatten
- Logging: every order, fill, error, and state transition timestamped
- Backoff and rate limits: respect the API's rate limit; exponential backoff on 429s
- Reconciliation: periodically compare your local state with the exchange's reported state
A minimal order function
def place_order(side, symbol, qty, price):
if qty > MAX_ORDER_SIZE:
raise RiskError("order too large")
if get_open_positions() + qty > MAX_POSITION:
raise RiskError("position limit exceeded")
client_id = uuid4()
order = exchange.create_order(
symbol=symbol, side=side, type='limit',
quantity=qty, price=price, clientOrderId=client_id
)
log(f"submitted {client_id} {side} {qty} {symbol} @ {price}")
return order
Every one of those checks is a real bug you'll eventually hit.
Common pitfalls
- Floating-point rounding on quantity/price → rejected orders
- Forgetting to handle websocket disconnects → silently stale data
- Resting orders left open overnight → unexpected fills at the open
- Treating testnet and mainnet identically → testnet orders on mainnet
- No idempotency → duplicate fills after a network retry
Summary
The execution layer is where strategy meets reality. Authenticate carefully, handle every order state, enforce pre-trade risk, and log everything. A robust execution layer is unglamorous — but it's the difference between a strategy that runs for years and one that explodes on day three.
Live Chart
Open full chart →Related market data, powered by TradingView.