Compare commits

..

No commits in common. "b30372f4fe60a5257aae412d4e78e73aff860b14" and "08d159e65296d8c64061601abbe46d07acfea560" have entirely different histories.

10 changed files with 25 additions and 194 deletions

View File

@ -20,14 +20,10 @@ Handy cross-broker utils.
""" """
from __future__ import annotations from __future__ import annotations
# from functools import partial # from functools import partial
from typing import (
Type,
)
import json import json
import httpx import httpx
import logging import logging
from msgspec import Struct
from piker.log import ( from piker.log import (
colorize_json, colorize_json,
@ -101,12 +97,6 @@ class DataThrottle(BrokerError):
''' '''
# TODO: add in throttle metrics/feedback # TODO: add in throttle metrics/feedback
class SchemaMismatch(BrokerError):
'''
Market `Pair` fields mismatch, likely due to provider API update.
'''
def resproc( def resproc(
resp: httpx.Response, resp: httpx.Response,
@ -133,45 +123,3 @@ def resproc(
log.debug(f"Received json contents:\n{colorize_json(msg)}") log.debug(f"Received json contents:\n{colorize_json(msg)}")
return msg if return_json else resp 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

View File

@ -49,9 +49,6 @@ from piker import config
from piker.clearing._messages import ( from piker.clearing._messages import (
Order, Order,
) )
from piker.brokers._util import (
get_or_raise_on_pair_schema_mismatch,
)
from piker.accounting import ( from piker.accounting import (
Asset, Asset,
digits_to_dec, digits_to_dec,
@ -373,12 +370,20 @@ class Client:
item['filters'] = filters item['filters'] = filters
pair_type: Type = PAIRTYPES[venue] pair_type: Type = PAIRTYPES[venue]
pair: Pair = get_or_raise_on_pair_schema_mismatch( try:
pair_type=pair_type, pair: Pair = pair_type(**item)
fields_data=item, except Exception as e:
provider_name='binance', e.add_note(
api_url='https://binance-docs.github.io/apidocs/spot/en/#exchange-information', 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'
) )
raise
pair_table[pair.symbol.upper()] = pair pair_table[pair.symbol.upper()] = pair
# update an additional top-level-cross-venue-table # update an additional top-level-cross-venue-table
@ -604,11 +609,7 @@ class Client:
start_time = binance_timestamp(start_dt) start_time = binance_timestamp(start_dt)
end_time = binance_timestamp(end_dt) end_time = binance_timestamp(end_dt)
import tractor bs_pair: Pair = self._pairs[mkt.bs_fqme.upper()]
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 # https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
bars = await self._api( bars = await self._api(

View File

@ -48,7 +48,6 @@ import tractor
from piker.brokers import ( from piker.brokers import (
open_cached_client, open_cached_client,
NoData, NoData,
SymbolNotFound,
) )
from piker._cacheables import ( from piker._cacheables import (
async_lifo_cache, async_lifo_cache,
@ -306,10 +305,6 @@ async def get_mkt_info(
# uppercase since kraken bs_mktid is always upper # uppercase since kraken bs_mktid is always upper
if 'binance' not in fqme.lower(): if 'binance' not in fqme.lower():
log.warning(
f'Missing `.<provider>` part in fqme ??\n'
f'fqme: {fqme!r}\n'
)
fqme += '.binance' fqme += '.binance'
mkt_mode: str = '' mkt_mode: str = ''
@ -324,12 +319,6 @@ async def get_mkt_info(
venue: str = venue.upper() venue: str = venue.upper()
venue_lower: str = venue.lower() 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 # XXX TODO: we should change the usdtm_futes name to just
# usdm_futes (dropping the tether part) since it turns out that # usdm_futes (dropping the tether part) since it turns out that
# there are indeed USD-tokens OTHER THEN tether being used as # there are indeed USD-tokens OTHER THEN tether being used as
@ -371,8 +360,6 @@ async def get_mkt_info(
if not mkt_mode: if not mkt_mode:
mkt_mode: str = f'{venue_lower}_futes' mkt_mode: str = f'{venue_lower}_futes'
await tractor.pause()
async with open_cached_client( async with open_cached_client(
'binance', 'binance',
) as client: ) as client:

View File

@ -20,7 +20,6 @@ Per market data-type definitions and schemas types.
""" """
from __future__ import annotations from __future__ import annotations
from typing import ( from typing import (
ClassVar,
Literal, Literal,
) )
from decimal import Decimal from decimal import Decimal
@ -204,8 +203,6 @@ class FutesPair(Pair):
# NOTE: see `.data._symcache.SymbologyCache.load()` for why # NOTE: see `.data._symcache.SymbologyCache.load()` for why
ns_path: str = 'piker.brokers.binance:FutesPair' 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` # NOTE: for compat with spot pairs and `MktPair.src: Asset`
# processing.. # processing..
@property @property

View File

@ -95,7 +95,6 @@ from .symbols import (
) )
from ...log import get_logger from ...log import get_logger
from .venues import ( from .venues import (
is_expired,
is_venue_open, is_venue_open,
sesh_times, sesh_times,
is_venue_closure, is_venue_closure,
@ -497,7 +496,7 @@ class Client:
await self.ib.reqContractDetailsAsync(contract) await self.ib.reqContractDetailsAsync(contract)
)[0] )[0]
# convert to makt-native tz # convert to makt-native tz
tz: str = details.timeZoneId or 'EST' tz: str = details.timeZoneId
end_dt = end_dt.in_tz(tz) end_dt = end_dt.in_tz(tz)
first_dt: DateTime = from_timestamp(first).in_tz(tz) first_dt: DateTime = from_timestamp(first).in_tz(tz)
last_dt: DateTime = from_timestamp(last).in_tz(tz) last_dt: DateTime = from_timestamp(last).in_tz(tz)
@ -509,18 +508,10 @@ class Client:
_open_now: bool = is_venue_open( _open_now: bool = is_venue_open(
con_deats=details, con_deats=details,
) )
_is_expired: bool = is_expired(
con_deats=details,
)
# XXX, do gap detections. # XXX, do gap detections.
has_closure_gap: bool = False has_closure_gap: bool = False
if ( if (
# XXX, expired tracts can't be introspected
# for open/closure intervals due to ib's chitty
# details seemingly..
not _is_expired
and
last_dt.add(seconds=sample_period_s) last_dt.add(seconds=sample_period_s)
< <
end_dt end_dt

View File

@ -34,7 +34,6 @@ from typing import (
import exchange_calendars as xcals import exchange_calendars as xcals
from pendulum import ( from pendulum import (
parse,
now, now,
Duration, Duration,
Interval, Interval,
@ -57,17 +56,6 @@ if TYPE_CHECKING:
) )
def is_expired(
con_deats: ContractDetails,
) -> bool:
'''
Predicate
'''
expiry_dt: datetime = parse(con_deats.realExpirationDate)
return expiry_dt.date() >= now().date()
def has_weekend( def has_weekend(
period: Interval, period: Interval,
) -> bool: ) -> bool:
@ -182,22 +170,7 @@ def sesh_times(
get the (day-agnostic) times for the start/end. get the (day-agnostic) times for the start/end.
''' '''
# ?TODO, lookup the next front contract instead? earliest_sesh: Interval = next(iter_sessions(con_deats))
if is_expired(con_deats):
raise ValueError(
f'Contract is already expired!\n'
f'Choose an active alt contract instead.\n'
f'con_deats: {con_deats!r}\n'
)
maybe_sessions: list[Interval] = list(iter_sessions(con_deats))
if not maybe_sessions:
raise ValueError(
f'Contract has no trading-session info?\n'
f'con_deats: {con_deats!r}\n'
)
earliest_sesh: Interval = maybe_sessions[0]
return ( return (
earliest_sesh.start.time(), earliest_sesh.start.time(),
earliest_sesh.end.time(), earliest_sesh.end.time(),
@ -238,13 +211,7 @@ def is_venue_closure(
''' '''
open: Time open: Time
close: Time close: Time
maybe_oc: tuple|None = sesh_times(con_deats) open, close = sesh_times(con_deats)
if maybe_oc is None:
# XXX, should never get here.
breakpoint()
return False
open, close = maybe_oc
# ensure times are in mkt-native timezone # ensure times are in mkt-native timezone
tz: str = con_deats.timeZoneId tz: str = con_deats.timeZoneId

View File

@ -52,7 +52,6 @@ from piker.brokers._util import (
SymbolNotFound, SymbolNotFound,
BrokerError, BrokerError,
DataThrottle, DataThrottle,
get_or_raise_on_pair_schema_mismatch,
) )
from piker.accounting import Transaction from piker.accounting import Transaction
from piker.log import get_logger from piker.log import get_logger
@ -503,16 +502,7 @@ class Client:
# NOTE: always cache in pairs tables for faster lookup # NOTE: always cache in pairs tables for faster lookup
with tractor.devx.maybe_open_crash_handler(): # as bxerr: 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 # register the above `Pair` structs for all
# key-sets/monikers: a set of 4 (frickin) tables # key-sets/monikers: a set of 4 (frickin) tables

View File

@ -19,9 +19,6 @@ Symbology defs and search.
''' '''
from decimal import Decimal from decimal import Decimal
from typing import (
ClassVar,
)
import tractor import tractor
@ -89,14 +86,9 @@ class Pair(Struct):
short_position_limit: float = 0 short_position_limit: float = 0
long_position_limit: float = float('inf') 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? # TODO: should we make this a literal NamespacePath ref?
ns_path: str = 'piker.brokers.kraken:Pair' ns_path: str = 'piker.brokers.kraken:Pair'
_api_url: ClassVar[str] = 'https://docs.kraken.com/api/docs/rest-api/get-tradable-asset-pairs'
@property @property
def bs_mktid(self) -> str: def bs_mktid(self) -> str:
''' '''

View File

@ -206,8 +206,8 @@ pyvnc = { git = "https://github.com/regulad/pyvnc.git" }
# xonsh = { git = 'https://github.com/xonsh/xonsh.git', branch = 'main' } # xonsh = { git = 'https://github.com/xonsh/xonsh.git', branch = 'main' }
# XXX since, we're like, always hacking new shite all-the-time. Bp # XXX since, we're like, always hacking new shite all-the-time. Bp
# tractor = { git = "https://github.com/goodboy/tractor.git", branch ="main" } tractor = { git = "https://github.com/goodboy/tractor.git", branch ="main" }
# tractor = { git = "https://pikers.dev/goodboy/tractor", branch = "piker_pin" } # tractor = { git = "https://pikers.dev/goodboy/tractor", branch = "piker_pin" }
# ------ goodboy ------ # ------ goodboy ------
# hackin dev-envs, usually there's something new he's hackin in.. # hackin dev-envs, usually there's something new he's hackin in..
tractor = { path = "../tractor", editable = true } # tractor = { path = "../tractor", editable = true }

46
uv.lock
View File

@ -1034,7 +1034,7 @@ requires-dist = [
{ name = "tomli", specifier = ">=2.0.1,<3.0.0" }, { name = "tomli", specifier = ">=2.0.1,<3.0.0" },
{ name = "tomli-w", specifier = ">=1.0.0,<2.0.0" }, { name = "tomli-w", specifier = ">=1.0.0,<2.0.0" },
{ name = "tomlkit", git = "https://github.com/pikers/tomlkit.git?branch=piker_pin" }, { name = "tomlkit", git = "https://github.com/pikers/tomlkit.git?branch=piker_pin" },
{ name = "tractor", editable = "../tractor" }, { name = "tractor", git = "https://github.com/goodboy/tractor.git?branch=main" },
{ name = "trio", specifier = ">=0.27" }, { name = "trio", specifier = ">=0.27" },
{ name = "trio-typing", specifier = ">=0.10.0" }, { name = "trio-typing", specifier = ">=0.10.0" },
{ name = "trio-util", specifier = ">=0.7.0,<0.8.0" }, { name = "trio-util", specifier = ">=0.7.0,<0.8.0" },
@ -1680,7 +1680,7 @@ wheels = [
[[package]] [[package]]
name = "tractor" name = "tractor"
version = "0.1.0a6.dev0" version = "0.1.0a6.dev0"
source = { editable = "../tractor" } source = { git = "https://github.com/goodboy/tractor.git?branch=main#e77198bb64f0467a50e251ed140daee439752354" }
dependencies = [ dependencies = [
{ name = "bidict" }, { name = "bidict" },
{ name = "cffi" }, { name = "cffi" },
@ -1693,48 +1693,6 @@ dependencies = [
{ name = "wrapt" }, { name = "wrapt" },
] ]
[package.metadata]
requires-dist = [
{ name = "bidict", specifier = ">=0.23.1" },
{ name = "cffi", specifier = ">=1.17.1" },
{ name = "colorlog", specifier = ">=6.8.2,<7" },
{ name = "msgspec", specifier = ">=0.19.0" },
{ name = "pdbp", specifier = ">=1.8.2,<2" },
{ name = "platformdirs", specifier = ">=4.4.0" },
{ name = "tricycle", specifier = ">=0.4.1,<0.5" },
{ name = "trio", specifier = ">0.27" },
{ name = "wrapt", specifier = ">=1.16.0,<2" },
]
[package.metadata.requires-dev]
dev = [
{ name = "greenback", specifier = ">=1.2.1,<2" },
{ name = "pexpect", specifier = ">=4.9.0,<5" },
{ name = "prompt-toolkit", specifier = ">=3.0.50" },
{ name = "psutil", specifier = ">=7.0.0" },
{ name = "pyperclip", specifier = ">=1.9.0" },
{ name = "pytest", specifier = ">=8.3.5" },
{ name = "stackscope", specifier = ">=0.2.2,<0.3" },
{ name = "typing-extensions", specifier = ">=4.14.1" },
{ name = "xonsh", specifier = ">=0.22.2" },
]
devx = [
{ name = "greenback", specifier = ">=1.2.1,<2" },
{ name = "stackscope", specifier = ">=0.2.2,<0.3" },
{ name = "typing-extensions", specifier = ">=4.14.1" },
]
lint = [{ name = "ruff", specifier = ">=0.9.6" }]
repl = [
{ name = "prompt-toolkit", specifier = ">=3.0.50" },
{ name = "psutil", specifier = ">=7.0.0" },
{ name = "pyperclip", specifier = ">=1.9.0" },
{ name = "xonsh", specifier = ">=0.22.2" },
]
testing = [
{ name = "pexpect", specifier = ">=4.9.0,<5" },
{ name = "pytest", specifier = ">=8.3.5" },
]
[[package]] [[package]]
name = "tricycle" name = "tricycle"
version = "0.4.1" version = "0.4.1"