Unify contract->fqsn translation with new cached-helper
parent
070b9f3dc1
commit
a27431c34f
|
@ -424,23 +424,15 @@ class Client:
|
|||
# one set per future result
|
||||
details = {}
|
||||
for details_set in results:
|
||||
|
||||
# XXX: if there is more then one entry in the details list
|
||||
# then the contract is so called "ambiguous".
|
||||
for d in details_set:
|
||||
con = d.contract
|
||||
|
||||
key = '.'.join([
|
||||
con.symbol,
|
||||
con.primaryExchange or con.exchange,
|
||||
])
|
||||
expiry = con.lastTradeDateOrContractMonth
|
||||
if expiry:
|
||||
key += f'.{expiry}'
|
||||
|
||||
# nested dataclass we probably don't need and that
|
||||
# won't IPC serialize..
|
||||
# nested dataclass we probably don't need and that won't
|
||||
# IPC serialize..
|
||||
d.secIdList = ''
|
||||
|
||||
key, calc_price = con2fqsn(d.contract)
|
||||
details[key] = d
|
||||
|
||||
return details
|
||||
|
@ -470,7 +462,7 @@ class Client:
|
|||
self,
|
||||
pattern: str,
|
||||
# how many contracts to search "up to"
|
||||
upto: int = 3,
|
||||
upto: int = 6,
|
||||
asdicts: bool = True,
|
||||
|
||||
) -> dict[str, ContractDetails]:
|
||||
|
@ -522,10 +514,14 @@ class Client:
|
|||
elif sectype == 'CASH':
|
||||
dst, src = tract.localSymbol.split('.')
|
||||
pair_key = "/".join([dst, src])
|
||||
tract.exchange = 'FOREX'
|
||||
results[f'{pair_key}.forex'] = tract
|
||||
exch = tract.exchange.lower()
|
||||
results[f'{pair_key}.{exch}'] = tract
|
||||
results.pop(key)
|
||||
|
||||
# XXX: again seems to trigger the weird tractor
|
||||
# bug with the debugger..
|
||||
# assert 0
|
||||
|
||||
return results
|
||||
|
||||
async def get_fute(
|
||||
|
@ -595,12 +591,11 @@ class Client:
|
|||
|
||||
# another hack for forex pairs lul.
|
||||
if (
|
||||
'.forex' in symbol
|
||||
# and not '.ib' in pattern
|
||||
'.idealpro' in symbol
|
||||
# or '/' in symbol
|
||||
):
|
||||
exch = 'FOREX'
|
||||
symbol = symbol.removesuffix('.forex')
|
||||
exch = 'IDEALPRO'
|
||||
symbol = symbol.removesuffix('.idealpro')
|
||||
if '/' in symbol:
|
||||
symbol, currency = symbol.split('/')
|
||||
|
||||
|
@ -665,7 +660,7 @@ class Client:
|
|||
)
|
||||
|
||||
elif (
|
||||
exch in ('FOREX')
|
||||
exch in ('IDEALPRO')
|
||||
or sectype == 'CASH'
|
||||
):
|
||||
# if '/' in symbol:
|
||||
|
@ -948,6 +943,58 @@ class Client:
|
|||
return self.ib.positions(account=account)
|
||||
|
||||
|
||||
def con2fqsn(
|
||||
con: Contract,
|
||||
_cache: dict[int, (str, bool)] = {}
|
||||
|
||||
) -> tuple[str, bool]:
|
||||
'''
|
||||
Convert contracts to fqsn-style strings to be used both in symbol-search
|
||||
matching and as feed tokens passed to the front end data deed layer.
|
||||
|
||||
Previously seen contracts are cached by id.
|
||||
|
||||
'''
|
||||
# should be real volume for this contract by default
|
||||
calc_price = False
|
||||
if con.conId:
|
||||
try:
|
||||
return _cache[con.conId]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
suffix = con.primaryExchange or con.exchange
|
||||
symbol = con.symbol
|
||||
expiry = con.lastTradeDateOrContractMonth or ''
|
||||
|
||||
match con:
|
||||
case ibis.Commodity():
|
||||
# commodities and forex don't have an exchange name and
|
||||
# no real volume so we have to calculate the price
|
||||
suffix = con.secType
|
||||
|
||||
# no real volume on this tract
|
||||
calc_price = True
|
||||
|
||||
case ibis.Forex() | ibis.Contract(secType='CASH'):
|
||||
dst, src = con.localSymbol.split('.')
|
||||
symbol = ''.join([dst, src])
|
||||
suffix = con.exchange
|
||||
|
||||
# no real volume on forex feeds..
|
||||
calc_price = True
|
||||
|
||||
# append a `.<suffix>` to the returned symbol
|
||||
# key for derivatives that normally is the expiry
|
||||
# date key.
|
||||
if expiry:
|
||||
suffix += f'.{expiry}'
|
||||
|
||||
fqsn_key = '.'.join((symbol, suffix)).lower()
|
||||
_cache[con.conId] = fqsn_key, calc_price
|
||||
return fqsn_key, calc_price
|
||||
|
||||
|
||||
# per-actor API ep caching
|
||||
_client_cache: dict[tuple[str, int], Client] = {}
|
||||
_scan_ignore: set[tuple[str, int]] = set()
|
||||
|
|
|
@ -42,6 +42,7 @@ from piker.data._sharedmem import ShmArray
|
|||
from .._util import SymbolNotFound, NoData
|
||||
from .api import (
|
||||
# _adhoc_futes_set,
|
||||
con2fqsn,
|
||||
log,
|
||||
load_aio_clients,
|
||||
ibis,
|
||||
|
@ -559,47 +560,18 @@ async def open_aio_quote_stream(
|
|||
|
||||
|
||||
# TODO: cython/mypyc/numba this!
|
||||
# or we can at least cache a majority of the values
|
||||
# except for the ones we expect to change?..
|
||||
def normalize(
|
||||
ticker: Ticker,
|
||||
calc_price: bool = False
|
||||
|
||||
) -> dict:
|
||||
|
||||
# should be real volume for this contract by default
|
||||
calc_price = False
|
||||
|
||||
# check for special contract types
|
||||
con = ticker.contract
|
||||
symbol = con.symbol
|
||||
|
||||
if type(con) in (
|
||||
ibis.Commodity,
|
||||
):
|
||||
# commodities and forex don't have an exchange name and
|
||||
# no real volume so we have to calculate the price
|
||||
suffix = con.secType
|
||||
# no real volume on this tract
|
||||
calc_price = True
|
||||
|
||||
elif type(con) in (
|
||||
ibis.Forex,
|
||||
):
|
||||
suffix = 'forex'
|
||||
symbol = con.pair()
|
||||
# no real volume on forex feeds..
|
||||
calc_price = True
|
||||
|
||||
else:
|
||||
suffix = con.primaryExchange
|
||||
if not suffix:
|
||||
suffix = con.exchange
|
||||
|
||||
# append a `.<suffix>` to the returned symbol
|
||||
# key for derivatives that normally is the expiry
|
||||
# date key.
|
||||
expiry = con.lastTradeDateOrContractMonth
|
||||
if expiry:
|
||||
suffix += f'.{expiry}'
|
||||
fqsn, calc_price = con2fqsn(con)
|
||||
|
||||
# convert named tuples to dicts so we send usable keys
|
||||
new_ticks = []
|
||||
|
@ -631,9 +603,7 @@ def normalize(
|
|||
|
||||
# generate fqsn with possible specialized suffix
|
||||
# for derivatives, note the lowercase.
|
||||
data['symbol'] = data['fqsn'] = '.'.join(
|
||||
(symbol, suffix)
|
||||
).lower()
|
||||
data['symbol'] = data['fqsn'] = fqsn
|
||||
|
||||
# convert named tuples to dicts for transport
|
||||
tbts = data.get('tickByTicks')
|
||||
|
|
Loading…
Reference in New Issue