P
PipsGrowth
← Back to Libraries

ib_insync for Interactive Brokers

A modern, Pythonic wrapper for the Interactive Brokers API. Features async/await support, event-driven programming, and a clean API for live trading and market data.

Difficulty: Intermediate
Category: Live Trading
🔗 IB Integration

Prerequisites

  • Interactive Brokers account (live or paper trading)
  • TWS (Trader Workstation) or IB Gateway installed and running
  • API connections enabled in TWS settings (Edit → Global Configuration → API)

Installation

# Install ib_insync
$ pip install ib_insync
# With Jupyter notebook support
$ pip install ib_insync nest_asyncio

Connection Settings

TWS (Trader Workstation)

  • Paper Trading: port 7497
  • Live Trading: port 7496

IB Gateway

  • Paper Trading: port 4002
  • Live Trading: port 4001

Key Features

Async/Await Support

Native async programming support for efficient handling of multiple data streams and orders.

Real-Time Data

Streaming market data, real-time bars, and tick-by-tick data with event callbacks.

Full Trading API

Complete order management: market, limit, stop, bracket orders, and complex order types.

Paper Trading

Test strategies with paper trading account using the same API as live trading.

Code Examples

Connect to Interactive Brokers

Establish connection to TWS or IB Gateway

Python
from ib_insync import IB, Stock, util
# Create IB instance
ib = IB()
# Connect to TWS or IB Gateway
# TWS: port 7497 (paper), 7496 (live)
# Gateway: port 4002 (paper), 4001 (live)
ib.connect('127.0.0.1', 7497, clientId=1)
print(f"Connected: {ib.isConnected()}")
# Get account info
account = ib.accountSummary()
for item in account:
if item.tag in ['TotalCashValue', 'NetLiquidation', 'AvailableFunds']:
print(f"{item.tag}: {item.value} {item.currency}")
# Always disconnect when done
ib.disconnect()

Get Real-Time Market Data

Stream live prices and historical data

Python
from ib_insync import IB, Forex, Stock, util
import asyncio
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Define contract
eurusd = Forex('EURUSD')
ib.qualifyContracts(eurusd)
# Get snapshot
ticker = ib.reqMktData(eurusd, '', False, False)
ib.sleep(2) # Wait for data
print(f"Bid: {ticker.bid}")
print(f"Ask: {ticker.ask}")
print(f"Last: {ticker.last}")
print(f"Volume: {ticker.volume}")
# Stream live data with callback
def on_tick(ticker):
print(f"Time: {ticker.time}, Bid: {ticker.bid}, Ask: {ticker.ask}")
ticker.updateEvent += on_tick
# Run for 10 seconds
ib.sleep(10)
# Cancel market data
ib.cancelMktData(eurusd)
ib.disconnect()

Download Historical Data

Get historical bars for backtesting

Python
from ib_insync import IB, Forex, util
import pandas as pd
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Define contract
eurusd = Forex('EURUSD')
ib.qualifyContracts(eurusd)
# Request historical bars
# Duration: 1 D, 1 W, 1 M, 1 Y
# Bar size: 1 min, 5 mins, 15 mins, 1 hour, 1 day
bars = ib.reqHistoricalData(
eurusd,
endDateTime='',
durationStr='30 D',
barSizeSetting='1 hour',
whatToShow='MIDPOINT',
useRTH=False,
formatDate=1
)
# Convert to DataFrame
df = util.df(bars)
print(df.tail())
# Calculate indicators
df['SMA_20'] = df['close'].rolling(20).mean()
df['Returns'] = df['close'].pct_change()
print(f"\nTotal bars: {len(df)}")
print(f"Date range: {df['date'].iloc[0]} to {df['date'].iloc[-1]}")
ib.disconnect()

Place Market and Limit Orders

Execute trades with different order types

Python
from ib_insync import IB, Forex, MarketOrder, LimitOrder, StopOrder, util
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Define contract
eurusd = Forex('EURUSD')
ib.qualifyContracts(eurusd)
# Market Order - Buy 20,000 units
market_order = MarketOrder('BUY', 20000)
trade = ib.placeOrder(eurusd, market_order)
ib.sleep(1)
print(f"Market Order Status: {trade.orderStatus.status}")
# Limit Order - Buy at specific price
ticker = ib.reqMktData(eurusd)
ib.sleep(1)
limit_price = ticker.bid - 0.0010 # 10 pips below bid
limit_order = LimitOrder('BUY', 20000, limit_price)
limit_trade = ib.placeOrder(eurusd, limit_order)
print(f"Limit Order placed at: {limit_price}")
# Stop Order
stop_price = ticker.ask + 0.0020
stop_order = StopOrder('BUY', 20000, stop_price)
stop_trade = ib.placeOrder(eurusd, stop_order)
print(f"Stop Order placed at: {stop_price}")
# Cancel pending orders
ib.cancelOrder(limit_order)
ib.cancelOrder(stop_order)
ib.disconnect()

Bracket Orders with SL/TP

Enter position with stop loss and take profit

Python
from ib_insync import IB, Forex, util
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
eurusd = Forex('EURUSD')
ib.qualifyContracts(eurusd)
# Get current price
ticker = ib.reqMktData(eurusd)
ib.sleep(1)
entry_price = ticker.ask
# Define SL/TP levels
stop_loss = entry_price - 0.0050 # 50 pips SL
take_profit = entry_price + 0.0100 # 100 pips TP
# Create bracket order
bracket = ib.bracketOrder(
action='BUY',
quantity=20000,
limitPrice=entry_price,
takeProfitPrice=take_profit,
stopLossPrice=stop_loss
)
# Place all orders
for order in bracket:
trade = ib.placeOrder(eurusd, order)
print(f"Placed: {order.orderType} @ {order.lmtPrice if hasattr(order, 'lmtPrice') else order.auxPrice}")
# Monitor fills
def on_fill(trade, fill):
print(f"Filled: {fill.execution.side} {fill.execution.shares} @ {fill.execution.avgPrice}")
for order in bracket:
trade = ib.trades()[-1]
trade.fillEvent += on_fill
ib.sleep(5)
ib.disconnect()

Monitor and Manage Positions

Track open positions and close trades

Python
from ib_insync import IB, Forex, MarketOrder
ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)
# Get all positions
positions = ib.positions()
print("Current Positions:")
for pos in positions:
print(f" {pos.contract.symbol}: {pos.position} @ {pos.avgCost:.5f}")
# Get open orders
orders = ib.openOrders()
print(f"\nOpen Orders: {len(orders)}")
for order in orders:
print(f" {order.action} {order.totalQuantity} {order.orderType}")
# Get P&L
pnl = ib.pnl()
for p in pnl:
print(f"\nP&L - Daily: {p.dailyPnL:.2f}, Unrealized: {p.unrealizedPnL:.2f}")
# Close specific position
eurusd = Forex('EURUSD')
ib.qualifyContracts(eurusd)
for pos in positions:
if pos.contract.symbol == 'EUR' and pos.position != 0:
# Close position
action = 'SELL' if pos.position > 0 else 'BUY'
quantity = abs(pos.position)
close_order = MarketOrder(action, quantity)
trade = ib.placeOrder(eurusd, close_order)
print(f"\nClosing position: {action} {quantity}")
ib.disconnect()

Async/Await Pattern

Use async programming for better performance

Python
from ib_insync import IB, Forex, util
import asyncio
async def main():
ib = IB()
await ib.connectAsync('127.0.0.1', 7497, clientId=1)
# Define contracts
pairs = [Forex(pair) for pair in ['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD']]
await ib.qualifyContractsAsync(*pairs)
# Request data for all pairs concurrently
async def get_price(contract):
ticker = ib.reqMktData(contract)
await asyncio.sleep(1)
return {
'symbol': contract.symbol + contract.currency,
'bid': ticker.bid,
'ask': ticker.ask,
'spread': (ticker.ask - ticker.bid) * 10000 if ticker.bid else None
}
# Gather all prices
tasks = [get_price(pair) for pair in pairs]
prices = await asyncio.gather(*tasks)
print("Currency Pair Prices:")
for p in prices:
if p['spread']:
print(f" {p['symbol']}: Bid={p['bid']:.5f}, Ask={p['ask']:.5f}, Spread={p['spread']:.1f} pips")
ib.disconnect()
# Run async main
if __name__ == '__main__':
util.startLoop()
asyncio.run(main())

Event-Driven Trading Bot

React to market events in real-time

Python
from ib_insync import IB, Forex, MarketOrder
import pandas as pd
class TradingBot:
def __init__(self):
self.ib = IB()
self.contract = None
self.prices = []
self.position = 0
self.sma_period = 20
def connect(self):
self.ib.connect('127.0.0.1', 7497, clientId=1)
self.contract = Forex('EURUSD')
self.ib.qualifyContracts(self.contract)
def on_bar_update(self, bars, has_new_bar):
"""Called when new bar data arrives."""
if not has_new_bar:
return
df = pd.DataFrame(bars, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
if len(df) < self.sma_period:
return
# Calculate signal
sma = df['close'].rolling(self.sma_period).mean().iloc[-1]
price = df['close'].iloc[-1]
# Trading logic
if price > sma and self.position <= 0:
self.place_order('BUY', 20000)
self.position = 1
elif price < sma and self.position >= 0:
self.place_order('SELL', 20000)
self.position = -1
def place_order(self, action, quantity):
order = MarketOrder(action, quantity)
trade = self.ib.placeOrder(self.contract, order)
print(f"Order placed: {action} {quantity}")
def run(self):
# Subscribe to real-time bars
bars = self.ib.reqRealTimeBars(
self.contract,
5, # 5-second bars
'MIDPOINT',
False
)
bars.updateEvent += self.on_bar_update
print("Bot running... Press Ctrl+C to stop")
self.ib.run()
def stop(self):
self.ib.disconnect()
# Run bot
bot = TradingBot()
bot.connect()
try:
bot.run()
except KeyboardInterrupt:
bot.stop()

Common Use Cases

Automated forex trading
Multi-asset portfolio management
Algorithmic execution
Real-time signal execution
Position monitoring
Risk management systems
Market data collection
Paper trading simulation
Order management
Account monitoring

Best Practices & Common Pitfalls

Always Qualify Contracts

Call ib.qualifyContracts() before using any contract to ensure proper resolution.

Use Paper Trading First

Test all strategies on paper account (port 7497) before switching to live (7496).

Handle Disconnections

Implement reconnection logic and state recovery for production trading systems.

Market Data Limitations

IB has limits on concurrent market data subscriptions. Cancel unused subscriptions.

Order ID Management

Use unique client IDs for each connection to avoid order ID conflicts.

Gateway vs TWS

IB Gateway is more stable for automated trading. TWS requires user interaction.

Additional Resources

Learning Resources

  • Notebook examples in GitHub repo
  • IB API webinars and tutorials
  • Quantopian community discussions

Next Steps

Explore other live trading options or build strategy components: