A build guide for trading bots. Stack choice, exchange connectivity, the execution loop, state persistence, and how market conditions enter as an input.
Most guides on how to build a trading bot stop at the strategy. They explain an entry rule, show a backtest, and leave the hard parts undocumented. The hard parts are connectivity, timing, state, and knowing what conditions your logic assumes. This guide covers the build itself.
If you want the conceptual layer first, what turns a set of rules into a system, start with how to build a trading system. This piece assumes you have decided to build and are choosing what to write.
Almost every working bot decomposes the same way. Naming the parts early makes the build tractable.
Builders tend to spend most of their time on strategy logic. In deployed systems, execution and state cause more failures than logic does.
Python is the default for good reasons. Library coverage is deep and the exchange clients are mature. The choice within Python matters more than the language choice.
CCXT is a unified client across exchanges. It handles authentication, order formats, and rate limits behind one interface. If you plan to run on more than one venue, this is the layer that saves you.
Freqtrade is a full bot framework with backtesting, optimization, and a Telegram interface built in. You write strategy classes rather than infrastructure. The tradeoff is working inside its structure.
Backtrader is a backtesting engine with live trading attached. It is well documented and event driven. Development has been quiet for some time, which matters for exchange API changes.
Vectorbt is built for fast vectorized testing across large parameter spaces. It is a research tool more than a runtime. Many builders test in vectorbt and deploy elsewhere.
QuantConnect is a hosted platform with its own data and execution. You trade infrastructure control for not maintaining any.
A reasonable default is CCXT for connectivity, your own loop, and a separate research environment for testing. Frameworks help most when your strategy fits their assumptions.
This is where most builds stall, and it is entirely unglamorous.
Generate API keys with the narrowest permissions the strategy needs. Withdrawal permission is almost never required. Bind keys to a static IP where the exchange supports it.
Use the testnet before live keys touch your code. Testnet behavior diverges from production on fills and liquidity. Treat it as a plumbing test rather than a performance test.
Read the rate limits and respect them in code, not by hope. Most exchanges weight endpoints differently, so a naive polling loop can exhaust your budget on one call type. Build backoff in from the first commit.
The loop is the part a backtest never simulates honestly.
Polling is simpler and sufficient for anything on a 15 minute candle or slower. Websockets matter when you act intra-candle or need fill confirmations quickly.
Decide explicitly whether you act on candle close or on live price. A backtest almost always assumes close. A live loop that fires mid-candle is running a different strategy from the one you tested. The two diverge most in exactly the conditions that matter.
Make every action idempotent. A restart, a duplicate webhook, or a retried request should not open a second position. Tag orders with a client order ID derived from the candle timestamp and the pair, then check before sending.
When results and expectations separate, the cause is often here rather than in the rules. Testing methodology is covered in crypto backtesting.
A bot that forgets on restart will eventually do damage. The state layer needs to persist at minimum:
SQLite is enough for a single instance. The requirement is that state is written before the action, not after. A crash between placing an order and recording it is the case that costs money.
A rule that works in one market state often loses in another. Mean reversion entries decay in strong trends. Breakout entries bleed in ranges. Most builders discover this through drawdown rather than design.
Market regime is the state a market is in, described rather than predicted. Trending, ranging, or bearish. It belongs in the bot as an input, not as a signal.
The structure that works is sequential. Check the regime first. Confirm direction second. Evaluate the signal last. Each gate can only block, never trigger. A signal that passes all three has survived conditions it would otherwise ignore.
Confirmation is the part worth building deliberately. A regime read taken from a single candle flips constantly around the threshold, producing a stream of state changes that are noise. Requiring the same reading across three consecutive 15 minute candles, a 45 minute confirmation window, removed that chatter entirely on our own historical data while preserving genuine changes. The cost is roughly 30 minutes of median lag behind the actual flip. That is the trade, stated plainly: you learn later, and what you learn is real.
You can compute this yourself from ADX and moving average structure. The threshold most systems use is ADX 25 for a defined trend, with anything at 31 or below treated as weak. If you would rather not maintain that calculation across every pair you trade, the RegimeLab API returns the current confirmed state for all tracked pairs in one read only call, which drops into the top of your loop as a gate.
Sizing is where an otherwise sound system quietly fails.
The failure is specific and common. Position size ends up correlated with signal confidence, and confidence ends up correlated with the conditions that perform worst. The result is a system with a positive average percentage return and a negative dollar return, because losses are carried at larger size than wins.
Check for this directly. Compare the average notional of your winning trades against your losing trades. If losers carry meaningfully more size, the sizing rule is working against you regardless of how good the entry logic looks.
Fixed fractional sizing is unglamorous and hard to beat. Any confidence weighting should be validated against realized dollar outcomes before it goes live, not against percentages.
A bot executes a rule set faster and more consistently than you would. That is the whole of the advantage.
It does not find edge. If the rules have no edge, automation produces losses more reliably.
It does not predict. Every indicator described here, including regime classification, is computed from prices that already happened. A confirmed trend describes the recent past and offers no guarantee about the next candle.
It does not remove judgment. It relocates judgment to the moment you wrote the rules, where it is harder to revise and easier to forget.
Automation is a discipline mechanism. Treat it as one.
Python is the practical default. Library coverage is deep, exchange clients are mature, and most research tooling assumes it. Language choice matters less than the choice of framework within it.
Frameworks like Freqtrade save infrastructure work but constrain structure. Writing your own loop with CCXT for connectivity gives more control and is a reasonable default for a first build.
Enough that exchange minimum order sizes do not distort your position sizing. Below that threshold, rounding effects change your realized results relative to any test.
The most common causes are execution timing, fees, and slippage rather than the rules. A loop acting intra-candle is running a different strategy from one tested on candle close.
Scheduled runs aligned to candle close are simpler and sufficient for timeframes of 15 minutes and above. Continuous operation matters when you act intra-candle.
It is a check on market state placed before your entry logic. It cannot generate entries. It can prevent a rule from firing in conditions where it historically performs poorly.
Monitor for staleness rather than errors alone. A process that runs without crashing while its data feed has gone stale is the failure mode that goes unnoticed longest.