IBKR Trade AI
Disclaimer
This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
Trading stocks and other financial instruments carries a high level of risk, and may not be suitable for all investors. Before deciding to trade, you should carefully consider your investment objectives, level of experience, and risk appetite.
A lightweight, asyncio-based trading library for Interactive Brokers (IBKR) with built-in trade tracking, order management, and persistent caching.
Features
- Async-first design: Built on
asynciofor high-performance concurrent operations - Persistent trade tracking: Uses
diskcachefor reliable trade state persistence - Type-safe: Fully typed with comprehensive type hints for better IDE support
- Thread-safe: Built-in thread validation decorators for multi-threaded environments
- Order management: Support for market and limit orders with automatic order ID generation
- Real-time updates: Event-driven architecture for order status and execution updates
- Overnight trading: Built-in support for regular and overnight trading hours
- Flexible callbacks: Register async callbacks for trade events
Installation
From PyPI (when published)
pip install ibkr-trade-ai
From Source
git clone https://github.com/osjayaprakash/ibkr-trade-ai.git
cd ibkr-trade-ai
pip install -e .
Development Installation
pip install -e ".[dev]"
Quick Start
Basic Usage
import asyncio
from diskcache import Cache
from bolt2 import TradeDataManager
async def main():
# Initialize cache for trade persistence
cache = Cache('./trade_cache')
# Create trade manager
manager = TradeDataManager(cache)
# Place a market order
await manager.placeOrderAsync(
sym='AAPL',
lmtPrice=None, # None for market orders
qty=10,
action='BUY',
slot=None
)
# Run the event loop
await manager.runLoop()
if __name__ == '__main__':
asyncio.run(main())
Placing Limit Orders
# Place a limit order
await manager.placeOrderAsync(
sym='TSLA',
lmtPrice=250.50, # Limit price
qty=5,
action='SELL',
slot='portfolio_1'
)
Registering Callbacks
async def on_trade_update(trade):
print(f"Trade updated: {trade.symbol} - {trade.status}")
# Your custom logic here
# Register the callback
manager.callbacks.append(on_trade_update)
Architecture
Core Components
TradeDataManager
The main orchestrator that handles:
- Order placement and validation
- Event loop management
- Trade state updates
- Callback execution
Trade
Represents a single trade with:
- Order details (symbol, quantity, price)
- Execution information
- State tracking (Submitted, Filled, Cancelled, etc.)
- Persistent caching
TradeWrapper
IBKR API wrapper that:
- Manages connection to Interactive Brokers TWS/Gateway
- Handles API callbacks
- Queues events for processing
API Reference
TradeDataManager
__init__(cache: Cache, timeout: int, clientId: int)
Initialize the trade manager with a cache instance, timeout, and client ID.
Parameters:
cache(Cache): diskcache.Cache instance for trade persistence
async placeOrderAsync(sym: str, lmtPrice: Optional[float], qty: int, action: str, slot: Optional[str]) -> None
Place an order asynchronously.
Parameters:
sym(str): Stock symbollmtPrice(Optional[float]): Limit price, or None for market ordersqty(int): Quantity to trade (must be >= 1)action(str): 'BUY' or 'SELL'slot(Optional[str]): Optional identifier for grouping trades
Raises:
ValueError: If action is not 'BUY' or 'SELL', qty < 1, or invalid limit priceRuntimeError: If unable to get next valid order ID
Example:
# Market order
await manager.placeOrderAsync('AAPL', None, 100, 'BUY', None)
# Limit order
await manager.placeOrderAsync('MSFT', 380.50, 50, 'SELL', 'tech_portfolio')
async runLoop() -> None
Run the main event processing loop. Processes events from the queue and executes callbacks.
Example:
# Run in background task
asyncio.create_task(manager.runLoop())
callbacks: list[Callable[[Trade], Awaitable[Any]]]
List of async callbacks to execute on trade updates.
Example:
async def log_trade(trade):
print(f"Trade update: {trade}")
manager.callbacks.append(log_trade)
Trade States
The library uses the following trade states:
| State | Description |
|---|---|
PendingSubmit |
Order is being prepared for submission |
PendingCancel |
Order cancellation is pending |
PreSubmitted |
Order is pre-submitted to IBKR |
Submitted |
Order is active on the exchange |
Filled |
Order has been completely filled |
Cancelled |
Order has been cancelled |
Inactive |
Order is inactive |
CustomSubmitInitiated |
Custom state for initial submission |
CustomCancelInitiated |
Custom state for cancel initiation |
DirtyCancelled |
Order was cancelled but no confirmation received |
Trading Hours
Regular Trading Hours
9:30 AM - 4:00 PM EST
from bolt2 import isRegularTradingHourMin
if isRegularTradingHourMin():
# Place regular hours trades
pass
Overnight Trading Hours
8:00 PM - 3:50 AM EST
from bolt2 import isOvernightTradingHourMin
if isOvernightTradingHourMin():
# Place overnight trades (requires limit orders)
pass
Configuration
Cache Setup
The library uses diskcache for persistent storage:
from diskcache import Cache
# Basic cache
cache = Cache('./trade_cache')
# Cache with custom settings
cache = Cache(
directory='./trade_cache',
size_limit=500 * 1024 * 1024, # 500 MB
eviction_policy='least-recently-used'
)
Logging
Configure logging for debugging:
from bolt2.utils import setupRootLogger
# Setup with default log file
setupRootLogger()
# Custom log file
setupRootLogger('./logs/trading.log')
Thread Safety
The library includes thread validation decorators:
from bolt2.utils import setUserThread, setApiThread, inUserThread, inApiThread
import threading
# Set thread IDs
setUserThread(threading.get_ident())
# Decorate functions that must run in specific threads
@inUserThread
def user_thread_function():
pass
Best Practices
1. Always Use Async Context
async def trade_workflow():
cache = Cache('./cache')
manager = TradeDataManager(cache, timeout=30, clientId=1)
try:
await manager.placeOrderAsync('AAPL', None, 10, 'BUY', None)
await manager.runDataManagerLoop()
except Exception as e:
logger.error(f"Trading error: {e}")
2. Handle Errors Gracefully
try:
await manager.placeOrderAsync('INVALID', None, -5, 'BUY', None)
except ValueError as e:
print(f"Invalid order parameters: {e}")
except RuntimeError as e:
print(f"Runtime error: {e}")
3. Use Callbacks for Trade Monitoring
async def monitor_trade(trade):
if trade.status == TradeState.Filled.value:
await send_notification(f"Order filled: {trade.symbol}")
elif trade.status == TradeState.Cancelled.value:
await log_cancelled_order(trade)
manager.callbacks.append(monitor_trade)
4. Validate Trading Hours
from bolt2 import isRegularTradingHourMin, isOvernightTradingHourMin
if isRegularTradingHourMin():
# Market orders OK
await manager.placeOrderAsync('AAPL', None, 10, 'BUY', None)
elif isOvernightTradingHourMin():
# Must use limit orders
await manager.placeOrderAsync('AAPL', 180.50, 10, 'BUY', None)
else:
print("Market is closed")
Development
Setup Development Environment
# Clone repository
git clone https://github.com/osjayaprakash/ibkr-trade-ai.git
cd ibkr-trade-ai
# Install with dev dependencies
pip install -e ".[dev]"
# Run type checking
mypy bolt2
# Run tests
pytest
# Format code
black bolt2
ruff check bolt2
Project Structure
ibkr-trade-ai/
├── bolt2/ # Main package
│ ├── __init__.py # Public API exports
│ ├── manager.py # TradeDataManager class
│ ├── trade.py # Trade class and utilities
│ ├── utils.py # Utility functions and enums
│ ├── wrapper.py # IBKR API wrapper
│ └── py.typed # PEP 561 type marker
├── .github/
│ └── workflows/
│ └── publish-to-test-pypi.yml # CI/CD pipeline
├── tests/ # Test suite (if available)
├── pyproject.toml # Project configuration
├── README.md # This file
└── LICENSE # MIT License
Publishing & CI/CD Setup
This project uses GitHub Actions for automated testing and publishing to PyPI.
Automated Workflow Features
The CI/CD pipeline automatically:
- ✅ Tests on Python 3.9, 3.10, 3.11, 3.12, 3.13
- ✅ Runs type checking (mypy)
- ✅ Runs linting (ruff)
- ✅ Checks code formatting (black)
- ✅ Validates version consistency across files
- ✅ Builds and verifies package distributions
- ✅ Tests package installation
- ✅ Publishes to TestPyPI for release candidates (
v*-rc*tags) - ✅ Publishes to PyPI for stable releases (
v*tags) - ✅ Creates GitHub releases with auto-generated notes
Initial Setup (One-Time)
1. Create GitHub Environments
Go to Settings → Environments and create:
-
pypi- Production PyPI environment- Optional: Add 5-minute wait timer (gives review time)
- Optional: Set deployment branch to
mainonly
-
testpypi- Testing PyPI environment- No restrictions needed
2. Configure PyPI Trusted Publishing (Recommended)
This is more secure than using API tokens.
For PyPI (Production):
- Go to https://pypi.org/manage/account/publishing/
- Click "Add a new pending publisher"
- Fill in:
- PyPI Project Name:
ibkr-trade-ai - Owner:
osjayaprakash(your GitHub username) - Repository name:
ibkr-trade-ai - Workflow name:
publish-to-test-pypi.yml - Environment name:
pypi
- PyPI Project Name:
- Save
For TestPyPI (Testing):
- Go to https://test.pypi.org/manage/account/publishing/
- Repeat the same process with environment name:
testpypi
Alternative: Using API Tokens
If you prefer API tokens instead of trusted publishing:
- Generate tokens at https://pypi.org/manage/account/token/
- Add to GitHub: Settings → Secrets and variables → Actions
PYPI_API_TOKEN- PyPI tokenTEST_PYPI_API_TOKEN- TestPyPI token
- Update workflow to use
password: ${{ secrets.PYPI_API_TOKEN }}
3. (Optional) Branch Protection
Protect the main branch:
Settings → Branches → Add rule for main:
- ✅ Require status checks to pass:
test (Python 3.9)test (Python 3.10)test (Python 3.11)test (Python 3.12)test (Python 3.13)build
- ✅ Require branches to be up to date
Release Process
For Release Candidates (Testing on TestPyPI):
# 1. Update version to include 'rc' suffix in both:
# - pyproject.toml: version = "0.1.0-rc1"
# - bolt2/__init__.py: __version__ = "0.1.0-rc1"
# 2. Commit and tag
git add .
git commit -m "Prepare release 0.1.0-rc1"
git tag v0.1.0-rc1
git push origin main --tags
# 3. Workflow automatically:
# - Runs all tests
# - Builds package
# - Publishes to TestPyPI (because tag contains 'rc')
# 4. Test installation from TestPyPI
pip install -i https://test.pypi.org/simple/ ibkr-trade-ai==0.1.0rc1
# 5. Test your package
python -c "from bolt2 import TradeDataManager; print('Success!')"
For Stable Releases (Publishing to PyPI):
# 1. Update version (no 'rc' suffix) in both:
# - pyproject.toml: version = "0.1.0"
# - bolt2/__init__.py: __version__ = "0.1.0"
# 2. Commit and tag
git add .
git commit -m "Release 0.1.0"
git tag v0.1.0
git push origin main --tags
# 3. Workflow automatically:
# - Runs all tests
# - Validates version consistency
# - Builds package
# - Publishes to PyPI (no 'rc' in tag)
# - Creates GitHub Release with artifacts
# 4. Install from PyPI
pip install ibkr-trade-ai
# 5. Verify installation
python -c "from bolt2 import TradeDataManager; print('Success!')"
Version Management
Important: Keep versions synchronized across:
pyproject.toml- Line 7:version = "0.1.0"bolt2/__init__.py- Line 28:__version__ = "0.1.0"
The CI pipeline will fail if versions don't match the git tag.
Version Format:
- Stable releases:
v0.1.0,v1.0.0,v2.1.3 - Release candidates:
v0.1.0-rc1,v1.0.0-rc2 - Alpha/Beta:
v0.1.0-alpha1,v0.1.0-beta1
Workflow Triggers
The workflow runs on:
- ✅ Pushes to
mainbranch (tests only) - ✅ Pull requests to
main(tests only) - ✅ Version tags
v*(tests + publish) - ✅ Manual trigger via GitHub Actions UI
Troubleshooting
"Version mismatch" error:
- Check that tag, pyproject.toml, and init.py all have the same version
- Tag should be
v0.1.0for version0.1.0
"Trusted publishing failed":
- Verify you created the pending publisher on PyPI/TestPyPI
- Check that repository name, owner, and workflow name match exactly
- Wait a few minutes after creating the publisher
Tests failing:
- Check the Actions tab for detailed error messages
- Run tests locally:
pytest tests/ - Run type checking:
mypy bolt2
Package not found on PyPI:
- Check the Actions tab to see if publish step succeeded
- Wait a few minutes for PyPI to index the package
- Verify you're using the correct package name:
ibkr-trade-ai
Requirements
- Python 3.9+
- diskcache
- ibapi-latest (Interactive Brokers API)
- pytz
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Guidelines
- Follow the existing code style (black, mypy)
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built on top of Interactive Brokers API
- Inspired by the need for a modern, async-first trading library
- Thanks to the Python async community
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Roadmap
- Add comprehensive test suite
- Support for options trading
- Portfolio management features
- Real-time market data integration
- Backtesting framework
- Web dashboard
- Enhanced order types (stop-loss, trailing stop, etc.)
Made with ❤️ by Jayaprakash Sundararaj
Release files for ibkr-trade-ai 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ibkr_trade_ai-0.1.3.tar.gz | 29.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ibkr_trade_ai-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.8 kB
Release files / ibkr_trade_ai-0.1.3.tar.gz
| Download URL | ibkr_trade_ai-0.1.3.tar.gz |
|---|---|
| Size | 29.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4b808c616060fc60b0333cd8b5e67c3aaa12d078509b30fe26bf86dcde426adc
|
|
BLAKE2b-256 checksum How to use checksums |
2ae19f6ccd4b9c8eed81b7e190e2e14f17fcc4666187533cb363c62cb53b5b37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 26, 2025.
Transparency logRelease files / ibkr_trade_ai-0.1.3-py3-none-any.whl
| Download URL | ibkr_trade_ai-0.1.3-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
af66cf5b4eb90ec7b2cd0f9fd31a421987acbe3ba242217a8f3ebc1e7ebd66d5
|
|
BLAKE2b-256 checksum How to use checksums |
7e1d0a17fdd8e2b999e32c1b11b2fa95c6ad71ab5e59dd3d8cd0d46c5d9cbb05
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 26, 2025.
Transparency log