Interactive Brokers Market Data¶
This tutorial covers the Interactive Brokers integration in chronos-lab for real-time and historical market data.
What You'll Learn¶
- Connecting to IB Gateway/TWS and managing connections
- Understanding IB Contracts and why they matter
- Fetching historical data (non-streaming)
- Subscribing to streaming bar data
- Working with real-time tick data
- Direct access to IB API functions
Prerequisites¶
Software Requirements¶
IB Gateway or TWS: You must have Interactive Brokers Gateway or Trader Workstation installed and running
- Download from: Interactive Brokers
- Configure API access in Gateway/TWS settings
chronos-lab with IB support:
!pip install chronos-lab[ib,arcticdb,visualization]
Configuration¶
IB connection parameters are configured in ~/.chronos_lab/.env. Key settings include:
IB_GATEWAY_HOST=127.0.0.1 # Gateway/TWS hostname
IB_GATEWAY_PORT=4001 # Port (4001 for Gateway)
IB_GATEWAY_READONLY=True # Read-only mode (recommended)
IB_GATEWAY_CLIENT_ID=1 # Unique client ID
IB_REF_DATA_CONCURRENCY=10 # Max concurrent reference data requests
IB_HISTORICAL_DATA_CONCURRENCY=10 # Max concurrent historical data requests
Read-Only Mode: The IB module focuses on market data, not trading. We recommend using IB_GATEWAY_READONLY=True (the default). While ib_async can access portfolio and account data, that's outside the scope of this module.
For complete IB Gateway setup instructions, see the IB API documentation.
Understanding the IB Integration¶
Architecture¶
The chronos-lab IB module is built on top of ib_async (formerly ib_insync), which provides a Pythonic wrapper around the Interactive Brokers API. Key characteristics:
Stateful Connection: Unlike Yahoo Finance or Intrinio, the IB module maintains a persistent connection
- Requires explicit initialization:
get_ib() - Connection lifecycle: connect → use → disconnect
- Subscriptions persist until cancelled
- Requires explicit initialization:
Singleton Pattern: A single connection instance is shared across your application
- Prevents duplicate connections
- Manages connection state globally
Async-First Design: Maximum benefits come from using async functions
- Concurrent contract qualification and data fetching
- Semaphore-based rate limiting to avoid API violations
- Parallel operations across multiple symbols
What's Available¶
The IB module provides:
- Historical data: One-time fetches via
ohlcv_from_ib()orget_hist_data() - Streaming bars: Real-time bar updates via
subscribe_bars() - Real-time ticks: Bid/ask/last updates via
sub_tickers() - Direct API access: Advanced features via
ib.conn(market depth, tick-by-tick, etc.)
What's Not Covered¶
The IB API offers many additional market data functions that don't have specific chronos-lab wrappers:
reqHistoricalTicks- Tick-level historical datareqTickByTickData- Ultra-granular real-time ticksreqMktDepth- Market depth (Level 2) datareqMktDepthExchanges- Available depth exchanges
These can be accessed directly via ib.conn. See the ib-async documentation for examples.
Understanding IB Contracts¶
What is a Contract?¶
In the IB API, a Contract is the fundamental way to identify a security. Unlike simple ticker symbols, contracts contain:
symbol: The ticker symbol (e.g., "AAPL")secType: Security type ("STK" for stocks, "CASH" for forex, "FUT" for futures, etc.)exchange: Exchange code ("SMART" for smart routing, "NYSE", "NASDAQ", etc.)currency: Currency denomination ("USD", "EUR", etc.)conId: Contract ID - IB's unique identifier (obtained after qualification)
Why Contracts Matter¶
Symbol ambiguity problem: A ticker symbol like "AA" can refer to multiple securities:
- Alcoa Corp on NYSE
- AA PLC on London Stock Exchange
- Different share classes of the same company
Symbol-to-contract lookup is non-deterministic, especially for:
- International securities
- Multiple share classes
- Options and futures
Best Practice: While many chronos-lab functions accept symbols (for convenience), it's highly advisable to work with contracts when you need precision.
Contract Lifecycle¶
1. Create → Contract(secType='STK', symbol='AAPL', exchange='SMART', currency='USD')
2. Qualify → ib.symbols_to_contracts() or ib.conn.qualifyContracts()
3. Get Details → ib.lookup_cds() [optional, for additional metadata]
After qualification, the contract has a conId and can be used with IB API functions.
See the Appendix at the end of this notebook for detailed contract examples.
Setup: Connection & Initialization¶
Let's start by importing the necessary modules and establishing a connection to IB Gateway/TWS.
from chronos_lab.ib import get_ib, hist_to_ohlcv
from chronos_lab.sources import ohlcv_from_ib_async, ohlcv_from_ib
from chronos_lab.storage import ohlcv_to_arcticdb
from ib_async import util, Contract
import pandas as pd
Starting the Event Loop¶
When using ib_async in Jupyter notebooks, we need to start the event loop explicitly:
util.startLoop()
Getting the IB Connection¶
The get_ib() function returns the singleton IBMarketData instance and ensures it's connected:
# Get singleton instance and connect
ib = get_ib()
# Check connection status
if ib._connected:
print("✓ Connected to IB Gateway/TWS")
else:
print("✗ Connection failed - check IB Gateway/TWS is running")
# Fetch 6 months of daily data for tech stocks
symbols = ["AAPL", "MSFT", "GOOGL", "NVDA"]
# Async version (recommended for multiple symbols)
hist_data = await ohlcv_from_ib_async(
symbols=symbols,
period="6m",
interval="1d"
)
print(f"Downloaded {len(hist_data)} rows for {len(hist_data.index.get_level_values('symbol').unique())} symbols")
hist_data.head(10)
Downloaded 492 rows for 4 symbols
/Users/vitalik/python/chronos-lab/chronos_lab/ib.py:415: RuntimeWarning: '<' not supported between instances of 'Contract' and 'Contract', sort order is undefined for incomparable objects. return pd.concat(indexed_dfs, sort=False)
| open | high | low | close | volume | conId | ||
|---|---|---|---|---|---|---|---|
| date | symbol | ||||||
| 2025-08-15 00:00:00+00:00 | AAPL | 233.55 | 233.83 | 228.92 | 231.15 | 27981915.0 | 265598 |
| 2025-08-18 00:00:00+00:00 | AAPL | 231.33 | 232.68 | 229.67 | 230.45 | 21419078.0 | 265598 |
| 2025-08-19 00:00:00+00:00 | AAPL | 230.84 | 232.43 | 228.91 | 230.12 | 21176689.0 | 265598 |
| 2025-08-20 00:00:00+00:00 | AAPL | 229.53 | 230.03 | 225.34 | 225.58 | 23402034.0 | 265598 |
| 2025-08-21 00:00:00+00:00 | AAPL | 225.84 | 226.09 | 223.35 | 224.47 | 18942470.0 | 265598 |
| 2025-08-22 00:00:00+00:00 | AAPL | 225.74 | 228.65 | 224.98 | 227.33 | 23198421.0 | 265598 |
| 2025-08-25 00:00:00+00:00 | AAPL | 226.05 | 228.86 | 225.80 | 226.73 | 18562772.0 | 265598 |
| 2025-08-26 00:00:00+00:00 | AAPL | 226.43 | 229.05 | 224.26 | 228.87 | 19919435.0 | 265598 |
| 2025-08-27 00:00:00+00:00 | AAPL | 228.25 | 230.46 | 227.83 | 230.05 | 18188438.0 | 265598 |
| 2025-08-28 00:00:00+00:00 | AAPL | 230.38 | 232.97 | 228.89 | 232.12 | 20723021.0 | 265598 |
Benefits of ohlcv_from_ib():
- Automatically creates and qualifies contracts from symbols
- Returns chronos-lab standard format (date, symbol) MultiIndex
- Handles IB API parameter conversion
- Async version fetches multiple symbols concurrently
- Compatible with
ohlcv_to_arcticdb()for storage
Low-Level: get_hist_data()¶
For more control over IB API parameters, use get_hist_data() or get_hist_data_async():
# First, create and qualify contracts
contracts = await ib.symbols_to_contracts_async(["AAPL", "MSFT"])
print(f"Qualified {len(contracts)} contracts")
for contract in contracts:
print(f" {contract.symbol}: conId={contract.conId}")
# Fetch historical data with IB API parameters
hist_data_raw = await ib.get_hist_data_async(
contracts=contracts,
duration="30 D", # IB duration format
barsize="1 day", # IB bar size format
datatype="TRADES", # Data type
userth=True # Regular trading hours only
)
print(f"\nRaw data shape: {hist_data_raw.shape}")
hist_data_raw.head()
Qualified 2 contracts AAPL: conId=265598 MSFT: conId=272093 Raw data shape: (60, 8)
| open | high | low | close | volume | average | barCount | barsize | |||
|---|---|---|---|---|---|---|---|---|---|---|
| contract | datatype | date | ||||||||
| Contract(secType='STK', conId=265598, symbol='AAPL', exchange='SMART', primaryExchange='ISLAND', currency='USD', localSymbol='AAPL', tradingClass='NMS') | TRADES | 2025-12-29 00:00:00+00:00 | 272.70 | 274.36 | 272.35 | 273.76 | 10098230.0 | 273.555 | 58005 | 1 day |
| 2025-12-30 00:00:00+00:00 | 272.81 | 274.08 | 272.28 | 273.08 | 11846812.0 | 273.029 | 65753 | 1 day | ||
| 2025-12-31 00:00:00+00:00 | 273.06 | 273.68 | 271.75 | 271.86 | 11592691.0 | 272.580 | 63818 | 1 day | ||
| 2026-01-02 00:00:00+00:00 | 272.25 | 277.84 | 269.00 | 271.01 | 21546382.0 | 272.209 | 117839 | 1 day | ||
| 2026-01-05 00:00:00+00:00 | 270.72 | 271.51 | 266.14 | 267.26 | 23355082.0 | 268.243 | 129587 | 1 day |
The raw format uses (contract, datatype, date) as index. Convert to chronos-lab format with hist_to_ohlcv():
# Convert to standard OHLCV format
ohlcv_data = hist_to_ohlcv(hist_data_raw)
print(f"Converted data shape: {ohlcv_data.shape}")
ohlcv_data.tail()
Converted data shape: (60, 6)
| open | high | low | close | volume | conId | ||
|---|---|---|---|---|---|---|---|
| date | symbol | ||||||
| 2026-02-04 00:00:00+00:00 | MSFT | 410.97 | 419.80 | 409.24 | 414.19 | 26975115.0 | 272093 |
| 2026-02-05 00:00:00+00:00 | MSFT | 408.00 | 408.30 | 392.32 | 393.67 | 41928389.0 | 272093 |
| 2026-02-06 00:00:00+00:00 | MSFT | 399.39 | 401.79 | 393.00 | 401.14 | 33652590.0 | 272093 |
| 2026-02-09 00:00:00+00:00 | MSFT | 404.62 | 414.89 | 400.87 | 413.60 | 25849992.0 | 272093 |
| 2026-02-10 00:00:00+00:00 | MSFT | 419.61 | 423.68 | 412.70 | 413.27 | 24161214.0 | 272093 |
Storing to ArcticDB¶
Historical data can be persisted to ArcticDB for later use:
# Store historical data
result = ohlcv_to_arcticdb(
ohlcv=hist_data,
library_name='ib_data',
backend='LMDB'
)
if result['statusCode'] == 0:
print(f"✓ Stored {len(hist_data.index.get_level_values('symbol').unique())} symbols to ArcticDB")
✓ Stored 4 symbols to ArcticDB
Visualizing Historical Data¶
Let's create a candlestick chart using mplfinance:
import mplfinance as mpf
from chronos_lab.plot import bloomberg_style
# Select a single symbol for plotting
symbol = "NVDA"
chart_data = hist_data.xs(symbol, level='symbol')
# Create candlestick chart
fig, axes = mpf.plot(
chart_data,
type='candle',
style=bloomberg_style,
volume=True,
figsize=(12, 8),
panel_ratios=(3, 1),
returnfig=True,
ylabel='Price',
ylabel_lower='Volume',
datetime_format='%Y-%m-%d'
)
# Style adjustments
for ax in axes:
ax.set_facecolor('#000000')
ax.grid(True, which='major', linestyle=':', linewidth=0.8, color='#C0C0C0')
fig.suptitle(
f"{symbol} - Historical OHLCV from IB",
color='white',
fontsize=20,
y=0.92
)
print(f"Chart created for {symbol} ({len(chart_data)} bars)")
Chart created for NVDA (123 bars)
# Subscribe to 5-minute bars for tech stocks
contract_ids = await ib.subscribe_bars_async(
symbols=["AAPL", "MSFT", "GOOGL"],
period="1d", # How much history to fetch initially
interval="5m", # Bar interval
realtime=False # Use keepUpToDate historical bars (not real-time bars)
)
print(f"✓ Subscribed to {len(contract_ids)} contracts for streaming")
print(f"Contract IDs: {contract_ids}")
✓ Subscribed to 3 contracts for streaming Contract IDs: [265598, 272093, 208813719]
About the realtime parameter:
realtime=False(default): Uses IB'sreqHistoricalData()withkeepUpToDate=Truefor streaming updates- Respects the
intervalparameter (e.g., "5m", "1h", "1d") - Provides historical data plus ongoing updates
- Most flexible option for various time intervals
- Respects the
realtime=True: Uses IB'sreqRealTimeBars()API- Special case: Always returns 5-second bars regardless of the
intervalparameter - IB API limitation: 5-second bars are the only interval available with
reqRealTimeBars() - Use this only when you specifically need 5-second granularity without historical data
- Note: The
intervalparameter is ignored whenrealtime=True
- Special case: Always returns 5-second bars regardless of the
Retrieving Streaming Data¶
Use get_bars() to retrieve current bar data from active subscriptions:
util.sleep(0)
# Retrieve all bars
bars_df = ib.get_bars()
print(f"Retrieved {len(bars_df)} bars")
bars_df.tail(10)
Retrieved 234 bars
| open | high | low | close | volume | conId | ||
|---|---|---|---|---|---|---|---|
| date | symbol | ||||||
| 2026-02-10 20:10:00+00:00 | GOOGL | 319.64 | 319.73 | 319.46 | 319.57 | 134343.0 | 208813719 |
| 2026-02-10 20:15:00+00:00 | GOOGL | 319.57 | 320.00 | 319.52 | 319.86 | 180045.0 | 208813719 |
| 2026-02-10 20:20:00+00:00 | GOOGL | 319.86 | 320.27 | 319.77 | 320.00 | 204239.0 | 208813719 |
| 2026-02-10 20:25:00+00:00 | GOOGL | 319.98 | 320.26 | 319.72 | 320.05 | 121487.0 | 208813719 |
| 2026-02-10 20:30:00+00:00 | GOOGL | 320.04 | 320.38 | 319.75 | 319.88 | 192006.0 | 208813719 |
| 2026-02-10 20:35:00+00:00 | GOOGL | 319.88 | 319.95 | 319.41 | 319.45 | 211502.0 | 208813719 |
| 2026-02-10 20:40:00+00:00 | GOOGL | 319.45 | 319.65 | 319.37 | 319.52 | 158313.0 | 208813719 |
| 2026-02-10 20:45:00+00:00 | GOOGL | 319.53 | 319.75 | 319.48 | 319.60 | 246338.0 | 208813719 |
| 2026-02-10 20:50:00+00:00 | GOOGL | 319.56 | 319.56 | 318.87 | 318.97 | 512054.0 | 208813719 |
| 2026-02-10 20:55:00+00:00 | GOOGL | 318.95 | 319.17 | 318.40 | 318.54 | 1046831.0 | 208813719 |
Filtering Bar Data¶
get_bars() supports flexible filtering:
# Get only AAPL bars
aapl_bars = ib.get_bars(symbols=["AAPL"])
print(f"AAPL: {len(aapl_bars)} bars\n")
print(aapl_bars.tail())
# Get last 10 bars per symbol
recent_bars = ib.get_bars(last=10)
print(f"\nLast 10 bars per symbol: {len(recent_bars)} total bars")
# Get bars for specific date range
today = pd.Timestamp.now(tz='UTC').normalize()
date_filtered = ib.get_bars(start_date=today)
print(f"\nToday's bars: {len(date_filtered)} bars")
AAPL: 78 bars
open high low close volume \
date symbol
2026-02-10 20:35:00+00:00 AAPL 274.11 274.27 274.00 274.13 144853.0
2026-02-10 20:40:00+00:00 AAPL 274.15 274.39 274.03 274.26 152205.0
2026-02-10 20:45:00+00:00 AAPL 274.28 274.49 274.23 274.49 207710.0
2026-02-10 20:50:00+00:00 AAPL 274.49 274.49 273.26 273.41 540839.0
2026-02-10 20:55:00+00:00 AAPL 273.40 274.00 273.00 273.79 1114461.0
conId
date symbol
2026-02-10 20:35:00+00:00 AAPL 265598
2026-02-10 20:40:00+00:00 AAPL 265598
2026-02-10 20:45:00+00:00 AAPL 265598
2026-02-10 20:50:00+00:00 AAPL 265598
2026-02-10 20:55:00+00:00 AAPL 265598
Last 10 bars per symbol: 30 total bars
Today's bars: 0 bars
Unsubscribing¶
Cancel subscriptions to free up resources:
# Unsubscribe from specific contracts
# ib.unsub_bars(contract_ids=[contract_ids[0]])
# Or unsubscribe from all
ib.unsub_bars()
print("✓ Unsubscribed from all bar subscriptions")
✓ Unsubscribed from all bar subscriptions
Low-Level: sub_bars()¶
For direct control over IB API parameters:
# Create contracts
contracts = await ib.symbols_to_contracts_async(["TSLA"])
# Subscribe with explicit IB parameters
await ib.sub_bars_async(
contracts=contracts,
endDateTime="",
durationStr="1 D",
barSizeSetting="1 min",
whatToShow="TRADES",
useRTH=False, # Include extended hours
keepUpToDate=True, # Streaming mode
formatDate=2,
realtime=False
)
print("✓ Subscribed to TSLA with custom parameters")
✓ Subscribed to TSLA with custom parameters
# Create contracts
contracts = await ib.symbols_to_contracts_async(["AAPL", "MSFT", "GOOGL"])
# Subscribe to tick updates
ib.sub_tickers(
contracts=contracts,
gen_tick_list='' # Basic ticks; use ib.gen_tick_list for comprehensive data
)
print(f"✓ Subscribed to tickers for {len(contracts)} contracts")
✓ Subscribed to tickers for 3 contracts
Retrieving Tick Data¶
# Wait for data
util.sleep(0)
# Get current tick data
tickers_df = ib.get_tickers()
print(f"Current tick data for {len(tickers_df)} symbols:")
tickers_df[['last', 'bid', 'ask', 'bidSize', 'askSize', 'marketPrice']]
Current tick data for 3 symbols:
| last | bid | ask | bidSize | askSize | marketPrice | |
|---|---|---|---|---|---|---|
| symbol | ||||||
| AAPL | 274.13 | 274.07 | 274.13 | 100.0 | 100.0 | 274.13 |
| MSFT | 416.70 | 416.70 | 416.80 | 80.0 | 80.0 | 416.70 |
| GOOGL | 320.31 | 320.14 | 320.35 | 100.0 | 200.0 | 320.31 |
Analyzing Bid-Ask Spreads¶
# Calculate bid-ask spreads
if len(tickers_df) > 0:
tickers_df['spread'] = tickers_df['ask'] - tickers_df['bid']
tickers_df['spread_pct'] = (tickers_df['spread'] / tickers_df['marketPrice']) * 100
print("Bid-Ask Spread Analysis:")
print(tickers_df[['bid', 'ask', 'spread', 'spread_pct']].round(4))
else:
print("No tick data available yet")
Bid-Ask Spread Analysis:
bid ask spread spread_pct
symbol
AAPL 274.07 274.13 0.06 0.0219
MSFT 416.70 416.80 0.10 0.0240
GOOGL 320.14 320.35 0.21 0.0656
Unsubscribing from Tickers¶
# Unsubscribe from all tickers
ib.unsub_tickers()
print("✓ Unsubscribed from all tick subscriptions")
✓ Unsubscribed from all tick subscriptions
# Check which exchanges support market depth
exchanges = ib.conn.reqMktDepthExchanges()
print(f"Exchanges with market depth support: {len(exchanges)}")
for ex in exchanges[:5]: # Show first 5
print(f" {ex.exchange}: {ex.secType}")
Exchanges with market depth support: 304 DTB: OPT COMEX: FOP LSEETF: STK SGX: FUT ISLAND: WAR
# Subscribe to market depth for a contract
contract = (await ib.symbols_to_contracts_async(["AAPL"]))[0]
depth_ticker = ib.conn.reqMktDepth(contract, numRows=5)
print(f"Subscribed to market depth for {contract.symbol}")
# Wait for data
util.sleep(3)
# Display order book
print(f"\nBids (top 5):")
for bid in depth_ticker.domBids[:5]:
print(f" {bid.size:6} @ {bid.price:.2f}")
print(f"\nAsks (top 5):")
for ask in depth_ticker.domAsks[:5]:
print(f" {ask.size:6} @ {ask.price:.2f}")
# Cancel subscription
ib.conn.cancelMktDepth(contract)
print("\n✓ Cancelled market depth subscription")
Subscribed to market depth for AAPL Bids (top 5): Asks (top 5): ✓ Cancelled market depth subscription
More Examples¶
For additional examples of direct IB API usage, see the ib-async documentation:
- Historical tick data
- Tick-by-tick data
- Options chains
- Fundamental data
- News feeds
Cleanup: Disconnecting¶
Always disconnect when done to cleanly close the connection:
# Disconnect cleans up all subscriptions automatically
ib.disconnect()
print("✓ Disconnected from IB Gateway/TWS")
✓ Disconnected from IB Gateway/TWS
from ib_async import Contract
# Stock contract
stock = Contract(
secType='STK',
symbol='AAPL',
exchange='SMART',
currency='USD'
)
# Forex contract
forex = Contract(
secType='CASH',
symbol='EUR',
currency='USD'
)
contracts = [stock, forex]
print("Created contracts (not yet qualified)")
print(f"Stock conId: {stock.conId}") # Will be 0 until qualified
print(f"Forex conId: {forex.conId}")
Created contracts (not yet qualified) Stock conId: 0 Forex conId: 0
Qualifying Contracts¶
Qualification fills in missing details and obtains the conId:
# Reconnect if needed
if not ib._connected:
ib = get_ib()
# Qualify contracts
qualified = ib.conn.qualifyContracts(*contracts)
print("Qualified contracts:")
for contract in qualified:
print(f" {contract.symbol} ({contract.secType}): conId={contract.conId}")
Qualified contracts: AAPL (STK): conId=265598 EUR (CASH): conId=12087792
Getting Contract Details¶
Contract details provide comprehensive metadata:
# Lookup contract details
await ib.lookup_cds_async(qualified)
# Retrieve as DataFrame
details_df = ib.get_cds()
print(f"Contract details for {len(details_df)} contracts:")
print(details_df[['conId', 'longName', 'industry', 'category']].head())
Contract details for 2 contracts:
conId longName industry category
symbol
AAPL 265598 APPLE INC Technology Computers
EUR 12087792 European Monetary Union Euro
Configuration Reference¶
Complete list of IB-related settings in ~/.chronos_lab/.env:
Connection Settings¶
- IB_GATEWAY_HOST: Gateway/TWS hostname (default:
127.0.0.1) - IB_GATEWAY_PORT: Port number (default:
4002for Gateway,7497for TWS paper trading) - IB_GATEWAY_READONLY: Read-only mode (default:
True) - IB_GATEWAY_CLIENT_ID: Unique client identifier (default:
1) - IB_GATEWAY_ACCOUNT: IB account identifier (optional)
Rate Limiting¶
- IB_REF_DATA_CONCURRENCY: Max concurrent reference data requests (default:
10) - IB_HISTORICAL_DATA_CONCURRENCY: Max concurrent historical data requests (default:
10)
Troubleshooting¶
Connection Issues¶
- "Connection refused": Check that IB Gateway/TWS is running
- "Already connected": Another client is using the same client ID
- Solution: Change
IB_GATEWAY_CLIENT_IDin.env
- Solution: Change
Data Issues¶
- "No data returned": Contract not found or market closed
- "Pacing violation": Too many requests too quickly
- Solution: Reduce concurrency settings or add delays
- "Historical data limit exceeded": Requested period too long for bar size
- Solution: Use larger bar size or shorter period
Port Numbers¶
- Gateway live:
4001 - Gateway paper:
4002(recommended for testing) - TWS live:
7496 - TWS paper:
7497
IB API Limitations¶
The IB API has specific constraints on historical data requests:
| Bar Size | Maximum Duration |
|---|---|
| 1 sec - 30 secs | 30 minutes |
| 1 min - 30 mins | 30 days |
| 1 hour - 8 hours | 30 days |
| 1 day | 1 year |
| 1 week - 1 month | 1 year |
The chronos-lab functions handle these constraints automatically and will warn if overfetching is required.
Next Steps¶
Now that you understand IB integration, consider:
- Combining IB data with other sources (Yahoo Finance, Intrinio)
- Building real-time monitoring dashboards
- Creating automated data pipelines with ArcticDB storage
- Exploring advanced IB features (options, futures, forex)
See the Getting Started tutorial for more on data workflows and storage patterns.