Python is the default language of trading research. Broker REST and WebSocket APIs are how many of those notebooks try to grow up. The gap between a requests.post in a tutorial and a bot you can leave running is identity, state, and all the ways a retry can buy twice. This guide is the build sequence we use when a founder or CTO wants a Python bot against a real broker API, not a screenshot of a candlestick library.
DayDecoder implements this as custom software from Ahmedabad. We will connect to APIs, write tests, and put risk in front of orders. We will not sell you a “profitable Python bot”.
Risk disclaimer. Live trading through broker APIs can lose money, including by software defects, duplicate orders, stale data and leverage. Tutorials, backtests and paper trading are not live results. This article is about engineering against APIs. It is not investment, trading or financial advice, and DayDecoder does not promise profits or favourable fills. Only risk capital you can afford to lose. Seek regulated advice in your jurisdiction if you need it.
Key takeaways
- Read the broker’s order, position and rate-limit model before you pick a library. Wrappers hide the parts that hurt.
- Paper or a dedicated demo account first. Live keys never live in the research environment.
- Idempotency, reconciler, and a kill switch are the product. The signal is a plugin.
- Pin library versions. Broker APIs change. Your bot should fail loud, not trade yesterday’s schema.
- One execution path. If MetaTrader is also in the mix, read the bridge article and pick an owner.
Choose the venue like a vendor, not like a YouTube comment
Write down: asset class, whether you need CFDs, listed futures, cash equities or crypto; whether the firm is allowed to offer it to you; whether the API can place the order types you specified; whether there is a sandbox; whether you can IP-allowlist; what the kill switch is if their websocket dies.
If the API cannot query open orders reliably, you do not have a production venue. If the only docs are a forum thread, budget discovery, not a fixed-price bot.
Legal: in the US, UK, EU and Australia, who may offer you leverage and under what warnings is regulated. We are a software shop. We are not your broker and not your lawyer. Your procurement should already have a broker relationship before we write the first client.
Project layout that will still make sense in six months
config— environment, not a JSON file of lots next to the git root.marketdata— adapters that normalise trades and clocks to your internal types.strategy— pure-ish; see architecture.risk— hard limits, called every time, with tests.execution— the broker adapter: place, cancel, query, map rejects.reconcile— venue is truth.ops— pause file, metrics, structured logs.
Use a lockfile (uv, Poetry, or pip-tools). Python 3.12+ is fine; what matters is the same version on the VPS. Type hints and a schema library (Pydantic or similar) for order payloads. Tests that do not hit the network by default. HTTP replay fixtures from the sandbox.
Auth and secrets
API keys in the environment or a secret manager. Rotate. Separate read-only keys for research if the broker offers them. Never commit a .env with live keys. Never log the Authorization header. If you use JWT, handle clock skew. If you use HMAC, canonicalise the body exactly as they specify or you will debug 401s for a day.
Talking to REST and websockets
REST: timeouts, bounded retries only on idempotent GETs, and on POST only when the broker’s idempotency key is in the request. A 500 after you received no body is the worst case: query orders by client id before you send again.
Websocket: a supervised connection, ping/pong, resubscribe, and a policy when the stream is stale (pause, or fall back to REST snapshots). Do not compute signals on a stream you cannot prove is live.
Respect rate limits with a token bucket in your process. Bursting until 429 and then spinning is how you miss the flatten you needed.
The first vertical slice (no alpha required)
- Authenticate against sandbox.
- Fetch positions and orders. Persist a snapshot.
- Place a tiny limit order well away from the market, cancel it, prove the round trip in logs.
- Kill switch: a local file or HTTP POST that cancels all and refuses new orders.
- Only then plug a strategy that may actually want a fill, still at toy size, still in sandbox or paper.
If an agency starts with the moving average, they are skipping the part that costs you money when it fails.
Mapping symbols and sizes
Broker symbols are not Yahoo tickers. Store a map you control. Contract size, tick, and min size come from the API’s instrument endpoint if it exists, cached with a TTL, not hardcoded from a blog post (including this one). Round sizes with their rules, not with round(x, 2) as a superstition.
Running it
A Linux VPS or container with a process supervisor, disk for logs, NTP, and alerts if the process dies. Do not run under a laptop lid. Deploy from git tags. The same discipline as MT5 on a VPS, different binary.
If the rest of your company is Laravel, you may still keep the bot in Python and expose a small control API. Do not put the trading loop inside a PHP-FPM worker. Queues are for jobs; a trading loop is a long-lived process.
What to send us to quote
Broker name, sandbox access under NDA if needed, asset list, whether you need MT in parallel, who owns the kill switch, and the rule in one page. Indicative cost is a function of those, not a public rate card. Typical first slices of API work land in a five-figure band when the venue docs are real; messy undocumented APIs and multi-venue books run higher. Those bands are indicative, not a quote. Use contact or contact@daydecoder.com.
DayDecoder: 10+ years, 103 customers, software you hold in your git org.
State machines beat “if position then else”
A small explicit state — flat, pending, in, flattening, paused, unknown — keeps you from sending a second entry while a cancel is in flight. unknown is the state after a disconnect until reconcile returns. Trading from unknown is how you pyramid by accident. Draw the states on one page. If you cannot, the code will invent them at 3 a.m.
Pending orders need timeouts. A limit that never fills is not a position, but it is risk if the market can sprint through it later. The spec says whether you chase, cancel, or wait. The code should not improvise.
Testing against recordings
Record sandbox HTTP and websocket (sanitised) as fixtures. Replay in CI. When the broker changes a field name, the fixture test fails before live does. Mock clocks. Never sleep(60) in a unit test. Property-style tests on rounding (lots always on step, never below min) catch more losses than another candlestick screenshot.
A thin “sim venue” that acknowledges orders and keeps a position is useful for strategy tests. Do not confuse it with the real matcher. Label the report “sim” in fat letters so nobody pastes it into a pitch deck.
Dependencies, licensing and supply chain
Pin hashes if you can. Review who owns the SDK. An abandoned GitHub wrapper is a liability. Prefer the broker’s official client plus your adapter. If you must use community code, vendor it and take patches. The same attitude as the unofficial MT DLL: origin, bitness, updates.
Python’s scientific stack is heavy. The live box does not need Jupyter, CUDA, or a full conda zoo if the strategy is a few numbers. Smaller image, fewer surprises, faster restart.
Observability you will actually use
Structured logs: ts, account, symbol, intent_id, venue_id, state. Metrics: rejects, lag, unexplained position, reconnects. An alert if no heartbeat. An alert if heartbeat says paused and you thought it was live. Dashboards without alerts are interior design.
When something is wrong, you should be able to answer “what was the last acknowledged order” without SSHing into a mystery tmux. If you cannot, fix that before adding another strategy.
Have a broker API and a rule, and need Python that fails closed? Custom software development is the usual home, with cloud if you want us to harden the box. Or talk to us.
Paper trading the API, not the dream
Use the broker’s paper account with the same endpoints if they offer them. Log every payload. After a week, compare your local position file to their position endpoint every minute. Any drift is a bug, even by one contract. Do not “restart to clear it” without finding the send that caused it. Restarts that clear state are how you hide duplicates.
When paper looks boring — and it should — you have a system. Boring is the goal. Excitement is a paging event. If your developer is excited about a live fill on day one, they may have skipped the reconciler. Ask to see it.
SDK upgrades go through the same fixture tests. Brokers rename fields. Your adapter is the shock absorber. Strategies should not import the SDK. That is how you survive an upgrade without rewriting the rule. Put the SDK version in the heartbeat log so a reject spike can be correlated with a deploy you forgot.
Environment split: research, paper, live, with keys that cannot cross. A single .env with a commented live key is how comments fail. Separate files, separate users on the box, separate allowlists at the broker if they offer IP pins. Promotion is copy of a git tag, not copy of a key. If we find a live key in a research notebook during a review, that is a blocking finding, same as a public S3 bucket.
Handover checklist
You should have: repo, pinned dependencies, adapter not leaking SDK types, paper vs live env files, fixture tests in CI, heartbeat fields, IP allowlist notes, and a kill switch that a non-author of the strategy can hit. Include the broker’s sandbox expiry if any. Include how to rotate a key without leaving the old one in a tmux session. If we cannot rotate a key in a rehearsal, we do not call the system production. Call it a prototype and keep the size at zero. Zero is an honourable position size. It is also a risk limit you can test without a market. Write the rotation steps in the README so the next engineer does not invent a fourth environment file named .env.final. README beats folklore, and folklore is how live keys leak into research laptops.
Partial fills, sessions and whose clock you trust
A fill for half the size you sent is not a bug at most venues. Your position file has to accept it, your strategy has to know it is half in, and flatten has to cancel the remainder before it sends the other side. Tests should include a partial fill fixture. If your code assumes all-or-nothing, the first busy session will teach you otherwise, at size.
Session hours belong in config: when the venue is open, when your bot is allowed to send, and what happens at the Friday close if you are not meant to hold. A bot that “just runs” through a halt, a holiday, or a stale Sunday book is not unsupervised alpha. It is an unattended process with a debit card. Put the calendar next to the kill switch. If the broker publishes a clock endpoint, prefer it to the VPS wall clock for session boundaries, and still NTP the box so your logs can be compared to theirs.
Rejects need a taxonomy in logs: rate limit, invalid size, outside hours, insufficient margin, unknown symbol. Each class has a different next action. Treating every 400 as “retry” is how you hammer a bad lot size until the account is flagged. Treating every 400 as “pause forever” is how a one-tick fat-finger rule disables you until Monday. Write the map. Review it when the broker adds a code.
FAQ
Which Python library should we use?
The one the broker maintains, if it exists, plus your own adapter so you can replace it. Unofficial wrappers go stale. We will wrap whatever we use so your strategy does not import a random SDK in fifteen files.
Can we use ccxt or a similar multi-exchange kit?
For research, maybe. For live, only if you accept their abstraction of order types and you still write a reconciler against the raw venue. Multi-exchange “alpha” is often multi-exchange bugs.
How do we paper trade?
Prefer the broker’s paper/sandbox. A local simulator is useful for tests and a poor model of their matching engine. Never point live keys at a test strategy “just this once”.
Will you write the strategy that makes money?
No. We implement and productionise rules you own, with risk controls. Edge is your problem, or a licensed adviser’s, not a software invoice.
Can this sit next to our Laravel product?
Yes: control plane and customer UI in Laravel, trading loop in Python, a clear boundary. That is a product architecture conversation, not a copy-paste from a trading forum.
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash