Skip to content

Commit 86d5a0a

Browse files
author
rob
committed
move ib contract details into master client
1 parent de1a027 commit 86d5a0a

8 files changed

+150
-35
lines changed

sysbrokers/IB/client/ib_client.py

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
from ib_insync import IB
55

66
from sysbrokers.IB.ib_connection import connectionIB
7+
from sysbrokers.IB.ib_contracts import ibContract
78
from sysbrokers.IB.config.ib_instrument_config import (
89
IBconfig,
910
read_ib_config_from_file,
1011
get_instrument_code_from_broker_code,
1112
)
1213

14+
from syscore.constants import arg_not_supplied
15+
from syscore.cache import Cache
16+
from syscore.exceptions import missingContract
17+
1318
from syslogdiag.logger import logger
1419
from syslogdiag.log_to_screen import logtoscreen
1520

@@ -75,6 +80,11 @@ def __init__(
7580

7681
self._ib_connnection = ibconnection
7782
self._log = log
83+
self._cache = Cache(self)
84+
85+
@property
86+
def cache(self):
87+
return self._cache
7888

7989
@property
8090
def ib_connection(self) -> connectionIB:
@@ -105,13 +115,35 @@ def error_handler(
105115
:param contract: IB contract or None
106116
:return: success
107117
"""
118+
119+
msg = "Reqid %d: %d %s" % (reqid, error_code, error_string)
120+
121+
log_to_use = self._get_log_for_contract(contract)
122+
123+
iserror = error_code in IB_IS_ERROR
124+
if iserror:
125+
# Serious requires some action
126+
myerror_type = IB_ERROR_TYPES.get(error_code, "generic")
127+
self.broker_error(msg=msg, myerror_type=myerror_type, log=log_to_use)
128+
129+
else:
130+
# just a general message
131+
self.broker_message(msg=msg, log=log_to_use)
132+
133+
def _get_log_for_contract(self, contract: Contract) -> logger:
108134
if contract is None:
109-
ib_contract_str = ""
110135
log_to_use = self.log.setup()
111136
else:
112137
ib_instrument_code = contract.symbol
113138
ib_expiry_str = contract.lastTradeDateOrContractMonth
114-
ib_contract_str = str("%s %s" % (ib_instrument_code, ib_expiry_str))
139+
140+
contract_details = self.contract_details(
141+
contract, allow_expired=False, allow_multiple_contracts=False
142+
)
143+
144+
## FIXME: REPLACE WITH GET EXTENDED CONTRACT DETAILS?
145+
ib_exchange = arg_not_supplied
146+
ib_multiplier = arg_not_supplied
115147

116148
instrument_code = self.get_instrument_code_from_broker_code(
117149
ib_instrument_code
@@ -120,17 +152,7 @@ def error_handler(
120152
futures_contract = futuresContract(instrument_code, ib_expiry_str)
121153
log_to_use = futures_contract.specific_log(self.log)
122154

123-
msg = "Reqid %d: %d %s %s" % (reqid, error_code, error_string, ib_contract_str)
124-
125-
iserror = error_code in IB_IS_ERROR
126-
if iserror:
127-
# Serious requires some action
128-
myerror_type = IB_ERROR_TYPES.get(error_code, "generic")
129-
self.broker_error(msg=msg, myerror_type=myerror_type, log=log_to_use)
130-
131-
else:
132-
# just a general message
133-
self.broker_message(msg=msg, log=log_to_use)
155+
return log_to_use
134156

135157
def broker_error(self, msg, log, myerror_type):
136158
log.warn(msg)
@@ -141,9 +163,18 @@ def broker_message(self, log, msg):
141163
def refresh(self):
142164
self.ib.sleep(0.00001)
143165

144-
def get_instrument_code_from_broker_code(self, ib_code: str) -> str:
166+
def get_instrument_code_from_broker_code(
167+
self,
168+
ib_code: str,
169+
ib_multiplier: float = arg_not_supplied,
170+
ib_exchange: str = arg_not_supplied,
171+
) -> str:
145172
instrument_code = get_instrument_code_from_broker_code(
146-
log=self.log, ib_code=ib_code, config=self.ib_config
173+
log=self.log,
174+
ib_code=ib_code,
175+
config=self.ib_config,
176+
ib_multiplier=ib_multiplier,
177+
ib_exchange=ib_exchange,
147178
)
148179
return instrument_code
149180

@@ -160,3 +191,31 @@ def _get_and_set_ib_config_from_file(self) -> IBconfig:
160191
config_data = read_ib_config_from_file(log=self.log)
161192

162193
return config_data
194+
195+
def contract_details(
196+
self,
197+
ib_contract_pattern: ibContract,
198+
allow_expired: bool = False,
199+
allow_multiple_contracts: bool = False,
200+
):
201+
contract_details = self.cache.get(
202+
self._contract_details, ib_contract_pattern, allow_expired=allow_expired
203+
)
204+
205+
if len(contract_details) == 0:
206+
raise missingContract
207+
208+
if allow_multiple_contracts:
209+
return contract_details
210+
211+
elif len(contract_details) > 1:
212+
self.log.critical("Multiple contracts and only expected one")
213+
214+
return contract_details[0]
215+
216+
def _contract_details(
217+
self, ib_contract_pattern: ibContract, allow_expired: bool = False
218+
):
219+
ib_contract_pattern.includeExpired = allow_expired
220+
221+
return self.ib.reqContractDetails(ib_contract_pattern)

sysbrokers/IB/client/ib_contracts_client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,12 @@ def ib_get_contract_chain(
588588
:param ibcontract_pattern: ibContract which may not fully specify the contract
589589
:return: list of ibContracts
590590
"""
591-
ibcontract_pattern.includeExpired = allow_expired
592-
new_contract_details_list = self.ib.reqContractDetails(ibcontract_pattern)
591+
592+
new_contract_details_list = self.contract_details(
593+
ibcontract_pattern,
594+
allow_expired=allow_expired,
595+
allow_multiple_contracts=True,
596+
)
593597

594598
ibcontract_list = [
595599
contract_details.contract for contract_details in new_contract_details_list

sysbrokers/IB/config/ib_instrument_config.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
NOT_REQUIRED_FOR_IB,
66
ibInstrumentConfigData,
77
)
8-
from syscore.constants import missing_file, missing_instrument
8+
from syscore.constants import missing_file, missing_instrument, arg_not_supplied
99
from syscore.fileutils import resolve_path_and_filename_for_package
1010
from syscore.genutils import return_another_value_if_nan
1111
from syslogdiag.log_to_screen import logtoscreen
@@ -99,7 +99,11 @@ def _get_instrument_object_from_valid_config(
9999

100100

101101
def get_instrument_code_from_broker_code(
102-
config: IBconfig, ib_code: str, log: logger = logtoscreen("")
102+
config: IBconfig,
103+
ib_code: str,
104+
log: logger = logtoscreen(""),
105+
ib_exchange: str = arg_not_supplied,
106+
ib_multiplier: float = arg_not_supplied,
103107
) -> str:
104108

105109
config_row = config[config.IBSymbol == ib_code]
@@ -110,17 +114,27 @@ def get_instrument_code_from_broker_code(
110114

111115
if len(config_row) > 1:
112116
## need to resolve with multiplier
113-
instrument_code = get_instrument_code_from_broker_code_with_multiplier(
114-
ib_code=ib_code, config=config, log=log
117+
instrument_code = (
118+
get_instrument_code_from_broker_code_with_multiple_possibilities(
119+
ib_code=ib_code,
120+
config=config,
121+
log=log,
122+
ib_exchange=ib_exchange,
123+
ib_multiplier=ib_multiplier,
124+
)
115125
)
116126
else:
117127
instrument_code = config_row.iloc[0].Instrument
118128

119129
return instrument_code
120130

121131

122-
def get_instrument_code_from_broker_code_with_multiplier(
123-
config: IBconfig, ib_code: str, log: logger = logtoscreen("")
132+
def get_instrument_code_from_broker_code_with_multiple_possibilities(
133+
config: IBconfig,
134+
ib_code: str,
135+
log: logger = logtoscreen(""),
136+
ib_exchange: str = arg_not_supplied,
137+
ib_multiplier: float = arg_not_supplied,
124138
) -> str:
125139

126140
# FIXME PATCH

sysbrokers/IB/ib_contract_position_data.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,29 @@ def _get_contract_position_for_raw_entry(self, position_entry) -> contractPositi
7979
if position == 0:
8080
raise missingContract
8181

82-
ib_code = position_entry["symbol"]
83-
ib_multiplier = position_entry["multiplier"]
84-
instrument_code = (
85-
self.futures_instrument_data.get_instrument_code_from_broker_code(ib_code)
86-
)
87-
8882
expiry = position_entry["expiry"]
89-
83+
instrument_code = self._get_instrument_code_from_ib_position_entry(
84+
position_entry
85+
)
9086
contract = futuresContract(instrument_code, expiry)
9187

9288
contract_position_object = contractPosition(position, contract)
9389

9490
return contract_position_object
9591

92+
def _get_instrument_code_from_ib_position_entry(self, position_entry) -> str:
93+
94+
ib_code = position_entry["symbol"]
95+
ib_multiplier = position_entry["multiplier"]
96+
ib_exchange = position_entry["exchange"]
97+
instrument_code = (
98+
self.futures_instrument_data.get_instrument_code_from_broker_code(
99+
ib_code, ib_exchange=ib_exchange, ib_multiplier=ib_multiplier
100+
)
101+
)
102+
103+
return instrument_code
104+
96105
def _get_all_futures_positions_as_raw_list(
97106
self, account_id: str = arg_not_supplied
98107
) -> list:

sysbrokers/IB/ib_instruments_data.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
)
1212
from sysbrokers.IB.ib_connection import connectionIB
1313
from sysbrokers.broker_instrument_data import brokerFuturesInstrumentData
14+
15+
from syscore.constants import arg_not_supplied
16+
1417
from sysdata.data_blob import dataBlob
1518

1619
from syslogdiag.log_to_screen import logtoscreen
@@ -41,10 +44,19 @@ def __repr__(self):
4144
def ibconnection(self) -> connectionIB:
4245
return self._ibconnection
4346

44-
def get_instrument_code_from_broker_code(self, ib_code: str) -> str:
47+
def get_instrument_code_from_broker_code(
48+
self,
49+
ib_code: str,
50+
ib_multiplier: float = arg_not_supplied,
51+
ib_exchange: str = arg_not_supplied,
52+
) -> str:
4553
config = self.ib_config
4654
broker_code = get_instrument_code_from_broker_code(
47-
config=config, ib_code=ib_code, log=self.log
55+
config=config,
56+
ib_code=ib_code,
57+
log=self.log,
58+
ib_multiplier=ib_multiplier,
59+
ib_exchange=ib_exchange,
4860
)
4961

5062
return broker_code

sysbrokers/IB/ib_orders.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,22 @@ def _create_broker_control_order_object(
210210
"""
211211
try:
212212
try:
213+
## FIXME THIS IS HORRIFIC
214+
ib_contract = (
215+
trade_with_contract_from_ib.ibcontract_with_legs.ibcontract
216+
)
217+
ib_instrument_code = trade_with_contract_from_ib.ib_instrument_code
218+
219+
contract_details = self.ib_client.ib.reqContractDetails(ib_contract)
220+
221+
ib_exchange = contract_details[0].validExchanges
222+
ib_multiplier = contract_details[0].evMultiplier # is this right?
223+
213224
instrument_code = (
214225
self.futures_instrument_data.get_instrument_code_from_broker_code(
215-
trade_with_contract_from_ib.ib_instrument_code
226+
ib_instrument_code,
227+
ib_exchange=ib_exchange,
228+
ib_multiplier=ib_multiplier,
216229
)
217230
)
218231
except:

sysbrokers/IB/ib_translate_broker_order_objects.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dateutil.tz import tz
66

77
from ib_insync import Trade as ibTrade
8-
from sysbrokers.IB.ib_contracts import ibcontractWithLegs
8+
from sysbrokers.IB.ib_contracts import ibcontractWithLegs, ibContract
99
from sysbrokers.broker_trade import brokerTrade
1010
from syscore.exceptions import missingData
1111
from syscore.constants import arg_not_supplied
@@ -41,6 +41,10 @@ def trade(self) -> ibTrade:
4141
def ib_instrument_code(self):
4242
return self.trade.contract.symbol
4343

44+
@property
45+
def ib_contract(self) -> ibContract:
46+
return self.trade.contract
47+
4448

4549
class ibBrokerOrder(brokerOrder):
4650
@classmethod

sysbrokers/broker_instrument_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self, data: dataBlob, log=logtoscreen("brokerFuturesInstrumentData"
1717
super().__init__(log=log)
1818
self._data = data
1919

20-
def get_instrument_code_from_broker_code(self, broker_code: str) -> str:
20+
def get_instrument_code_from_broker_code(self, broker_code: str, **kwargs) -> str:
2121
raise NotImplementedError
2222

2323
def get_list_of_instruments(self) -> list:

0 commit comments

Comments
 (0)