binance.feed: use `Client.get_assets()` for mkt pairs
Instead of constructing them (previously manually) in `.get_mkt_info()` ep, just call `.get_assets()` and do key lookups for assets to hand directly to the `.src/dst` of `MktPair`. Refine fqme input parsing to match: - adjust parsing logic to only use `unpack_fqme()` on the input fqme token. - set `.mkt_mode: str` to the derivs venue when an expiry token is detected in the fqme. - pass the parsed `expiry: str` to `Client.exch_info()` to ensure a deriv venue (table) is used for pair lookup. - skip any "DEFI" venue or other unknown asset type cases (since binance doesn't seem to define some assets anywhere?). Also, just use the `Client._pairs` unified table for search input since the first call to `.exch_info()` won't necessarily contain the most up-to-date state whereas `._pairs` always will.account_tests
parent
19be8348e5
commit
a2c6749112
|
@ -400,7 +400,7 @@ async def open_trade_dialog(
|
||||||
# and comparison with binance's own position calcs.
|
# and comparison with binance's own position calcs.
|
||||||
# - load pps and accounts using accounting apis, write
|
# - load pps and accounts using accounting apis, write
|
||||||
# the ledger and account files
|
# the ledger and account files
|
||||||
# - table: PpTable
|
# - table: Account
|
||||||
# - ledger: TransactionLedger
|
# - ledger: TransactionLedger
|
||||||
|
|
||||||
async with (
|
async with (
|
||||||
|
|
|
@ -24,8 +24,11 @@ from contextlib import (
|
||||||
aclosing,
|
aclosing,
|
||||||
)
|
)
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import (
|
||||||
|
partial,
|
||||||
|
)
|
||||||
import itertools
|
import itertools
|
||||||
|
from pprint import pformat
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
AsyncGenerator,
|
AsyncGenerator,
|
||||||
|
@ -54,7 +57,6 @@ from piker.accounting import (
|
||||||
DerivTypes,
|
DerivTypes,
|
||||||
MktPair,
|
MktPair,
|
||||||
unpack_fqme,
|
unpack_fqme,
|
||||||
digits_to_dec,
|
|
||||||
)
|
)
|
||||||
from piker.data.types import Struct
|
from piker.data.types import Struct
|
||||||
from piker.data.validate import FeedInit
|
from piker.data.validate import FeedInit
|
||||||
|
@ -277,69 +279,107 @@ async def open_history_client(
|
||||||
async def get_mkt_info(
|
async def get_mkt_info(
|
||||||
fqme: str,
|
fqme: str,
|
||||||
|
|
||||||
) -> tuple[MktPair, Pair]:
|
) -> tuple[MktPair, Pair] | None:
|
||||||
|
|
||||||
# uppercase since kraken bs_mktid is always upper
|
# uppercase since kraken bs_mktid is always upper
|
||||||
if 'binance' not in fqme:
|
if 'binance' not in fqme.lower():
|
||||||
fqme += '.binance'
|
fqme += '.binance'
|
||||||
|
|
||||||
bs_fqme, _, broker = fqme.rpartition('.')
|
mkt_mode: str = ''
|
||||||
broker, mkt_ep, venue, expiry = unpack_fqme(fqme)
|
broker, mkt_ep, venue, expiry = unpack_fqme(fqme)
|
||||||
|
venue: str = venue.lower()
|
||||||
|
|
||||||
|
# XXX TODO: we should change the usdtm_futes name to just
|
||||||
|
# usdm_futes (dropping the tether part) since it turns out that
|
||||||
|
# there are indeed USD-tokens OTHER THEN tether being used as
|
||||||
|
# the margin assets.. it's going to require a wholesale
|
||||||
|
# (variable/key) rename as well as file name adjustments to any
|
||||||
|
# existing tsdb set..
|
||||||
|
if 'usd' in venue:
|
||||||
|
mkt_mode: str = 'usdtm_futes'
|
||||||
|
|
||||||
|
# NO IDEA what these contracts (some kinda DEX-ish futes?) are
|
||||||
|
# but we're masking them for now..
|
||||||
|
elif (
|
||||||
|
'defi' in venue
|
||||||
|
|
||||||
|
# TODO: handle coinm futes which have a margin asset that
|
||||||
|
# is some crypto token!
|
||||||
|
# https://binance-docs.github.io/apidocs/delivery/en/#exchange-information
|
||||||
|
or 'btc' in venue
|
||||||
|
):
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
# NOTE: see the `FutesPair.bs_fqme: str` implementation
|
# NOTE: see the `FutesPair.bs_fqme: str` implementation
|
||||||
# to understand the reverse market info lookup below.
|
# to understand the reverse market info lookup below.
|
||||||
mkt_mode = venue = venue.lower() or 'spot'
|
mkt_mode = venue or 'spot'
|
||||||
_atype: str = ''
|
|
||||||
|
sectype: str = ''
|
||||||
if (
|
if (
|
||||||
venue
|
venue
|
||||||
and 'spot' not in venue.lower()
|
and 'spot' not in venue
|
||||||
|
|
||||||
# XXX: catch all in case user doesn't know which
|
# XXX: catch all in case user doesn't know which
|
||||||
# venue they want (usdtm vs. coinm) and we can choose
|
# venue they want (usdtm vs. coinm) and we can choose
|
||||||
# a default (via config?) once we support coin-m APIs.
|
# a default (via config?) once we support coin-m APIs.
|
||||||
or 'perp' in bs_fqme.lower()
|
or 'perp' in venue
|
||||||
):
|
):
|
||||||
mkt_mode: str = f'{venue.lower()}_futes'
|
if not mkt_mode:
|
||||||
if 'perp' in expiry:
|
mkt_mode: str = f'{venue}_futes'
|
||||||
_atype = 'perpetual_future'
|
|
||||||
|
|
||||||
else:
|
sectype: str = 'future'
|
||||||
_atype = 'future'
|
if 'perp' in expiry:
|
||||||
|
sectype = 'perpetual_future'
|
||||||
|
|
||||||
async with open_cached_client(
|
async with open_cached_client(
|
||||||
'binance',
|
'binance',
|
||||||
) as client:
|
) as client:
|
||||||
|
|
||||||
# switch mode depending on input pattern parsing
|
assets: dict[str, Asset] = await client.get_assets()
|
||||||
|
pair_str: str = mkt_ep.upper()
|
||||||
|
|
||||||
|
# switch venue-mode depending on input pattern parsing
|
||||||
|
# since we want to use a particular endpoint (set) for
|
||||||
|
# pair info lookup!
|
||||||
client.mkt_mode = mkt_mode
|
client.mkt_mode = mkt_mode
|
||||||
|
|
||||||
pair_str: str = mkt_ep.upper()
|
pair: Pair = await client.exch_info(
|
||||||
pair: Pair = await client.exch_info(pair_str)
|
pair_str,
|
||||||
|
venue=mkt_mode, # explicit
|
||||||
|
expiry=expiry,
|
||||||
|
)
|
||||||
|
|
||||||
if 'futes' in mkt_mode:
|
if 'futes' in mkt_mode:
|
||||||
assert isinstance(pair, FutesPair)
|
assert isinstance(pair, FutesPair)
|
||||||
|
|
||||||
|
dst: Asset | None = assets.get(pair.bs_dst_asset)
|
||||||
|
if (
|
||||||
|
not dst
|
||||||
|
# TODO: a known asset DNE list?
|
||||||
|
# and pair.baseAsset == 'DEFI'
|
||||||
|
):
|
||||||
|
log.warning(
|
||||||
|
f'UNKNOWN {venue} asset {pair.baseAsset} from,\n'
|
||||||
|
f'{pformat(pair.to_dict())}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# XXX UNKNOWN missing "asset", though no idea why?
|
||||||
|
# maybe it's only avail in the margin venue(s): /dapi/ ?
|
||||||
|
return None
|
||||||
|
|
||||||
mkt = MktPair(
|
mkt = MktPair(
|
||||||
dst=Asset(
|
dst=dst,
|
||||||
name=pair.baseAsset,
|
src=assets[pair.bs_src_asset],
|
||||||
atype='crypto',
|
|
||||||
tx_tick=digits_to_dec(pair.baseAssetPrecision),
|
|
||||||
),
|
|
||||||
src=Asset(
|
|
||||||
name=pair.quoteAsset,
|
|
||||||
atype='crypto',
|
|
||||||
tx_tick=digits_to_dec(pair.quoteAssetPrecision),
|
|
||||||
),
|
|
||||||
price_tick=pair.price_tick,
|
price_tick=pair.price_tick,
|
||||||
size_tick=pair.size_tick,
|
size_tick=pair.size_tick,
|
||||||
bs_mktid=pair.symbol,
|
bs_mktid=pair.symbol,
|
||||||
expiry=expiry,
|
expiry=expiry,
|
||||||
venue=venue,
|
venue=venue,
|
||||||
broker='binance',
|
broker='binance',
|
||||||
_atype=_atype,
|
_atype=sectype,
|
||||||
)
|
)
|
||||||
both = mkt, pair
|
return mkt, pair
|
||||||
return both
|
|
||||||
|
|
||||||
|
|
||||||
@acm
|
@acm
|
||||||
|
@ -472,10 +512,11 @@ async def open_symbol_search(
|
||||||
ctx: tractor.Context,
|
ctx: tractor.Context,
|
||||||
) -> Client:
|
) -> Client:
|
||||||
|
|
||||||
|
# NOTE: symbology tables are loaded as part of client
|
||||||
|
# startup in ``.api.get_client()`` and in this case
|
||||||
|
# are stored as `Client._pairs`.
|
||||||
async with open_cached_client('binance') as client:
|
async with open_cached_client('binance') as client:
|
||||||
|
|
||||||
# load all symbols locally for fast search
|
|
||||||
fqpairs_cache = await client.exch_info()
|
|
||||||
# TODO: maybe we should deliver the cache
|
# TODO: maybe we should deliver the cache
|
||||||
# so that client's can always do a local-lookup-first
|
# so that client's can always do a local-lookup-first
|
||||||
# style try and then update async as (new) match results
|
# style try and then update async as (new) match results
|
||||||
|
@ -488,7 +529,7 @@ async def open_symbol_search(
|
||||||
async for pattern in stream:
|
async for pattern in stream:
|
||||||
matches = fuzzy.extractBests(
|
matches = fuzzy.extractBests(
|
||||||
pattern,
|
pattern,
|
||||||
fqpairs_cache,
|
client._pairs,
|
||||||
score_cutoff=50,
|
score_cutoff=50,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue