Skip to content

Commit 279fe36

Browse files
committed
Refine IB adapter
1 parent 03845a1 commit 279fe36

File tree

28 files changed

+1389
-779
lines changed

28 files changed

+1389
-779
lines changed

examples/live/interactive_brokers/historical_download.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ async def main(
5454
primaryExchange="NASDAQ",
5555
)
5656
instrument_id = "TSLA.NASDAQ"
57+
venue = "NASDAQ"
5758

5859
client = HistoricInteractiveBrokersClient(host=host, port=port, client_id=5)
5960
await client.connect()
@@ -65,6 +66,7 @@ async def main(
6566
)
6667

6768
bars = await client.request_bars(
69+
venue=venue,
6870
bar_specifications=["1-HOUR-LAST", "30-MINUTE-MID"],
6971
start_date_time=datetime.datetime(2023, 11, 6, 9, 30),
7072
end_date_time=datetime.datetime(2023, 11, 6, 16, 30),
@@ -74,7 +76,8 @@ async def main(
7476
)
7577

7678
trade_ticks = await client.request_ticks(
77-
"TRADES",
79+
venue=venue,
80+
tick_type="TRADES",
7881
start_date_time=datetime.datetime(2023, 11, 6, 10, 0),
7982
end_date_time=datetime.datetime(2023, 11, 6, 10, 1),
8083
tz_name="America/New_York",
@@ -83,7 +86,8 @@ async def main(
8386
)
8487

8588
quote_ticks = await client.request_ticks(
86-
"BID_ASK",
89+
venue=venue,
90+
tick_type="BID_ASK",
8791
start_date_time=datetime.datetime(2023, 11, 6, 10, 0),
8892
end_date_time=datetime.datetime(2023, 11, 6, 10, 1),
8993
tz_name="America/New_York",

examples/live/interactive_brokers/with_databento_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
ibg_client_id=1,
7474
account_id="DU123456", # This must match with the IB Gateway/TWS node is connecting to
7575
instrument_provider=InteractiveBrokersInstrumentProviderConfig(
76-
symbology_method=SymbologyMethod.DATABENTO,
76+
symbology_method=SymbologyMethod.IB_SIMPLIFIED,
7777
load_ids=frozenset(instrument_ids),
7878
),
7979
routing=RoutingConfig(
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# ---
2+
# jupyter:
3+
# jupytext:
4+
# formats: ipynb,py:percent
5+
# text_representation:
6+
# extension: .py
7+
# format_name: percent
8+
# format_version: '1.3'
9+
# jupytext_version: 1.17.1
10+
# kernelspec:
11+
# display_name: Python 3 (ipykernel)
12+
# language: python
13+
# name: python3
14+
# ---
15+
16+
# %%
17+
18+
# fmt: off
19+
from nautilus_trader.adapters.interactive_brokers.common import IB
20+
from nautilus_trader.adapters.interactive_brokers.common import IB_VENUE
21+
from nautilus_trader.adapters.interactive_brokers.config import InteractiveBrokersDataClientConfig
22+
from nautilus_trader.adapters.interactive_brokers.config import InteractiveBrokersExecClientConfig
23+
from nautilus_trader.adapters.interactive_brokers.config import InteractiveBrokersInstrumentProviderConfig
24+
from nautilus_trader.adapters.interactive_brokers.config import SymbologyMethod
25+
from nautilus_trader.adapters.interactive_brokers.factories import InteractiveBrokersLiveDataClientFactory
26+
from nautilus_trader.adapters.interactive_brokers.factories import InteractiveBrokersLiveExecClientFactory
27+
from nautilus_trader.common.enums import LogColor
28+
from nautilus_trader.config import LiveDataEngineConfig
29+
from nautilus_trader.config import LoggingConfig
30+
from nautilus_trader.config import RoutingConfig
31+
from nautilus_trader.config import TradingNodeConfig
32+
from nautilus_trader.live.node import TradingNode
33+
from nautilus_trader.model.data import Bar
34+
from nautilus_trader.model.data import BarType
35+
from nautilus_trader.model.enums import OrderSide
36+
from nautilus_trader.model.enums import TimeInForce
37+
from nautilus_trader.model.events import PositionOpened
38+
from nautilus_trader.model.identifiers import InstrumentId
39+
from nautilus_trader.trading.config import StrategyConfig
40+
from nautilus_trader.trading.strategy import Strategy
41+
42+
43+
# fmt: on
44+
45+
46+
class DemoStrategyConfig(StrategyConfig, frozen=True):
47+
bar_type: BarType
48+
instrument_id: InstrumentId
49+
50+
51+
class DemoStrategy(Strategy):
52+
def __init__(self, config: DemoStrategyConfig):
53+
super().__init__(config=config)
54+
55+
# Track if we've already placed an order
56+
self.order_placed = False
57+
58+
# Track total bars seen
59+
self.count_of_bars: int = 0
60+
self.show_portfolio_at_bar: int | None = 0
61+
62+
def on_start(self):
63+
"""
64+
Handle strategy start event.
65+
"""
66+
self.request_instrument(self.config.instrument_id)
67+
self.instrument = self.cache.instrument(self.config.instrument_id)
68+
69+
# Subscribe to market data
70+
self.subscribe_bars(self.config.bar_type)
71+
72+
# Show initial portfolio state
73+
self.show_portfolio_info("Portfolio state (Before trade)")
74+
75+
def on_bar(self, bar: Bar):
76+
"""
77+
Handle new bar event.
78+
"""
79+
# Increment total bars seen
80+
self.count_of_bars += 1
81+
82+
# Show portfolio state if we reached target bar
83+
if self.show_portfolio_at_bar == self.count_of_bars:
84+
self.show_portfolio_info("Portfolio state (2 minutes after position opened)")
85+
86+
# Only place one order for demonstration
87+
if not self.order_placed:
88+
# Prepare values for order
89+
last_price = bar.close
90+
tick_size = self.instrument.price_increment
91+
profit_price = self.instrument.make_price(last_price + (10 * tick_size))
92+
stoploss_price = self.instrument.make_price(last_price - (10 * tick_size))
93+
94+
# Create BUY MARKET order with PT and SL (both 10 ticks)
95+
bracket_order_list = self.order_factory.bracket(
96+
instrument_id=self.config.instrument_id,
97+
order_side=OrderSide.BUY,
98+
quantity=self.instrument.make_qty(1), # Trade size: 1 contract
99+
time_in_force=TimeInForce.GTC,
100+
tp_price=profit_price,
101+
sl_trigger_price=stoploss_price,
102+
entry_post_only=False,
103+
tp_post_only=False,
104+
)
105+
106+
# Submit order and remember it
107+
self.submit_order_list(bracket_order_list)
108+
self.order_placed = True
109+
self.log.info(f"Submitted bracket order: {bracket_order_list}", color=LogColor.GREEN)
110+
111+
def on_position_opened(self, event: PositionOpened):
112+
"""
113+
Handle position opened event.
114+
"""
115+
# Log position details
116+
self.log.info(f"Position opened: {event}", color=LogColor.GREEN)
117+
118+
# Show portfolio state when position is opened
119+
self.show_portfolio_info("Portfolio state (In position):")
120+
121+
# Set target bar number for next portfolio display
122+
self.show_portfolio_at_bar = self.count_of_bars + 2 # Show after 2 bars
123+
124+
def on_stop(self):
125+
"""
126+
Handle strategy stop event.
127+
"""
128+
# Show final portfolio state
129+
self.show_portfolio_info("Portfolio state (After trade)")
130+
131+
def show_portfolio_info(self, intro_message: str = ""):
132+
"""
133+
Display current portfolio information.
134+
"""
135+
if intro_message:
136+
self.log.info(f"====== {intro_message} ======")
137+
138+
# POSITION information
139+
self.log.info("Portfolio -> Position information:", color=LogColor.BLUE)
140+
is_flat = self.portfolio.is_flat(self.config.instrument_id)
141+
self.log.info(f"Is flat: {is_flat}", color=LogColor.BLUE)
142+
143+
net_position = self.portfolio.net_position(self.config.instrument_id)
144+
self.log.info(f"Net position: {net_position} contract(s)", color=LogColor.BLUE)
145+
146+
net_exposure = self.portfolio.net_exposure(self.config.instrument_id)
147+
self.log.info(f"Net exposure: {net_exposure}", color=LogColor.BLUE)
148+
149+
# -----------------------------------------------------
150+
151+
# P&L information
152+
self.log.info("Portfolio -> P&L information:", color=LogColor.YELLOW)
153+
154+
realized_pnl = self.portfolio.realized_pnl(self.config.instrument_id)
155+
self.log.info(f"Realized P&L: {realized_pnl}", color=LogColor.YELLOW)
156+
157+
unrealized_pnl = self.portfolio.unrealized_pnl(self.config.instrument_id)
158+
self.log.info(f"Unrealized P&L: {unrealized_pnl}", color=LogColor.YELLOW)
159+
160+
# -----------------------------------------------------
161+
162+
self.log.info("Portfolio -> Account information:", color=LogColor.CYAN)
163+
margins_init = self.portfolio.margins_init(IB_VENUE)
164+
self.log.info(f"Initial margin: {margins_init}", color=LogColor.CYAN)
165+
166+
margins_maint = self.portfolio.margins_maint(IB_VENUE)
167+
self.log.info(f"Maintenance margin: {margins_maint}", color=LogColor.CYAN)
168+
169+
balances_locked = self.portfolio.balances_locked(IB_VENUE)
170+
self.log.info(f"Locked balance: {balances_locked}", color=LogColor.CYAN)
171+
172+
173+
# %%
174+
instrument_provider = InteractiveBrokersInstrumentProviderConfig(
175+
symbology_method=SymbologyMethod.IB_SIMPLIFIED,
176+
convert_exchange_to_mic_venue=True,
177+
# load_ids=frozenset(
178+
# [
179+
# "YMM5.XCBT",
180+
# ],
181+
# ),
182+
)
183+
184+
# Configure the trading node
185+
# IMPORTANT: you must use the imported IB string so this client works properly
186+
config_node = TradingNodeConfig(
187+
trader_id="TESTER-001",
188+
logging=LoggingConfig(log_level="INFO"),
189+
data_clients={
190+
IB: InteractiveBrokersDataClientConfig(
191+
ibg_port=7497,
192+
handle_revised_bars=False,
193+
use_regular_trading_hours=False,
194+
instrument_provider=instrument_provider,
195+
),
196+
},
197+
exec_clients={
198+
IB: InteractiveBrokersExecClientConfig(
199+
ibg_port=7497,
200+
instrument_provider=instrument_provider,
201+
routing=RoutingConfig(default=True),
202+
account_id="DU187075",
203+
),
204+
},
205+
data_engine=LiveDataEngineConfig(
206+
time_bars_timestamp_on_close=False, # Will use opening time as `ts_event` (same as IB)
207+
validate_data_sequence=True, # Will make sure DataEngine discards any Bars received out of sequence
208+
),
209+
timeout_connection=90.0,
210+
timeout_reconciliation=5.0,
211+
timeout_portfolio=5.0,
212+
timeout_disconnection=5.0,
213+
timeout_post_stop=2.0,
214+
)
215+
216+
217+
# Instantiate the node with a configuration
218+
node = TradingNode(config=config_node)
219+
220+
# Instantiate your strategy
221+
instrument_id = "YMM5.XCBT" # AAPL.XNAS
222+
223+
strategy_config = DemoStrategyConfig(
224+
bar_type=BarType.from_str(f"{instrument_id}-1-MINUTE-LAST-EXTERNAL"),
225+
instrument_id=InstrumentId.from_str(instrument_id),
226+
)
227+
strategy = DemoStrategy(config=strategy_config)
228+
229+
# Add your strategies and modules
230+
node.trader.add_strategy(strategy)
231+
232+
# Register your client factories with the node (can take user-defined factories)
233+
node.add_data_client_factory(IB, InteractiveBrokersLiveDataClientFactory)
234+
node.add_exec_client_factory(IB, InteractiveBrokersLiveExecClientFactory)
235+
node.build()
236+
237+
# %%
238+
node.run()
239+
240+
# %%
241+
node.dispose()

0 commit comments

Comments
 (0)