ib.feed: handle fiat (forex) pairs with `Asset`

Also finally adds full `FeedInit` and `MktPair` support for this backend
by handling:
- all "currency" fields for each `Contract` by constructing
  and `Asset` and setting the `MktPair.src` with `.atype='fiat'`.
- always render the `MktPair.src` name in the `.fqme` for fiat pairs
  (aka forex) but never for other instruments.
account_tests
Tyler Goodlet 2023-06-29 14:08:42 -04:00
parent 10ebc855e4
commit 745c144314
1 changed files with 24 additions and 33 deletions

View File

@ -140,32 +140,21 @@ async def open_history_client(
# memory.
# IB's internal symbology does not expect the "source asset" in
# the "symbol name", what we call the "market name". This is
# the "symbol name", what we call the "market pair name". This is
# common in most legacy market brokers since it's presumed that
# given a certain stock exchange, listed assets are traded
# "from" a particular source fiat, normally something like USD.
if (
mkt.src
and mkt.src.atype == 'fiat'
):
# "from" a particular source fiat, normally something like USD
# on the given venue-provider, eg. nasdaq, nyse, etc.
fqme_kwargs: dict[str, Any] = {}
if mkt.dst.atype == 'forex':
# XXX: for now we do need the src token kept in since
if mkt.dst.atype != 'fiat':
fqme_kwargs = {
'without_src': False, # default is True
'without_src': True, # default is True
'delim_char': '', # bc they would normally use a frickin `.` smh
}
fqme: str = mkt.get_bs_fqme(**(fqme_kwargs))
else:
fqme = mkt.bs_fqme
async with open_data_client() as proxy:
max_timeout: float = 2.
mean: float = 0
count: int = 0
@ -178,7 +167,8 @@ async def open_history_client(
try:
head_dt = await proxy.get_head_time(fqme=fqme)
except RequestError:
head_dt = None
log.warning(f'Unable to get head time: {fqme} ?')
pass
async def get_hist(
timeframe: float,
@ -576,7 +566,7 @@ _asset_type_map = {
'OPT': 'option',
'FUT': 'future',
'CONTFUT': 'continuous_future',
'CASH': 'forex',
'CASH': 'fiat',
'IND': 'index',
'CFD': 'cfd',
'BOND': 'bond',
@ -837,7 +827,9 @@ async def get_mkt_info(
# if con.secType == 'STK':
size_tick = Decimal('1')
else:
size_tick: Decimal = Decimal(str(details.minSize).rstrip('0'))
size_tick: Decimal = Decimal(
str(details.minSize).rstrip('0')
)
# |-> TODO: there is also the Contract.sizeIncrement, bt wtf is it?
# NOTE: this is duplicate from the .broker.norm_trade_records()
@ -853,10 +845,8 @@ async def get_mkt_info(
# we need to figure out how we're going to handle this (later?)
# but likely we want all backends to eventually handle
# ``dst/src.venue.`` style !?
src: str | Asset = ''
if atype == 'forex':
src = Asset(
name=str(con.currency),
name=str(con.currency).lower(),
atype='fiat',
tx_tick=Decimal('0.01'), # right?
)
@ -879,6 +869,7 @@ async def get_mkt_info(
# TODO: options contract info as str?
# contract_info=<optionsdetails>
_fqme_without_src=(atype != 'fiat'),
)
return mkt, details
@ -920,7 +911,7 @@ async def stream_quotes(
init_msg = FeedInit(mkt_info=mkt)
if mkt.dst.atype in {
'forex',
'fiat',
'index',
'commodity',
}:
@ -947,7 +938,7 @@ async def stream_quotes(
isnan(first_ticker.last) # last quote price value is nan
and mkt.dst.atype not in {
'commodity',
'forex',
'fiat',
'crypto',
}
):