At a glance
- Prerequisite
- A tested strategy; automation does not create one
- Main components
- Data, signal, risk, execution, state, monitoring
- Hardest part
- State management and failure recovery
- Before real money
- Paper run, then minimum size, then scale
Key takeaways
- Write the strategy as a pure function of market data and current state, so it can be tested in isolation without a broker connection.
- Persist state to disk continuously, because the system will restart and must resume knowing exactly what it holds.
- Every external call can fail, time out, or return unexpected data, and every one of those cases needs an explicit response.
- Use client-generated order IDs so retries cannot create duplicate orders, which is the most dangerous common bug.
- The same code path should run in backtest, paper, and live modes, with only the data source and broker interface swapped.
Structuring the system
data/ fetch, validate, cache market data
strategy/ pure functions: (data, state) -> desired positions
risk/ validate and size every order; veto authority
execution/ broker interface: place, amend, cancel, poll fills
state/ persistent record of positions, orders, equity
monitor/ heartbeat, alerts, reconciliation
config/ parameters, limits, credentials (never in code)
logs/ structured, append-only, every decision recorded
Key design rule:
strategy/ must be a PURE function.
Same inputs -> same outputs, no side effects,
no network calls, no clock reads.
This is what makes it testable, backtestable, and
debuggable when something goes wrong at 3am.The purity rule is the most valuable single design decision. A strategy function that reads the clock or calls an API cannot be tested deterministically, cannot be backtested on the same code path, and cannot be reasoned about when it misbehaves.
The main loop
while running:
1. fetch market data
2. validate: is it fresh, complete, sane?
if not -> alert, skip this cycle, do not trade
3. reconcile: does broker state match our state?
if not -> alert, halt, require human review
4. compute desired positions from strategy(data, state)
5. compute the difference from current positions
6. for each required order:
risk layer validates or vetoes
execution places with a unique client order ID
7. poll for fills, update state, persist to disk
8. place or amend protective stops immediately
9. log everything
10. emit heartbeat
sleep until the next cycle
Every step between 2 and 9 can fail.
Each failure needs a defined response, and the default
response is always: stop trading and alert a human.State management, the hardest part
- Persist after every change. The process will be restarted, by you, by a crash, or by the host. It must resume knowing exactly what it holds and what orders are outstanding.
- Treat the broker as the source of truth. Your record is a cache. Reconcile against the broker on startup and on a schedule, and halt on any mismatch.
- Handle partial fills explicitly. A position of 37 shares when the strategy expected 100 must propagate correctly through every subsequent calculation.
- Track orders through their full lifecycle. An order you believe is cancelled but that actually filled is the source of the worst state bugs.
- Use append-only logs. You will need to reconstruct what the system believed at a specific moment, and overwritten state makes that impossible.
- Make restarts safe. Restarting mid-cycle should never duplicate an order or lose a stop. Test this deliberately by killing the process at random points.
Failure modes and responses
| Failure | What can go wrong | Correct response |
|---|---|---|
| API timeout on order placement | Order may or may not have been received | Query by client order ID before any retry |
| Data feed stops updating | Trading on stale prices | Halt trading; alert; do not use last known value |
| Partial fill | Position differs from intention | Update state from actual fills; adjust stops |
| Broker rejects an order | Margin, price band, or halted instrument | Log the reason; do not blindly retry |
| Process crash mid-cycle | Unknown state on restart | Reconcile with the broker before any action |
| Duplicate fill notification | Double-counted position | Deduplicate by order ID |
| Clock drift or timezone error | Trading at the wrong time | Use exchange timestamps, not local time |
| Corporate action | Position quantity changes overnight | Reconcile and handle explicitly |
Testing before real money
- 1
Unit test the strategy function
Feed it known data and assert the expected desired positions. Because it is pure, this is straightforward and fast.
- 2
Backtest through the same code path
Swap the data source and broker interface for historical versions. If backtest and live use different logic, you are testing something other than what you will run.
- 3
Test failure injection
Deliberately simulate timeouts, partial fills, rejections, stale data, and crashes. Confirm each produces the correct response rather than an exception.
- 4
Run in paper mode for weeks
Verify signal counts, order flow, and state handling. Compare against what the backtest would have done on the same days.
- 5
Run live at minimum size
Real fills, real rejections, real latency. Watch every cycle. Expect to find bugs that paper trading did not surface.
- 6
Add monitoring before scaling
Heartbeat, alerts, and reconciliation must be working before size increases, not after the first incident.
Frequently asked questions
What do I need before building a trading bot?
A strategy that has been backtested honestly, forward tested, and traded manually for long enough that every rule is unambiguous. Automation encodes rules; it cannot resolve ambiguity. Building a bot around an unfinished strategy means debugging two things at once.
Should I use an existing platform or build from scratch?
Platforms handle data, execution, and state management, which is most of the work, and are usually the right starting point. Building from scratch makes sense when you need behaviour the platform does not support, or when you require full control over execution. Most retail traders are better served by a platform plus custom strategy code.
How do I prevent my bot from placing duplicate orders?
Generate a unique client order ID for every intended order and include it on every submission and retry. Before retrying after a timeout, query the broker by that ID to determine whether the original order exists. This single practice prevents the most damaging category of automation bug.
Where should I host a trading bot?
A reliable cloud instance in a region near your broker, rather than a home machine subject to power and network interruptions. For strategies holding positions for minutes or longer, ordinary cloud hosting is entirely sufficient; colocation matters only for latency-sensitive approaches.
How do I know if my bot is working correctly?
Compare its signal count, entry prices, and trade sequence against what the backtest would have produced on the same days. Discrepancies indicate bugs. Monitor reconciliation matches, rejection rates, and slippage continuously; these reveal problems long before the equity curve does.
Test this idea before you trade it
Describe the rules in plain language and AlgoTrader AI turns them into a structured strategy blueprint with a configurable historical backtest, cost assumptions, and exportable code.
Build a backtestKeep reading
- Algo & QuantAlgorithmic Trading Guide: From Idea to Running System
- Algo & QuantPython for Trading: Tools, Structure, and Pitfalls
- Algo & QuantBroker APIs for Automated Trading: What to Check Before You Build
- BacktestingBacktesting Guide: How to Test a Strategy Honestly
- BacktestingForward Testing: The Only Truly Clean Evidence
- Algo & QuantExecution Algorithms: TWAP, VWAP, and Getting Filled Well
Referenced by
Educational use only. This guide explains how a strategy works. It is not investment advice, not a recommendation, and no result described here is a forecast. Test any approach on historical and out-of-sample data, size positions conservatively, and never risk money you cannot afford to lose.