Nautilus Trader - Integrations
Nautilus uses a modular adapter pattern: every venue/data-provider integration is a plug-in that translates the venue’s raw API into Nautilus’s unified domain model (native venue symbols + UNIX-epoch-nanosecond timestamps). An adapter is composed of three pieces - InstrumentProvider (loads contract/symbol metadata), DataClient (subscribes/requests market data), and ExecutionClient (submits/modifies/cancels orders) - plus the underlying transport (HttpClient for REST, WebSocketClient for streaming). The framework standardizes the lifecycle (connect → load instruments → reconcile account/positions → stream → reconnect on drop) so strategies remain venue-agnostic. Most adapters are marked stable; Coinbase is currently beta.
IBKR Integration (DETAILED)
Cortana MK2 currently runs on IBKR Gateway port 4002 with paper account DUP696099, using ib_async. Nautilus ships a first-class IBKR adapter that fully replaces direct ib_async usage for both data and execution.
Supported features
Order types - full coverage:
- Market, Limit, Stop-Market, Stop-Limit
- Market-If-Touched, Limit-If-Touched
- Trailing Stop Market, Trailing Stop Limit
- Market-on-Close / Limit-on-Close (
TimeInForce.AT_THE_CLOSE) - Bracket (parent-child)
- OCO via explicit
IBOrderTags(ContingencyType.OCOdoes not auto-create OCA groups - gotcha) - Contingent orders with 6 condition types: price, time, volume, execution, margin, percent change
Market data - quote ticks, trade ticks (microsecond precision), OHLCV bars (1-second through 1-month), Level-2 depth (where IB permits). Four IBMarketDataTypeEnum modes: REALTIME, DELAYED, DELAYED_FROZEN, FROZEN.
Account / position data - real-time balance + margin updates, real-time position + P&L stream, multi-account support (one execution client per account), position mode (net vs. separate long/short), leverage/margin mode config, account reconciliation on startup. Option-exercise tracking is opt-in via track_option_exercise_from_position_update=True.
Contract specs - equities + ETFs + equity options, futures + continuous futures (CONTFUT), options on futures (FOP), forex (IDEALPRO), crypto (PAXOS), indices + index options, bonds (ISIN/CUSIP), CFDs, commodities.
Authentication / configuration
Two connection paths:
- Existing TWS/Gateway instance - point
ibg_host+ibg_portat a TWS or Gateway you start yourself. - Dockerized IB Gateway (recommended for automation) -
DockerizedIBGatewayConfig(username, password, trading_mode, read_only_api, timeout)boots the gateway in a container.
Default ports:
| Mode | TWS | Gateway |
|---|---|---|
| Paper | 7497 | 4002 |
| Live | 7496 | 4001 |
Cortana’s 4002 matches Gateway paper. Account ID DUP696099 follows the IB paper convention (DU prefix; the P is non-standard but IB does mint accounts with letters after DU).
Critical config keys:
ibg_host(default127.0.0.1)ibg_port(required)ibg_client_id(1–32, must be unique per execution client)account_id(e.g.,DUP696099)IBMarketDataTypeEnumfor data clientSymbologyMethod.IB_SIMPLIFIED(e.g.,EUR/USD.IDEALPRO) vs.IB_RAW(e.g.,AAPL=STK.SMART)
Env vars: TWS_USERNAME, TWS_PASSWORD, TWS_ACCOUNT, IB_MAX_CONNECTION_ATTEMPTS.
Paper vs live
| Aspect | Paper | Live |
|---|---|---|
| Port | 7497 (TWS) / 4002 (Gateway) | 7496 (TWS) / 4001 (Gateway) |
| Account | DU… prefix | alphanumeric (e.g., U987654) |
| Data mode | DELAYED_FROZEN is sufficient | typically REALTIME |
| Credentials | same IB login | same IB login |
Mode is a config-only switch - no code changes. You can run paper and live as two execution clients in the same TradingNode (different ibg_client_id + account_id).
Reconciliation / position sync
- Connect over socket → run handshake.
- Pull account info, balances, positions (
InteractiveBrokersClientAccountMixin). - Load instruments listed in
load_ids/load_contracts. - Stream position + P&L updates continuously through
_process_message()(the central API-message gateway). Cache + portfolio update on each tick. - Watchdog loop monitors the socket; auto-reconnect with
IB_MAX_CONNECTION_ATTEMPTSretries. fetch_all_open_orders=Truepulls orders placed by any API client on the account (useful for surviving restarts; defaultFalse).
Known limitations / gotchas
- IB pacing limits - too many historical-data requests trigger a multi-minute API lockout. Batch instruments, throttle requests, tune
request_timeout_secs(default 60s). - Data permissions - many feeds require a paid IB market-data subscription. Use
DELAYED_FROZENfor dev. - Symbology -
IB_SIMPLIFIEDis human-readable;IB_RAWmirrors IB API fields exactly. Pick one and stick to it. - Contract discovery - undocumented contracts can’t materialize as
Instrumentobjects. Use IB’s Contract Information Center to find conIds. - Bar revisions - IB occasionally rewrites a published bar. Set
handle_revised_bars=Trueif your strategy needs the corrected version. - TWS UTC requirement - TWS/Gateway must be configured to emit UTC timestamps before Nautilus connects. This is a TWS GUI setting, not a Nautilus knob.
- OCA / OCO - must be configured via
IBOrderTags(ocaGroup=…, ocaType=…). SettingContingencyType.OCOon a Nautilus order does not create the IB-side OCA group automatically. - Multi-client - every execution client needs a unique
ibg_client_id. Collisions silently break order routing. connection_timeout=300- startup window is 5 minutes; if Gateway takes longer to log in (e.g., 2FA push), bump it.
Strategy wiring (basic)
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.model.identifiers import InstrumentId, Venue
class MyStrategy(Strategy):
def on_start(self):
self.request_instrument(
venue=Venue("IB"),
instrument_id=InstrumentId.from_str("SPY.ARCA"),
)
def on_instrument(self, instrument):
self.subscribe_quote_ticks(instrument.id)
self.subscribe_trade_ticks(instrument.id)
def on_quote_tick(self, tick):
order = self.order_factory.market(
instrument_id=tick.instrument_id,
order_side=OrderSide.BUY,
quantity=tick.instrument.make_qty(10),
)
self.submit_order(order)
def on_order_filled(self, event):
self.log.info(f"Filled: {event.order.id}")Sample config - minimal paper
from nautilus_trader.adapters.interactive_brokers.config import (
InteractiveBrokersDataClientConfig,
InteractiveBrokersExecClientConfig,
InteractiveBrokersInstrumentProviderConfig,
SymbologyMethod,
IBMarketDataTypeEnum,
)
from nautilus_trader.config import TradingNodeConfig, RoutingConfig
instrument_config = InteractiveBrokersInstrumentProviderConfig(
symbology_method=SymbologyMethod.IB_SIMPLIFIED,
load_ids=frozenset(["SPY.ARCA"]),
)
data_config = InteractiveBrokersDataClientConfig(
ibg_host="127.0.0.1",
ibg_port=4002, # Cortana's port
ibg_client_id=1,
market_data_type=IBMarketDataTypeEnum.DELAYED_FROZEN,
instrument_provider=instrument_config,
)
exec_config = InteractiveBrokersExecClientConfig(
ibg_host="127.0.0.1",
ibg_port=4002,
ibg_client_id=1,
account_id="DUP696099", # Cortana's paper account
instrument_provider=instrument_config,
routing=RoutingConfig(default=True),
)
node_config = TradingNodeConfig(
trader_id="CORTANA-PAPER",
data_clients={"IB": data_config},
exec_clients={"IB": exec_config},
)Sample config - options chain via Dockerized Gateway
from nautilus_trader.adapters.interactive_brokers.common import IBContract
from nautilus_trader.adapters.interactive_brokers.config import DockerizedIBGatewayConfig
gateway_config = DockerizedIBGatewayConfig(
username="…", password="…",
trading_mode="paper",
read_only_api=False,
)
instrument_config = InteractiveBrokersInstrumentProviderConfig(
load_contracts=frozenset([
IBContract(
secType="STK", symbol="SPY",
exchange="SMART", primaryExchange="ARCA",
build_options_chain=True,
min_expiry_days=0,
max_expiry_days=1, # 0DTE-style window
),
]),
)
exec_config = InteractiveBrokersExecClientConfig(
ibg_client_id=1,
account_id=os.environ["TWS_ACCOUNT"],
instrument_provider=instrument_config,
dockerized_gateway=gateway_config,
routing=RoutingConfig(default=True),
)Migration assessment for Cortana MK2
Verdict: Nautilus’s IBKR adapter would replace ib_async cleanly with low custom work. Concretely:
- Direct fit - Gateway-port-4002 + paper-account-
DUP696099map 1:1 toibg_port=4002+account_id="DUP696099". No translation layer needed. - 0DTE options coverage -
IBContract(build_options_chain=True, min_expiry_days=0, max_expiry_days=1)materializes the 0DTE chain natively. Equity-options orders, bracket parents, OCA groups (viaIBOrderTags) are first-class. - Position-sync invariant - Nautilus’s reconciliation on startup + continuous position stream replaces the bespoke reconciler in GH #46 (
project_pm_ibkr_exit_invariant.md). This is a large maintenance win. - Dual-TP defense-in-depth (
feedback_dual_tp_defense_in_depth.md) - Nautilus orders carry both broker-side LMT and an in-process state machine, so the existing software-fallback pattern is preserved. - Caveats / custom work needed:
- OCA gotcha: Cortana’s bracket-style exits will need
IBOrderTagspopulated, not justContingencyType.OCO. - TWS UTC: Gateway settings must be flipped before connect - make this a launchd preflight check.
- Watchdog overlap: Cortana already has a launchd-driven watchdog (
feedback_watchdog_to_telegram.md); Nautilus’s auto-reconnect partially overlaps. Decide which is authoritative - probably let Nautilus handle reconnect, keep launchd for process supervision. ib_asynccallsites: everyIB.connect()/placeOrder()/reqMktData()becomessubmit_order()/subscribe_*(). Mechanical but pervasive.- Symbology choice: pick
IB_SIMPLIFIEDfor readability orIB_RAWfor parity with existing IB conId tracking - one-time decision.
- OCA gotcha: Cortana’s bracket-style exits will need
Net: clean swap, weeks not months. The bigger lift is plumbing UW (below) into Nautilus’s data engine, not replacing IBKR plumbing.
Other Integrations (BRIEF)
Cryptocurrency CEX
- Binance - spot, USDM, COINM derivatives.
- Bybit - spot + perpetuals + USDC options.
- OKX - spot, perps, futures, options.
- Coinbase - spot (currently beta).
- Kraken - spot + futures.
- BitMEX - perps + futures.
- Deribit - crypto options + perps (the institutional crypto-options venue).
DEX / on-chain
- dYdX v4 - perps on Cosmos-based dYdX chain.
- Hyperliquid - perps on Hyperliquid L1.
- Polymarket - prediction-market binary outcomes (event-driven).
Other derivatives
- AX Exchange - perpetuals venue.
Traditional brokerage / multi-asset
- Interactive Brokers - covered above; the only adapter that spans equities + options + futures + FX + bonds + CFDs in one connector.
Alternative markets
- Betfair - sports-betting exchange (treated as a venue with quotes + orders).
Data-only providers
- Databento - historical + live multi-asset normalized data.
- Tardis - historical crypto market-data (high-fidelity tick-by-tick).
What about Unusual Whales?
Nautilus has no native UW adapter. UW is niche options-flow + GEX/charm/vanna data - not on Nautilus’s roadmap. A Cortana migration would build a custom data-only adapter following Nautilus’s adapter pattern:
UnusualWhalesInstrumentProvider- minimal; UW doesn’t define instruments, IB does. Provider can be a no-op or proxy IB instruments.UnusualWhalesDataClient- implements:connect()opens the WebSocket (UW streaming endpoint) + holds anHttpClientfor REST fallback / historical pulls (e.g.,/api/net-flow/expiry).subscribe_*methods for the custom data types Cortana cares about (flow, GEX, charm/vanna magnitudes, alerts).- Push each WS message into the Nautilus
DataEngineas a custom data type (subclass ofData). Nautilus supports user-defined data classes that flow through the same engine plumbing as bars/ticks; strategies consume them viasubscribe_data(YourCustomType)andon_data().
- No
ExecutionClient- UW is read-only. Execution stays on the IBKR adapter. - Wiring - register the UW client under a new
data_clients={"UW": uw_data_config}entry; strategies cansubscribe_data(UnusualWhalesFlowTick)alongside their IB quote-tick subscriptions in the same engine.
Effort estimate: a couple of weeks for a clean adapter. Existing Cortana UW code (REST + WS handlers, schema for flow/GEX/charm) ports almost directly into the new client - the work is structural (fitting Nautilus’s DataEngine contract) rather than business-logic.
Two known UW issues - strike-format 404 (#54) and WebSocket timestamp unit mismatch (#59) - would naturally get fixed inside the adapter layer rather than scattered across callsites.
See also
- nautilus-concepts.md (DataEngine, ExecutionEngine sections)
- nautilus-developer-guide.md (writing custom adapters)
- project_pm_ibkr_exit_invariant.md - the broker-truth invariant Nautilus’s reconciliation enforces by default
- feedback_dual_tp_defense_in_depth.md - defense-in-depth pattern survives migration