ib: fill out contract tables in `.get_mkt_info()`

Since getting a global symcache result from the API is basically
impossible, we ad-hoc fill out the needed client tables on demand per
client code queries to the mkt info EP.

Also, use `unpack_fqme()` in fqme (search) pattern parser instead of
hacky `str.partition()`.
account_tests
Tyler Goodlet 2023-07-25 16:43:08 -04:00
parent 50b221f788
commit b33be86b2f
1 changed files with 32 additions and 17 deletions

View File

@ -37,6 +37,7 @@ import trio
from piker.accounting import ( from piker.accounting import (
Asset, Asset,
MktPair, MktPair,
unpack_fqme,
) )
from piker._cacheables import ( from piker._cacheables import (
async_lifo_cache, async_lifo_cache,
@ -49,6 +50,7 @@ from ._util import (
if TYPE_CHECKING: if TYPE_CHECKING:
from .api import ( from .api import (
MethodProxy, MethodProxy,
Client,
) )
_futes_venues = ( _futes_venues = (
@ -358,24 +360,23 @@ def parse_patt2fqme(
# fqme parsing stage # fqme parsing stage
# ------------------ # ------------------
if '.ib' in pattern: if '.ib' in pattern:
from piker.accounting import unpack_fqme
_, symbol, venue, expiry = unpack_fqme(pattern) _, symbol, venue, expiry = unpack_fqme(pattern)
else: else:
symbol = pattern symbol = pattern
expiry = '' expiry = ''
# another hack for forex pairs lul. # # another hack for forex pairs lul.
if ( # if (
'.idealpro' in symbol # '.idealpro' in symbol
# or '/' in symbol # # or '/' in symbol
): # ):
exch = 'IDEALPRO' # exch: str = 'IDEALPRO'
symbol = symbol.removesuffix('.idealpro') # symbol = symbol.removesuffix('.idealpro')
if '/' in symbol: # if '/' in symbol:
symbol, currency = symbol.split('/') # symbol, currency = symbol.split('/')
else: # else:
# TODO: yes, a cache.. # TODO: yes, a cache..
# try: # try:
# # give the cache a go # # give the cache a go
@ -387,9 +388,9 @@ def parse_patt2fqme(
symbol, _, expiry = symbol.rpartition('.') symbol, _, expiry = symbol.rpartition('.')
# use heuristics to figure out contract "type" # use heuristics to figure out contract "type"
symbol, exch = symbol.upper().rsplit('.', maxsplit=1) symbol, venue = symbol.upper().rsplit('.', maxsplit=1)
return symbol, currency, exch, expiry return symbol, currency, venue, expiry
def con2fqme( def con2fqme(
@ -406,9 +407,12 @@ def con2fqme(
''' '''
# should be real volume for this contract by default # should be real volume for this contract by default
calc_price = False calc_price: bool = False
if con.conId: if con.conId:
try: try:
# TODO: LOL so apparently IB just changes the contract
# ID (int) on a whim.. so we probably need to use an
# FQME style key after all...
return _cache[con.conId] return _cache[con.conId]
except KeyError: except KeyError:
pass pass
@ -475,8 +479,9 @@ async def get_mkt_info(
) -> tuple[MktPair, ibis.ContractDetails]: ) -> tuple[MktPair, ibis.ContractDetails]:
# XXX: we don't need to split off any fqme broker part? if '.ib' not in fqme:
# bs_fqme, _, broker = fqme.partition('.') fqme += '.ib'
broker, pair, venue, expiry = unpack_fqme(fqme)
proxy: MethodProxy proxy: MethodProxy
if proxy is not None: if proxy is not None:
@ -492,7 +497,7 @@ async def get_mkt_info(
( (
con, # Contract con, # Contract
details, # ContractDetails details, # ContractDetails
) = await proxy.get_sym_details(symbol=fqme) ) = await proxy.get_sym_details(fqme=fqme)
except ConnectionError: except ConnectionError:
log.exception(f'Proxy is ded {proxy._aio_ns}') log.exception(f'Proxy is ded {proxy._aio_ns}')
raise raise
@ -558,4 +563,14 @@ async def get_mkt_info(
_fqme_without_src=(atype != 'fiat'), _fqme_without_src=(atype != 'fiat'),
) )
# if possible register the bs_mktid to the just-built
# mkt so that it can be retreived by order mode tasks later.
# TODO NOTE: this is going to be problematic if/when we split
# out the datatd vs. brokerd actors since the mktmap lookup
# table will now be inaccessible..
if proxy is not None:
client: Client = proxy._aio_ns
client._contracts[mkt.bs_fqme] = con
client._cons2mkts[con] = mkt
return mkt, details return mkt, details