QUANT 3 min read

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.

Getting Started with Algorithmic Trading Using Interactive Brokers API

The Barrier from Backtesting to Live Trading

IBKR Algorithm Trading API

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.

Share X Telegram
#Interactive Brokers #IBKR #Algorithmic Trading #API #Python

Newsletter

Weekly Quant & Market Insights

Get market analysis, quant strategy ideas, and AI & data tool insights delivered to your inbox.

Subscribe →
More in this category QUANT →