How to build an AI trading bot (with real code)
A practical, copy-pasteable walkthrough. We'll build the same SMA-crossover strategy you can test on our backtester — first in Python with ccxt, then in JavaScript — and wire in the risk management that actually keeps accounts alive.
Prerequisites
- Basic Python or JavaScript.
- An exchange account with API keys (use the testnet/sandbox first — never live keys while learning).
pip install ccxt pandasfor the Python path.
Build the backtester and risk manager before you connect live keys. The order you build in determines whether you learn cheaply or expensively.
1. Fetch market data
Every bot starts by pulling OHLCV (open, high, low, close, volume) candles. ccxt gives one API across 100+ crypto exchanges:
python · fetch_data.pyimport ccxt, pandas as pd
exchange = ccxt.binance() # or bybit(), kraken(), ...
ohlcv = exchange.fetch_ohlcv('BTC/USDT', timeframe='1d', limit=365)
df = pd.DataFrame(ohlcv, columns=['ts','open','high','low','close','volume'])
df['ts'] = pd.to_datetime(df['ts'], unit='ms')
print(df.tail())
2. Compute the signal
Here is the exact SMA-crossover logic our backtester uses: go long when the 20-period average rises above the 50-period average, flat otherwise.
python · signal.pydf['sma_fast'] = df['close'].rolling(20).mean()
df['sma_slow'] = df['close'].rolling(50).mean()
# position: 1 = long, 0 = flat. Shift(1) avoids look-ahead bias.
df['position'] = (df['sma_fast'] > df['sma_slow']).astype(int).shift(1).fillna(0)
That .shift(1) is critical. Without it your bot "decides" using a candle's close to trade during the same candle — impossible in reality, and it inflates backtest returns wildly. This single bug fools more beginners than any other.
3. Backtest before risking money
Replay history and compute equity, return and drawdown — fees included:
python · backtest.pyfee = 0.001 # 0.10% per side
df['ret'] = df['close'].pct_change().fillna(0)
df['turnover'] = df['position'].diff().abs().fillna(0)
df['pnl'] = df['position'] * df['ret'] - df['turnover'] * fee
df['equity'] = (1 + df['pnl']).cumprod()
total_return = (df['equity'].iloc[-1] - 1) * 100
max_dd = ((df['equity'] / df['equity'].cummax()) - 1).min() * 100
print(f"Return: {total_return:.1f}% Max DD: {max_dd:.1f}%")
This is exactly the math powering our in-browser backtester — try the same strategy there first to sanity-check your numbers.
4. Add risk management (the part that matters most)
Never size a trade by gut. Risk a fixed small fraction of the account per trade and let the stop define the size:
python · risk.pydef position_size(account, risk_pct, entry, stop):
"""Units to trade so hitting the stop loses exactly risk_pct."""
risk_amount = account * (risk_pct / 100)
per_unit = abs(entry - stop)
return 0 if per_unit == 0 else risk_amount / per_unit
# risk 1% of a $10k account, entry 42000, stop 40000 -> 0.05 BTC
units = position_size(10000, 1, 42000, 40000)
That's the same formula behind our position sizing calculator — open it side-by-side to verify your bot's sizing.
5. Paper trade, then go live (carefully)
- Run on the exchange testnet for weeks. Confirm orders, fills and edge cases behave.
- Add logging + alerts so you know the instant it breaks.
- Go live with the smallest possible size. Scale only after weeks of live results match the backtest.
python · live_order.pyexchange = ccxt.binance({'apiKey': KEY, 'secret': SECRET})
exchange.set_sandbox_mode(True) # TESTNET — always start here
if signal == 'buy':
exchange.create_market_buy_order('BTC/USDT', units)
JavaScript version (browser-style signal)
If you prefer JS, the signal logic is identical. This is the core of the engine running on this very site:
javascript · signal.jsfunction sma(closes, p, i) {
if (i < p - 1) return null;
let s = 0;
for (let k = i - p + 1; k <= i; k++) s += closes[k];
return s / p;
}
// position[i] = 1 when fast SMA > slow SMA, else 0
const pos = closes.map((_, i) => {
const f = sma(closes, 20, i), s = sma(closes, 50, i);
return (f && s && f > s) ? 1 : 0;
});
You now have all six layers: data, signal, backtest, risk, execution, monitoring. Swap the SMA rule for RSI, momentum or grid (see strategies explained) and re-run the backtest before each change.
Frequently asked questions
What language should I use to build a trading bot?
Python is the most popular for trading bots thanks to libraries like ccxt, pandas and backtesting frameworks. JavaScript/Node works well too, especially for browser tools and exchanges with good JS SDKs. Start with whichever you already know.
How long does it take to build a basic trading bot?
A working rule-based bot that fetches data, computes a signal and paper-trades can be built in a weekend. A robust, monitored, live-capital bot with proper risk controls takes weeks of testing.
Do I need machine learning to build an AI trading bot?
No. Most profitable retail bots use simple rule-based signals. Add machine learning only after you have a solid backtesting pipeline and understand overfitting — otherwise ML usually hurts.
Can I build a trading bot for free?
Yes. The libraries (ccxt, pandas), most exchange testnets and paper-trading sandboxes are free. You only need capital when you go live, and you should paper-trade for months first.