Skip to content

Commit 8860c46

Browse files
authored
Add support for additional market data bar types (#61)
* Add support for additional market data bar types * clarify ports & add comments
1 parent 413bdca commit 8860c46

File tree

3 files changed

+314
-4
lines changed

3 files changed

+314
-4
lines changed

examples/example_all_functionality.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,38 @@ def get_contracts() -> Dict[str, Contract]:
299299
bar_type=dhib.BarDataType.OPTION_IMPLIED_VOLATILITY, keep_up_to_date=False)
300300
client.request_bars_historical(rc, duration=dhib.Duration.days(10), bar_size=dhib.BarSize.MIN_5,
301301
bar_type=dhib.BarDataType.TRADES)
302+
client.request_bars_historical(rc, duration=dhib.Duration.days(10), bar_size=dhib.BarSize.MIN_5,
303+
bar_type=dhib.BarDataType.ADJUSTED_LAST, keep_up_to_date=False)
302304

303305
client.request_bars_realtime(rc, bar_type=dhib.BarDataType.MIDPOINT)
304306
client.request_bars_realtime(rc, bar_type=dhib.BarDataType.BID)
305307
client.request_bars_realtime(rc, bar_type=dhib.BarDataType.ASK)
306308
client.request_bars_realtime(rc, bar_type=dhib.BarDataType.TRADES)
307309

310+
311+
print("==============================================================================================================")
312+
print("==== Request bars (bonds).")
313+
print("==============================================================================================================")
314+
315+
# enter CUSIP as symbol
316+
contract = Contract()
317+
contract.symbol = "IBCID411964960"
318+
contract.secType = "BOND"
319+
contract.exchange = "SMART"
320+
contract.currency = "USD"
321+
322+
rc = client.get_registered_contract(contract)
323+
print(contract)
324+
325+
client.request_bars_historical(rc, duration=dhib.Duration.days(22), bar_size=dhib.BarSize.DAY_1,
326+
bar_type=dhib.BarDataType.YIELD_BID, keep_up_to_date=False)
327+
client.request_bars_historical(rc, duration=dhib.Duration.days(22), bar_size=dhib.BarSize.DAY_1,
328+
bar_type=dhib.BarDataType.YIELD_ASK, keep_up_to_date=False)
329+
client.request_bars_historical(rc, duration=dhib.Duration.days(22), bar_size=dhib.BarSize.DAY_1,
330+
bar_type=dhib.BarDataType.YIELD_BID_ASK, keep_up_to_date=False)
331+
client.request_bars_historical(rc, duration=dhib.Duration.days(22), bar_size=dhib.BarSize.DAY_1,
332+
bar_type=dhib.BarDataType.YIELD_LAST, keep_up_to_date=False)
333+
308334
print("==============================================================================================================")
309335
print("==== Request tick data.")
310336
print("==============================================================================================================")

examples/example_beta_calc.py

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
## Set the API port. Default port numbers are:
2+
# 7496 - Trader Workstation, real trading
3+
# 4001 - IB Gateway, real trading
4+
# 7497 - Trader Workstation, paper trading
5+
# 4002 - IB Gateway, paper trading
6+
API_PORT = 7497
7+
8+
import deephaven_ib as dhib
9+
10+
# Disable read-only mode when connecting to the default ports for paper trading:
11+
if API_PORT == 7497 or API_PORT == 4002:
12+
read_only_api = False
13+
else:
14+
read_only_api = True
15+
16+
client = dhib.IbSessionTws(host="host.docker.internal", port=API_PORT, read_only=read_only_api)
17+
client.connect()
18+
19+
if client.is_connected():
20+
print('Client connected!')
21+
else:
22+
raise RuntimeError("Client not connected!")
23+
24+
25+
def check_table_size(dh_table, table_name, expected_size=1):
26+
table_size = dh_table.size
27+
if (table_size < expected_size):
28+
raise RuntimeError(
29+
'Table "' + table_name + '" has ' + str(table_size) + ' rows! (Expected ' + str(expected_size) + '.)')
30+
else:
31+
print('Found ' + str(table_size) + ' rows in table "' + table_name + '".')
32+
33+
34+
# Get the Deephaven table of position updates, and use 'last_by' to find the
35+
# current positions (i.e. last row for each ContractId):
36+
positions = client.tables['accounts_positions'].last_by(['ContractId'])
37+
38+
positions.j_table.awaitUpdate()
39+
40+
check_table_size(positions, "pos")
41+
42+
##########
43+
##########
44+
##########
45+
46+
import numpy as np
47+
from deephaven.pandas import to_pandas
48+
49+
# Get a DH table containing only the distinct Symbols:
50+
pos_syms = positions.select_distinct(['Symbol'])
51+
mkt_data_syms_set = set(to_pandas(pos_syms)['Symbol'].values)
52+
print('Found ' + str(len(mkt_data_syms_set)) + ' position symbols: ' + str(mkt_data_syms_set))
53+
54+
# Add SPY to the set of symbols to request data for:
55+
mkt_data_syms_set.add('SPY')
56+
57+
from ibapi.contract import Contract
58+
59+
c = Contract()
60+
c.secType = 'STK'
61+
c.exchange = 'SMART'
62+
c.currency = 'USD'
63+
64+
c.symbol = None
65+
for sym in mkt_data_syms_set:
66+
print('Requesting data for symbol=' + str(sym))
67+
c.symbol = sym
68+
69+
rc = client.get_registered_contract(c)
70+
client.request_bars_historical(
71+
rc,
72+
duration=dhib.Duration.days(253),
73+
bar_size=dhib.BarSize.DAY_1,
74+
bar_type=dhib.BarDataType.ADJUSTED_LAST,
75+
keep_up_to_date=False
76+
)
77+
78+
# Retrieve the Deephaven table of historical data bars:
79+
hist_data_bars = client.tables['bars_historical']
80+
81+
# Wait for data to be retrieved:
82+
from time import sleep
83+
84+
sleep(5)
85+
86+
hist_data_bars.j_table.awaitUpdate()
87+
hist_data_recvd_syms = hist_data_bars.select_distinct(['Symbol'])
88+
check_table_size(hist_data_recvd_syms, 'hist_data_recvd_syms', len(mkt_data_syms_set))
89+
90+
##########
91+
##########
92+
##########
93+
94+
# Use 'colname_[i-1]' to read a value from the previous row
95+
hist_data_with_return = hist_data_bars \
96+
.update_view(formulas=[
97+
'SameTickerAsPrevRow = Symbol=Symbol_[i-1]',
98+
'Last = !SameTickerAsPrevRow ? null : Close_[i-1]',
99+
'Chg = Close - Last',
100+
'Return = Chg/Last',
101+
])
102+
103+
# Join the SPY returns onto the returns for all stocks
104+
spy = hist_data_with_return.where("Symbol=`SPY`")
105+
hist_data_with_spy = hist_data_with_return.natural_join(spy, ['Timestamp'], ['SPY_Return=Return'])
106+
107+
##########
108+
##########
109+
##########
110+
111+
112+
# Install sklearn and run a linear regression to calculate betas
113+
print("Installing sklearn...")
114+
import os
115+
116+
os.system("pip install sklearn")
117+
from sklearn.linear_model import LinearRegression
118+
119+
## Use a DynamicTableWriter to store regression results in a Deephaven table
120+
import deephaven.dtypes as dht
121+
from deephaven import DynamicTableWriter
122+
from deephaven.table import Table
123+
124+
table_writer = DynamicTableWriter(
125+
{"Symbol": dht.string,
126+
"Beta": dht.double,
127+
"Intercept": dht.double,
128+
"R2": dht.double
129+
}
130+
)
131+
regression_results = table_writer.table
132+
133+
# Partition the table, creating a distinct table for each Symbol:
134+
data_partitioned = hist_data_with_spy.partition_by(['Symbol'])
135+
136+
print('Calculating betas...')
137+
for symbol in mkt_data_syms_set:
138+
print('Calculating beta for ' + symbol + '...')
139+
returns_for_betas = data_partitioned.get_constituent(symbol) \
140+
.where(['!isNull(Return)', '!isNull(SPY_Return)'])
141+
142+
returns_for_betas_df = to_pandas(returns_for_betas)
143+
144+
reg = LinearRegression()
145+
X = returns_for_betas_df['SPY_Return'].values.reshape(-1, 1)
146+
Y = returns_for_betas_df['Return']
147+
reg.fit(X, Y)
148+
r2 = reg.score(X, Y).real
149+
150+
print(symbol + ' coef: ' + str(reg.coef_) +
151+
'; intercept: ' + str(reg.intercept_) +
152+
'; R2: ', str(r2))
153+
154+
# Append to the 'regression_results' table:
155+
table_writer.write_row(
156+
symbol,
157+
reg.coef_[0],
158+
reg.intercept_,
159+
r2
160+
)
161+
print('Finished calculating betas!')
162+
163+
##########
164+
##########
165+
##########
166+
167+
168+
# Request live prices:
169+
ticks_price = client.tables['ticks_price']
170+
live_prices = ticks_price.last_by(['ContractId'])
171+
172+
for sym in mkt_data_syms_set:
173+
print('Requesting data for symbol=' + str(sym))
174+
c.symbol = sym
175+
rc = client.get_registered_contract(c)
176+
client.request_market_data(
177+
rc,
178+
snapshot=False
179+
)
180+
181+
sleep(2)
182+
live_prices.j_table.awaitUpdate()
183+
check_table_size(live_prices, 'live_prices', len(mkt_data_syms_set))
184+
185+
##########
186+
##########
187+
##########
188+
189+
# Join the table of betas onto the positions
190+
pos_with_beta = positions.natural_join(live_prices, ['ContractId'], ['Price']) \
191+
.natural_join(regression_results, ['Symbol'], ['Beta', 'R2']) \
192+
.view([
193+
'Symbol',
194+
'ContractId',
195+
'SecType',
196+
'Currency',
197+
'Position',
198+
'PosValue = Position * Price',
199+
'Price',
200+
'AvgCost',
201+
'PNL = PosValue - AvgCost * Position',
202+
'Beta',
203+
'R2',
204+
'SPYBetaValue = Beta * PosValue',
205+
])
206+
207+
##########
208+
##########
209+
##########
210+
211+
# Calculate hedge, excluding positions with a very low R2:
212+
hedge_shares = pos_with_beta \
213+
.view([
214+
'PosValue',
215+
'WeightedBeta = Beta * PosValue',
216+
'SPYBetaValue',
217+
'SPYBetaValueForHedge = R2 > 1/5 ? SPYBetaValue : 0'
218+
]) \
219+
.sum_by() \
220+
.natural_join(live_prices.where('Symbol=`SPY`'), [], ['SPY_Price=Price']) \
221+
.view([
222+
'PortfolioValue = PosValue',
223+
'PortfolioBeta = WeightedBeta / PosValue',
224+
'SPYBetaValue',
225+
'SPYBetaValueForHedge',
226+
'HedgeShares = -round(SPYBetaValueForHedge / SPY_Price)',
227+
'HedgeCost = HedgeShares * SPY_Price',
228+
'SPY_Price'
229+
])
230+
231+
##########
232+
##########
233+
##########
234+
235+
# Set send_hedge_order to True to submit the order, not just generate it.
236+
# (Must also set read_only to False when creating the IbSessionTws instance.)
237+
send_hedge_order = False
238+
239+
from ibapi.order import Order
240+
241+
c.symbol = "SPY"
242+
rc = client.get_registered_contract(c)
243+
print(c)
244+
245+
# Extract the hedge information from the hedge_shares table:
246+
hedge_info = hedge_shares.j_table.getRecord(0, 'HedgeShares', 'SPY_Price')
247+
hedge_qty = hedge_info[0]
248+
hedge_last_px = hedge_info[1]
249+
hedge_side = "BUY" if hedge_qty > 0 else "SELL"
250+
hedge_limit_px = hedge_last_px + 0.05 * (1 if hedge_side is "BUY" else -1)
251+
252+
# Create an order with the IB API:
253+
order = Order()
254+
# order.account = "<account number>"
255+
order.action = hedge_side
256+
order.orderType = "LIMIT"
257+
order.totalQuantity = hedge_qty
258+
order.lmtPrice = hedge_limit_px
259+
order.eTradeOnly = False
260+
order.firmQuoteOnly = False
261+
262+
print('Order: ' + str(order))
263+
264+
if send_hedge_order:
265+
print('***** Sending order to ' + order.action + ' ' + str(
266+
order.totalQuantity) + ' shares of ' + c.symbol + '! *****')
267+
req = client.order_place(rc, order)
268+
269+
else:
270+
print('Not actually sending order.')
271+
272+
# To cancel orders:
273+
# req.cancel()
274+
# client.order_cancel_all()

src/deephaven_ib/__init__.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,24 @@ class BarDataType(Enum):
132132
"""Ask prices."""
133133
BID_ASK = 5
134134
"""Bid/Ask prices."""
135-
HISTORICAL_VOLATILITY = 6
135+
ADJUSTED_LAST = 6
136+
"""Bid/Ask prices."""
137+
HISTORICAL_VOLATILITY = 7
136138
"""Historical volatility."""
137-
OPTION_IMPLIED_VOLATILITY = 7
139+
OPTION_IMPLIED_VOLATILITY = 8
138140
"""Option implied volatility."""
139-
FEE_RATE = 8
140-
"""Fee rate."""
141141
REBATE_RATE = 9
142142
"""Rebate rate."""
143+
FEE_RATE = 10
144+
"""Fee rate."""
145+
YIELD_BID = 11
146+
"""Bid yield."""
147+
YIELD_ASK = 12
148+
"""Ask yield."""
149+
YIELD_BID_ASK = 13
150+
"""Bid/Ask yield."""
151+
YIELD_LAST = 14
152+
"""Last yield."""
143153

144154

145155
class BarSize(Enum):

0 commit comments

Comments
 (0)