Getting Started with Algorithmic Trading Using Interactive Brokers API
To go beyond backtesting and connect to live trading, a broker API is essential. This guide explains how to automate orders using IBKR TWS API and the ib_insync library.
The Barrier from Backtesting to Live Trading

It’s common to hear that backtesting works well, but connecting to live trading is challenging. There are mainly two reasons for this:
First, the learning curve of the broker API. Each broker has different API structures, and initial setup can be complicated.
Second, real-time data processing. Backtests use static datasets, but live trading requires streaming data.
Interactive Brokers (IBKR) has a high entry barrier. However, once mastered, the same API handles stocks, futures, options, forex, and bonds, making it highly practical for quants.
Why Choose IBKR
Fees: Around $0.005 per share for US stocks (minimum $1). Few brokers can match this price.
API Support: Both TWS API (Python, Java, C++) and REST API (via IBeam) are supported, with comprehensive documentation.
Asset Classes: Trade stocks, futures, options, forex, ETFs, bonds across global exchanges—all within a single account.
Drawbacks: You need to keep TWS (Trader Workstation) running continuously. For REST API, an IBeam gateway is required.
Opening an Account
Both individual and corporate accounts are available. Residents of Korea can also open accounts, completed entirely online.
Minimum deposits:
- Standard Account: recommended $10,000 or more (below which monthly fees may apply)
- Retirement Account (IRA): none
- Paper Trading Account: completely free (uses the same API environment as live trading)
Important: Always start with a paper trading account. It provides an environment identical to live trading, allowing thorough testing of your code.
Setting Up the Development Environment
Use ib_insync, which offers a more Pythonic interface compared to the official ibapi and is based on asyncio for real-time processing.
pip install ib_insync
Ensure TWS or IBeam Gateway is running and connected.
Basic Connection and Data Retrieval
from ib_insync import *
# Connect to TWS (for paper trading port 7497)
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Define BTC futures contract
btc = Crypto('BTC', 'PAXOS', 'USD')
ib.qualifyContracts(btc)
# Get the current price
ticker = ib.reqMktData(btc)
ib.sleep(2)
print(f"BTC current price: {ticker.marketPrice()}")
# Retrieve historical data (1-hour bars for 30 days)
bars = ib.reqHistoricalData(
btc,
endDateTime='',
durationStr='30 D',
barSizeSetting='1 hour',
whatToShow='MIDPOINT',
useRTH=True
)
import pandas as pd
df = util.df(bars)
print(df.tail())
ib.disconnect()
Executing Orders
from ib_insync import *
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Define SPY ETF contract
spy = Stock('SPY', 'SMART', 'USD')
ib.qualifyContracts(spy)
# Market buy order for 10 shares
order = MarketOrder('BUY', 10)
trade = ib.placeOrder(spy, order)
# Wait for completion
ib.waitOnUpdate(timeout=10)
print(f"Order status: {trade.orderStatus.status}")
print(f"Average fill price: {trade.orderStatus.avgFillPrice}")
ib.disconnect()
Test with paper trading account first to validate your code under realistic conditions.
Automating Strategy Execution
Trading strategies are event-driven. Here’s an example of a simple moving average crossover strategy:
from ib_insync import *
class MovingAverageStrategy:
def __init__(self, symbol: str, fast: int = 20, slow: int = 50):
self.ib = IB()
self.contract = Stock(symbol, 'SMART', 'USD')
self.fast = fast
self.slow = slow
self.position = 0
def on_bar_update(self, bars, has_new_bar: bool):
if not has_new_bar:
return
closes = [b.close for b in bars]
fast_ma = sum(closes[-self.fast:]) / self.fast
slow_ma = sum(closes[-self.slow:]) / self.slow
# Golden cross → buy
if fast_ma > slow_ma and self.position <= 0:
self.ib.placeOrder(self.contract, MarketOrder('BUY', 100))
self.position = 100
# Death cross → sell
elif fast_ma < slow_ma and self.position > 0:
self.ib.placeOrder(self.contract, MarketOrder('SELL', 100))
self.position = 0
def run(self):
self.ib.connect('127.0.0.1', 7497, clientId=1)
self.ib.qualifyContracts(self.contract)
bars = self.ib.reqRealTimeBars(
self.contract, 5, 'MIDPOINT', False
)
bars.updateEvent += self.on_bar_update
self.ib.run() # start event loop
# Usage
strategy = MovingAverageStrategy('AAPL')
strategy.run()
Cautions and Considerations
Latency: IBKR API can handle tens of orders per second but is not suitable for high-frequency strategies. Use it for strategies with minute or longer time frames.
Maintaining TWS Connection: TWS can be unstable if run continuously for long periods. Using IBeam (Docker-based gateway) provides more stability.
Error Handling: In live trading, handle disconnects, order rejections, and data delays robustly. Thoroughly test these in your paper account.
Conclusion
IBKR has a high entry barrier but remains one of the most practical brokers for quants, thanks to low fees and broad asset coverage. ib_insync offers a much more user-friendly Python interface, and paper trading allows comprehensive testing.
Even if backtest results are promising, actual trading success depends on robust validation and stress testing before going live.
When opening an IBKR account, using referral link can provide new account benefits. Referrers may also receive a small reward.
Related Posts
Newsletter
Weekly Quant & Market Insights
Get market analysis, quant strategy ideas, and AI & data tool insights delivered to your inbox.
Subscribe →