A gRPC-based service that provides a modern RPC interface to Interactive Brokers TWS/Gateway API.
- gRPC interface with protobuf serialization
- Timezone-aware datetime formatting utilities
- Connection management to TWS/Gateway
- Contract details lookup with full security identifiers
- Historical data retrieval with ISO 8601 formatted timestamps
- Historical tick data (TRADES, BID_ASK, MIDPOINT)
- Option chain retrieval (expirations and strikes)
- Wall Street Horizon corporate events (earnings, dividends, splits, etc.)
- Watchlist export parsing (read TWS watchlist from export.csv)
- Portfolio positions, open orders, executions, and completed orders
- Market scanner functionality
- Built-in IB API rate limiting (pacing) at the service level
Client Apps -> gRPC -> IBAgent Service -> ibapi -> TWS/Gateway
The install script creates a virtualenv, downloads the IB API, and installs dependencies:
python install.pyFor development, you can install manually:
# Copy IB API Python source to project root
cp -r /path/to/TWS-API/source/pythonclient/ibapi ./ibapi
# Install in editable mode
pip install -e .Make sure TWS or IB Gateway is running and API connections are enabled:
- TWS Paper Trading: Port 7497
- TWS Live Trading: Port 7496
- Gateway Paper Trading: Port 4002
- Gateway Live Trading: Port 4001
Global Configuration > API > Settings has a 'Read-Only API' checkbox. Leaving it checked is recommended: every RPC in this service is a read, and read-only mode makes it impossible for a bug to place or cancel an order.
One request is affected. TWS rejects reqCompletedOrders in read-only mode
with error 321 and pops a dialog, so GetCompletedOrders only works with the
checkbox cleared. Use GetOpenOrders and GetExecutions instead -- both are
served in read-only mode and together cover working orders and fills.
./start.sh# Run all tests
pytest
# Run with coverage
pytest --cov=app --cov-report=htmlSee the examples/ directory for Python client examples.
Set environment variables to customize the service:
# gRPC server settings
export GRPC_HOST=0.0.0.0
export GRPC_PORT=5005
export GRPC_MAX_WORKERS=10
# TWS/Gateway connection defaults
export TWS_HOST=127.0.0.1
export TWS_PORT=7497
export TWS_CLIENT_ID=1
# Logging configuration
export IBAPI_LOG_LEVEL=WARNING # Options: DEBUG, INFO, WARNING, ERROR (default: WARNING)Alternatively, edit settings.ini:
[grpc]
host = 127.0.0.1
port = 5005
max_workers = 10
[tws]
host = 127.0.0.1
port = 7497
client_id = 1
[logging]
path = {home}/logs
filename = twsagent.log
[watchlist]
export_path = {home}/Jts/export.csv
[pacing]
historical_max_requests = 50
historical_window_seconds = 600
identical_gap_seconds = 15
general_min_interval_seconds = 1.0Note: Setting IBAPI_LOG_LEVEL=WARNING (default) suppresses verbose debug messages from the IB API library.
The service enforces IB API pacing rules at the service level to prevent violations:
- Historical data: Max 50 requests per 10-minute sliding window, plus a 15-second gap between identical requests
- General requests: Minimum 1-second interval between any IB API calls
These limits are configurable via the [pacing] section in settings.ini or environment variables (PACING_HIST_MAX, PACING_HIST_WINDOW, PACING_IDENTICAL_GAP, PACING_GENERAL_INTERVAL).
The service can read TWS watchlist data from the exported CSV file:
- In TWS, export your watchlist using File > Export > Export Data
- The default export location is
~/Jts/export.csv - Configure the path in
settings.iniif you use a different location - Call the
GetWatchlistRPC method to retrieve the parsed watchlist
The parser supports all contract types: stocks (STK), options (OPT), futures (FUT), forex (CASH), crypto (CRYPTO), and indices (IND).
python examples/download_bars.py TQQQ
python examples/download_bars.py TQQQ --bar-size 5s --days 2
python examples/download_bars.py TQQQ --bar-size 1d --years 2
python examples/download_bars.py TQQQ --extended -o bars.jsonpython examples/download_trades.py TQQQ --ts 1772205420 --duration 60
python examples/download_trades.py TQQQ --ts 1772160000 --duration 86400
python examples/download_trades.py TQQQ --ts 1772160000 --duration 86400 --extended -o trades.jsonpython examples/download_contracts.py AAPL
python examples/download_contracts.py AAPL MSFT GOOGL
python examples/download_contracts.py --file symbols.txt -o securities.jsonpython examples/download_options.py AAPL
python examples/download_options.py AAPL TSLA SPY
python examples/download_options.py ES --sec-type FUTRequires a Wall Street Horizon subscription.
python examples/download_events.py AAPL
python examples/download_events.py AAPL --filter Earnings
python examples/download_events.py AAPL --start 20240101 --end 20241231python examples/download_positions.py
python examples/download_positions.py -o positions.json# Working orders, including ones placed manually in TWS
python examples/download_orders.py open
python examples/download_orders.py open --client-only
# Trade fills. TWS serves the last 24 hours by default; --last-n-days and
# --date need TWS 10.30+, and neither reaches back more than a few days.
python examples/download_orders.py executions
python examples/download_orders.py executions --last-n-days 7 --format summary
python examples/download_orders.py executions --symbol AAPL -o fills.json
# Terminal-state orders. Requires 'Read-Only API' to be unchecked in TWS.
python examples/download_orders.py completed --api-onlyFinished-order history, served from a file on disk the same way the watchlist is. Nothing here touches TWS, so it works with TWS closed and is unaffected by read-only mode.
Create an Activity Flex query in Client Portal (Performance & Reports > Flex
Queries, not Statements) with the Trades section enabled, 'Level of Detail' set
to Order, and format CSV. Run it, download the file, and save it to the path in
settings.ini ([flex] statement_path, default ~/Jts/trades.csv). The date
range lives in the query definition. Refresh the file for newer history.
python examples/download_trade_history.py -o trades.csv
python examples/download_trade_history.py --symbol AAPL --format summary
python examples/download_trade_history.py --statement-path ~/Downloads/flex.csvSet [flex] timezone. IBKR omits the UTC offset from the export, so
timestamps cannot be resolved without knowing the query's timezone. Left blank,
the service returns naive timestamps rather than guessing; set it to match the
query (Client Portal > Flex Queries > Date/Time), e.g. America/New_York.
GetTradeHistory and GetExecutions share field names, units, and
vocabularies so historical and live rows can land in one table: account,
con_id, symbol, sec_type, currency, exchange, side (BUY/SELL),
quantity (positive), price, executed_at (ISO 8601), commission,
commission_currency, realized_pnl.
At Order level IBKR leaves trade_id blank -- use ib_order_id as the dedup
key.
python examples/download_watchlist.py
python examples/download_watchlist.py --export-path ~/Jts/export.csv
python examples/download_watchlist.py -o watchlist.jsonpython examples/run_scanner.py --list-scans
python examples/run_scanner.py --scan TOP_PERC_GAIN --location STK.US.MAJOR --rows 20
python examples/run_scanner.py --scan MOST_ACTIVE --above-price 10IMPORTANT: The files stubs/tws_pb2.py and stubs/tws_pb2_grpc.py are auto-generated. Never edit them manually.
When you modify proto/tws.proto, regenerate the Python code:
pip install grpcio-tools protobuf
mkdir -p stubs
touch stubs/__init__.py
python3 -m grpc_tools.protoc \
--proto_path=./proto \
--python_out=./stubs \
--grpc_python_out=./stubs \
proto/tws.protoRestart your server to use the new generated code.
MIT