blog · ~6 min read

Build a Python Trading Script With ccxt and vectorbt

A working Python trading script template combining ccxt for exchange data and vectorbt for fast vectorized backtests, with concrete code you can adapt.

T By tradernewbie · Curated for beginners
#algorithmic#quant-trading
Este artículo está en inglés. ¿Verlo en tu idioma? Google Translate →

Las herramientas interactivas pueden no funcionar en la vista traducida.

Build a Python Trading Script With ccxt and vectorbt

A single Python script can pull live market data, backtest a strategy in milliseconds, and route orders to an exchange. The combination of ccxt (data and execution) and vectorbt (vectorized backtesting) covers most retail crypto workflows without the overhead of an event-driven engine.

Why ccxt and vectorbt

ccxt unifies the APIs of over 100 exchanges behind one interface. One library call fetches OHLCV from Binance, Kraken, or Bybit with identical syntax. vectorbt runs backtests as array operations instead of bar-by-bar loops, so a 5-year parameter sweep finishes in seconds rather than minutes.

Pulling data with ccxt

import ccxt
ex = ccxt.binance()
ohlcv = ex.fetch_ohlcv("BTC/USDT", "1h", limit=1000)

Always respect rate limits. Binance allows 1200 requests per minute, but ccxt's built-in enableRateLimit: True throttles automatically. Set it explicitly to avoid bans.

Building the backtest in vectorbt

import vectorbt as vbt
import pandas as pd

df = pd.DataFrame(ohlcv, columns=["ts","open","high","low","close","volume"])
df["ts"] = pd.to_datetime(df["ts"], unit="ms")
df.set_index("ts", inplace=True)

fast = vbt.MA.run(df["close"], 20)
slow = vbt.MA.run(df["close"], 50)
entries = fast.ma_crossed_above(slow)
exits  = fast.ma_crossed_below(slow)

pf = vbt.Portfolio.from_signals(df["close"], entries, exits,
        init_cash=10000, fees=0.001, slippage=0.0005)
print(pf.total_return(), pf.sharpe_ratio())

Note the fees and slippage arguments: omitting them inflates returns by 30-60% on short holding periods.

From backtest to live order

ex = ccxt.binance({"apiKey": KEY, "secret": SECRET, "enableRateLimit": True})
ex.create_order("BTC/USDT", "market", "buy", 0.001)

Practical guardrails

  • Never hard-code keys; load them from environment variables.
  • Always paper-trade for 2 weeks before going live. Use testnet endpoints (ccxt.binance({"urls": ...})).
  • Validate the OHLCV timestamp alignment after every fetch — exchange gaps corrupt indicators silently.
  • Cap order size at 1% of equity and add a hard stop in the script, not just in your head.

When to switch to backtrader

vectorbt excels at fast sweeps over clean data. Once you need order-by-order fills, partial fills, or multi-asset portfolios with cross-margining, move to backtrader or QuantConnect Lean. Vectorized speed is worthless if the fill model is unrealistic.

The durable workflow: prototype in vectorbt, validate logic, then port to an event-driven engine for final production testing. Keep the live and backtest code paths sharing the same signal function so results stay comparable; divergence between them is the first sign of a silent bug. The script above takes you from idea to tested signal in under 50 lines.

Related market data, powered by TradingView.

Educational content · Not financial advice · Trade at your own risk