Factor `Pair` schema-mismatch handling to `_util`
Add `get_or_raise_on_pair_schema_mismatch()` helper and `SchemaMismatch` error type in `brokers._util` to standardize the "provider changed their API" error reporting across backends. Deats, - add `SchemaMismatch(BrokerError)` exc type. - `get_or_raise_on_pair_schema_mismatch()`: catch `TypeError` on `Pair` ctor, build `ppfmt()`-ed report with provider name, fall back to `pair_type._api_url` if no explicit URL passed, then raise `SchemaMismatch`. - binance `api.py`: replace inline `try/except` + `e.add_note()` with the new helper. - kraken `api.py`: replace bare `Pair(...)` ctor with the new helper inside crash handler. Also, - add `_api_url: ClassVar[str]` to binance `FutesPair` and kraken `Pair` structs. - binance `feed.py`: warn on missing `.<provider>` in fqme; raise `SymbolNotFound` on empty venue. - reformat `start_dt`/`end_dt` unions to `datetime|None` style in binance `Client`. - wrap binance `_pairs` lookup in `maybe_open_crash_handler()`. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-coderepair_tests
parent
60b4526fad
commit
b30372f4fe
|
|
@ -20,10 +20,14 @@ Handy cross-broker utils.
|
|||
"""
|
||||
from __future__ import annotations
|
||||
# from functools import partial
|
||||
from typing import (
|
||||
Type,
|
||||
)
|
||||
|
||||
import json
|
||||
import httpx
|
||||
import logging
|
||||
from msgspec import Struct
|
||||
|
||||
from piker.log import (
|
||||
colorize_json,
|
||||
|
|
@ -97,6 +101,12 @@ class DataThrottle(BrokerError):
|
|||
'''
|
||||
# TODO: add in throttle metrics/feedback
|
||||
|
||||
class SchemaMismatch(BrokerError):
|
||||
'''
|
||||
Market `Pair` fields mismatch, likely due to provider API update.
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def resproc(
|
||||
resp: httpx.Response,
|
||||
|
|
@ -123,3 +133,45 @@ def resproc(
|
|||
log.debug(f"Received json contents:\n{colorize_json(msg)}")
|
||||
|
||||
return msg if return_json else resp
|
||||
|
||||
|
||||
def get_or_raise_on_pair_schema_mismatch(
|
||||
pair_type: Type[Struct],
|
||||
fields_data: dict,
|
||||
provider_name: str,
|
||||
api_url: str|None = None,
|
||||
) -> Struct:
|
||||
'''
|
||||
Boilerplate helper around assset-`Pair` field schema mismatches,
|
||||
normally due to provider API updates.
|
||||
|
||||
'''
|
||||
try:
|
||||
pair: Struct = pair_type(**fields_data)
|
||||
return pair
|
||||
except TypeError as err:
|
||||
|
||||
from tractor.devx.pformat import ppfmt
|
||||
repr_data: str = ppfmt(fields_data)
|
||||
report: str = (
|
||||
f'Field mismatch we need to codify!\n'
|
||||
f'\n'
|
||||
f'{pair_type!r}({repr_data})'
|
||||
f'\n'
|
||||
f'^^^ {err.args[0]!r} ^^^\n'
|
||||
f'\n'
|
||||
f"Don't panic, prolly {provider_name!r} "
|
||||
f"changed their symbology schema..\n"
|
||||
)
|
||||
if (
|
||||
api_url
|
||||
or
|
||||
(api_url := pair_type._api_url)
|
||||
):
|
||||
report += (
|
||||
f'\n'
|
||||
f'Check out their API docs here:\n'
|
||||
f'{api_url}\n'
|
||||
)
|
||||
|
||||
raise SchemaMismatch(report) from err
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ from piker import config
|
|||
from piker.clearing._messages import (
|
||||
Order,
|
||||
)
|
||||
from piker.brokers._util import (
|
||||
get_or_raise_on_pair_schema_mismatch,
|
||||
)
|
||||
from piker.accounting import (
|
||||
Asset,
|
||||
digits_to_dec,
|
||||
|
|
@ -370,20 +373,12 @@ class Client:
|
|||
item['filters'] = filters
|
||||
|
||||
pair_type: Type = PAIRTYPES[venue]
|
||||
try:
|
||||
pair: Pair = pair_type(**item)
|
||||
except Exception as e:
|
||||
e.add_note(
|
||||
f'\n'
|
||||
f'New or removed field we need to codify!\n'
|
||||
f'pair-type: {pair_type!r}\n'
|
||||
f'\n'
|
||||
f"Don't panic, prolly stupid binance changed their symbology schema again..\n"
|
||||
f'Check out their API docs here:\n'
|
||||
f'\n'
|
||||
f'https://binance-docs.github.io/apidocs/spot/en/#exchange-information\n'
|
||||
pair: Pair = get_or_raise_on_pair_schema_mismatch(
|
||||
pair_type=pair_type,
|
||||
fields_data=item,
|
||||
provider_name='binance',
|
||||
api_url='https://binance-docs.github.io/apidocs/spot/en/#exchange-information',
|
||||
)
|
||||
raise
|
||||
pair_table[pair.symbol.upper()] = pair
|
||||
|
||||
# update an additional top-level-cross-venue-table
|
||||
|
|
@ -609,7 +604,11 @@ class Client:
|
|||
start_time = binance_timestamp(start_dt)
|
||||
end_time = binance_timestamp(end_dt)
|
||||
|
||||
bs_pair: Pair = self._pairs[mkt.bs_fqme.upper()]
|
||||
import tractor
|
||||
with tractor.devx.maybe_open_crash_handler():
|
||||
bs_pair: Pair = self._pairs[
|
||||
mkt.bs_fqme.upper()
|
||||
]
|
||||
|
||||
# https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
|
||||
bars = await self._api(
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import tractor
|
|||
from piker.brokers import (
|
||||
open_cached_client,
|
||||
NoData,
|
||||
SymbolNotFound,
|
||||
)
|
||||
from piker._cacheables import (
|
||||
async_lifo_cache,
|
||||
|
|
@ -305,6 +306,10 @@ async def get_mkt_info(
|
|||
|
||||
# uppercase since kraken bs_mktid is always upper
|
||||
if 'binance' not in fqme.lower():
|
||||
log.warning(
|
||||
f'Missing `.<provider>` part in fqme ??\n'
|
||||
f'fqme: {fqme!r}\n'
|
||||
)
|
||||
fqme += '.binance'
|
||||
|
||||
mkt_mode: str = ''
|
||||
|
|
@ -319,6 +324,12 @@ async def get_mkt_info(
|
|||
venue: str = venue.upper()
|
||||
venue_lower: str = venue.lower()
|
||||
|
||||
if not venue:
|
||||
raise SymbolNotFound(
|
||||
f'Invalid or missing .<venue> part in fqme?\n'
|
||||
f'fqme: {fqme!r}\n'
|
||||
)
|
||||
|
||||
# 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
|
||||
|
|
@ -360,6 +371,8 @@ async def get_mkt_info(
|
|||
if not mkt_mode:
|
||||
mkt_mode: str = f'{venue_lower}_futes'
|
||||
|
||||
await tractor.pause()
|
||||
|
||||
async with open_cached_client(
|
||||
'binance',
|
||||
) as client:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Per market data-type definitions and schemas types.
|
|||
"""
|
||||
from __future__ import annotations
|
||||
from typing import (
|
||||
ClassVar,
|
||||
Literal,
|
||||
)
|
||||
from decimal import Decimal
|
||||
|
|
@ -203,6 +204,8 @@ class FutesPair(Pair):
|
|||
# NOTE: see `.data._symcache.SymbologyCache.load()` for why
|
||||
ns_path: str = 'piker.brokers.binance:FutesPair'
|
||||
|
||||
_api_url: ClassVar[str] = 'https://binance-docs.github.io/apidocs/spot/en/#exchange-information'
|
||||
|
||||
# NOTE: for compat with spot pairs and `MktPair.src: Asset`
|
||||
# processing..
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ from piker.brokers._util import (
|
|||
SymbolNotFound,
|
||||
BrokerError,
|
||||
DataThrottle,
|
||||
get_or_raise_on_pair_schema_mismatch,
|
||||
)
|
||||
from piker.accounting import Transaction
|
||||
from piker.log import get_logger
|
||||
|
|
@ -502,7 +503,16 @@ class Client:
|
|||
|
||||
# NOTE: always cache in pairs tables for faster lookup
|
||||
with tractor.devx.maybe_open_crash_handler(): # as bxerr:
|
||||
pair = Pair(xname=xkey, **data)
|
||||
# pair = Pair(xname=xkey, **data)
|
||||
pair: Pair = get_or_raise_on_pair_schema_mismatch(
|
||||
pair_type=Pair,
|
||||
fields_data=dict(
|
||||
xname=xkey,
|
||||
**data,
|
||||
),
|
||||
provider_name='kraken',
|
||||
# api_url='https://binance-docs.github.io/apidocs/spot/en/#exchange-information',
|
||||
)
|
||||
|
||||
# register the above `Pair` structs for all
|
||||
# key-sets/monikers: a set of 4 (frickin) tables
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ Symbology defs and search.
|
|||
|
||||
'''
|
||||
from decimal import Decimal
|
||||
from typing import (
|
||||
ClassVar,
|
||||
)
|
||||
|
||||
import tractor
|
||||
|
||||
|
|
@ -86,9 +89,14 @@ class Pair(Struct):
|
|||
short_position_limit: float = 0
|
||||
long_position_limit: float = float('inf')
|
||||
|
||||
# TODO, add API note when this was added!
|
||||
# execution_venue: str|None = None
|
||||
|
||||
# TODO: should we make this a literal NamespacePath ref?
|
||||
ns_path: str = 'piker.brokers.kraken:Pair'
|
||||
|
||||
_api_url: ClassVar[str] = 'https://docs.kraken.com/api/docs/rest-api/get-tradable-asset-pairs'
|
||||
|
||||
@property
|
||||
def bs_mktid(self) -> str:
|
||||
'''
|
||||
|
|
|
|||
Loading…
Reference in New Issue