From ec71dc20182ac25c7047817fb5437d9db8179347 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 13:06:53 -0400 Subject: [PATCH 01/13] Mk `Brokerd[Order].price` avoid `float`-errs By re-typing to a `.price: Decimal` field on both legs of the EMS. It seems we must do it ourselves since, - these msg's (fields) are relayed through the clearing engine to each `brokerd` backend and, - bc many (if not all) of those backends `.broker`-clients (nor their encapsulated "brokerage services") **are not** doing any precision-truncation themselves. So, for now, instead we opt to expect rounding at the source. This means we will explicitly require casting to/from `float` at the line-graphics interface to the order-clearing-engine (as implemented throughout `.ui.order_mode.OrderMode`); and this is coming shortly. --- piker/clearing/_ems.py | 18 +++++++++++++----- piker/clearing/_messages.py | 13 +++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/piker/clearing/_ems.py b/piker/clearing/_ems.py index 3f7045fa..2ea06d8a 100644 --- a/piker/clearing/_ems.py +++ b/piker/clearing/_ems.py @@ -1182,12 +1182,16 @@ async def process_client_order_cmds( submitting live orders immediately if requested by the client. ''' - # cmd: dict + # TODO, only allow `msgspec.Struct` form! + cmd: dict async for cmd in client_order_stream: - log.info(f'Received order cmd:\n{pformat(cmd)}') + log.info( + f'Received order cmd:\n' + f'{pformat(cmd)}\n' + ) # CAWT DAMN we need struct support! - oid = str(cmd['oid']) + oid: str = str(cmd['oid']) # register this stream as an active order dialog (msg flow) for # this order id such that translated message from the brokerd @@ -1347,7 +1351,11 @@ async def process_client_order_cmds( # (``translate_and_relay_brokerd_events()`` above) will # handle relaying the ems side responses back to # the client/cmd sender from this request - log.info(f'Sending live order to {broker}:\n{pformat(msg)}') + log.info( + f'Sending live order to {broker}:\n' + f'{pformat(msg)}' + ) + await brokerd_order_stream.send(msg) # an immediate response should be ``BrokerdOrderAck`` @@ -1531,7 +1539,7 @@ async def _emsd_main( ctx: tractor.Context, fqme: str, exec_mode: str, # ('paper', 'live') - loglevel: str | None = None, + loglevel: str|None = None, ) -> tuple[ dict[ diff --git a/piker/clearing/_messages.py b/piker/clearing/_messages.py index 51a3860c..788fe669 100644 --- a/piker/clearing/_messages.py +++ b/piker/clearing/_messages.py @@ -19,6 +19,7 @@ Clearing sub-system message and protocols. """ from __future__ import annotations +from decimal import Decimal from typing import ( Literal, ) @@ -71,7 +72,15 @@ class Order(Struct): symbol: str # | MktPair account: str # should we set a default as '' ? - price: float + # https://docs.python.org/3/library/decimal.html#decimal-objects + # + # ?TODO? decimal usage throughout? + # -[ ] possibly leverage the `Encoder(decimal_format='number')` + # bit? + # |_https://jcristharif.com/msgspec/supported-types.html#decimal + # -[ ] should we also use it for .size? + # + price: Decimal size: float # -ve is "sell", +ve is "buy" brokers: list[str] = [] @@ -178,7 +187,7 @@ class BrokerdOrder(Struct): time_ns: int symbol: str # fqme - price: float + price: Decimal size: float # TODO: if we instead rely on a +ve/-ve size to determine -- 2.34.1 From cbbf6747371efc766077eb14c237740aa6d53dd9 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 13:34:12 -0400 Subject: [PATCH 02/13] Finally drop `Symbol` It was replaced by `MktPair` long ago in, https://github.com/pikers/piker/pull/489 with follow up for final removal in, https://github.com/pikers/piker/issues/517 Resolves #517 --- piker/accounting/__init__.py | 2 - piker/accounting/_mktinfo.py | 91 +----------------------------------- 2 files changed, 2 insertions(+), 91 deletions(-) diff --git a/piker/accounting/__init__.py b/piker/accounting/__init__.py index e27dc4bf..1b776714 100644 --- a/piker/accounting/__init__.py +++ b/piker/accounting/__init__.py @@ -42,7 +42,6 @@ from ._mktinfo import ( dec_digits, digits_to_dec, MktPair, - Symbol, unpack_fqme, _derivs as DerivTypes, ) @@ -60,7 +59,6 @@ __all__ = [ 'Asset', 'MktPair', 'Position', - 'Symbol', 'Transaction', 'TransactionLedger', 'dec_digits', diff --git a/piker/accounting/_mktinfo.py b/piker/accounting/_mktinfo.py index a8c719da..b03635f1 100644 --- a/piker/accounting/_mktinfo.py +++ b/piker/accounting/_mktinfo.py @@ -390,8 +390,8 @@ class MktPair(Struct, frozen=True): cls, fqme: str, - price_tick: float | str, - size_tick: float | str, + price_tick: float|str, + size_tick: float|str, bs_mktid: str, broker: str | None = None, @@ -677,90 +677,3 @@ def unpack_fqme( # '.'.join([mkt_ep, venue]), suffix, ) - - -class Symbol(Struct): - ''' - I guess this is some kinda container thing for dealing with - all the different meta-data formats from brokers? - - ''' - key: str - - broker: str = '' - venue: str = '' - - # precision descriptors for price and vlm - tick_size: Decimal = Decimal('0.01') - lot_tick_size: Decimal = Decimal('0.0') - - suffix: str = '' - broker_info: dict[str, dict[str, Any]] = {} - - @classmethod - def from_fqme( - cls, - fqsn: str, - info: dict[str, Any], - - ) -> Symbol: - broker, mktep, venue, suffix = unpack_fqme(fqsn) - tick_size = info.get('price_tick_size', 0.01) - lot_size = info.get('lot_tick_size', 0.0) - - return Symbol( - broker=broker, - key=mktep, - tick_size=tick_size, - lot_tick_size=lot_size, - venue=venue, - suffix=suffix, - broker_info={broker: info}, - ) - - @property - def type_key(self) -> str: - return list(self.broker_info.values())[0]['asset_type'] - - @property - def tick_size_digits(self) -> int: - return float_digits(self.tick_size) - - @property - def lot_size_digits(self) -> int: - return float_digits(self.lot_tick_size) - - @property - def price_tick(self) -> Decimal: - return Decimal(str(self.tick_size)) - - @property - def size_tick(self) -> Decimal: - return Decimal(str(self.lot_tick_size)) - - @property - def broker(self) -> str: - return list(self.broker_info.keys())[0] - - @property - def fqme(self) -> str: - return maybe_cons_tokens([ - self.key, # final "pair name" (eg. qqq[/usd], btcusdt) - self.venue, - self.suffix, # includes expiry and other con info - self.broker, - ]) - - def quantize( - self, - size: float, - ) -> Decimal: - digits = float_digits(self.lot_tick_size) - return Decimal(size).quantize( - Decimal(f'1.{"0".ljust(digits, "0")}'), - rounding=ROUND_HALF_EVEN - ) - - # NOTE: when cast to `str` return fqme - def __str__(self) -> str: - return self.fqme -- 2.34.1 From 84ad34f51e0e84a95eb550d2df7bd40705038742 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 20:36:28 -0400 Subject: [PATCH 03/13] Cast to `float` as needed from order-mode and ems Since we're not quite yet using automatic typed msging from `tractor`/`msgspec` (i.e. still manually decoding order ctl msgs from built-in types..`dict`s still not `msgspec.Struct`) this adds the appropriate typecasting ops to ensure the required precision is attained prior to processing and/or submission to a brokerd backend service. For the `.clearing._ems`, - flip all `trigger_price` previously presumed to be `float` to just the field-identical `price: Decimal` and ensure we cast to `float` for any `trigger_price` usage, like before passing to `mk_check()`. For `.ui.order_mode.OrderMode`, - add a new `.curr_mkt: MktPair` convenience property to get the chart-active value. - ensure we always use the `.curr_mkt.quantize() -> Decimal` before setting any IPC-msg's `.price` field! - always cast `float(Order.price)` before use in setting line-levels. - don't bother setting `Order.symbol` to a (now fully removed) `Symbol` instance since it's not really required-for-use anywhere; leaving it a `str` (per the type-annot) is fine for now? --- piker/clearing/_ems.py | 14 +++--- piker/ui/order_mode.py | 96 +++++++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/piker/clearing/_ems.py b/piker/clearing/_ems.py index 2ea06d8a..e39c9cdc 100644 --- a/piker/clearing/_ems.py +++ b/piker/clearing/_ems.py @@ -76,7 +76,6 @@ if TYPE_CHECKING: # TODO: numba all of this def mk_check( - trigger_price: float, known_last: float, action: str, @@ -1297,7 +1296,7 @@ async def process_client_order_cmds( case { 'oid': oid, 'symbol': fqme, - 'price': trigger_price, + 'price': price, 'size': size, 'action': ('buy' | 'sell') as action, 'exec_mode': ('live' | 'paper'), @@ -1329,7 +1328,7 @@ async def process_client_order_cmds( symbol=sym, action=action, - price=trigger_price, + price=price, size=size, account=req.account, ) @@ -1371,7 +1370,7 @@ async def process_client_order_cmds( case { 'oid': oid, 'symbol': fqme, - 'price': trigger_price, + 'price': price, 'size': size, 'exec_mode': exec_mode, 'action': action, @@ -1399,7 +1398,12 @@ async def process_client_order_cmds( if isnan(last): last = flume.rt_shm.array[-1]['close'] - pred = mk_check(trigger_price, last, action) + trigger_price: float = float(price) + pred = mk_check( + trigger_price, + last, + action, + ) # NOTE: for dark orders currently we submit # the triggered live order at a price 5 ticks diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index d5720e8a..45f30bb5 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -21,6 +21,7 @@ Chart trading, the only way to scalp. from __future__ import annotations from contextlib import asynccontextmanager from dataclasses import dataclass, field +from decimal import Decimal from functools import partial from pprint import pformat import time @@ -41,7 +42,6 @@ from piker.accounting import ( Position, mk_allocator, MktPair, - Symbol, ) from piker.clearing import ( open_ems, @@ -143,6 +143,15 @@ class OrderMode: } _staged_order: Order | None = None + @property + def curr_mkt(self) -> MktPair: + ''' + Deliver the currently selected `MktPair` according + chart state. + + ''' + return self.chart.linked.mkt + def on_level_change_update_next_order_info( self, level: float, @@ -172,7 +181,12 @@ class OrderMode: line.update_labels(order_info) # update bound-in staged order - order.price = level + # order.price = level + mkt: MktPair = self.curr_mkt + order.price: Decimal = mkt.quantize( + size=level, + quantity_type='price', + ) order.size = order_info['size'] # when an order is changed we flip the settings side-pane to @@ -187,7 +201,9 @@ class OrderMode: ) -> LevelLine: - level = order.price + # TODO, if we instead just always decimalize at the ems layer + # we can avoid this back-n-forth casting? + level = float(order.price) line = order_line( chart or self.chart, @@ -224,7 +240,12 @@ class OrderMode: # the order mode allocator but we still need to update the # "staged" order message we'll send to the ems def update_order_price(y: float) -> None: - order.price = y + mkt: MktPair = self.curr_mkt + order.price: Decimal = mkt.quantize( + size=y, + quantity_type='price', + ) + # order.price = y line._on_level_change = update_order_price @@ -275,34 +296,31 @@ class OrderMode: chart = cursor.linked.chart if ( not chart - and cursor - and cursor.active_plot + and + cursor + and + cursor.active_plot ): return chart = cursor.active_plot - price = cursor._datum_xy[1] + price: float = cursor._datum_xy[1] if not price: # zero prices are not supported by any means # since that's illogical / a no-op. return - mkt: MktPair = self.chart.linked.mkt - - # NOTE : we could also use instead, - # mkt.quantize(price, quantity_type='price') - # but it returns a Decimal and it's probably gonna - # be slower? # TODO: should we be enforcing this precision - # at a different layer in the stack? right now - # any precision error will literally be relayed - # all the way back from the backend. - - price = round( - price, - ndigits=mkt.price_tick_digits, + # at a different layer in the stack? + # |_ might require `MktPair` tracking in the EMS? + # |_ right now any precision error will be relayed + # all the way back from the backend and vice-versa.. + # + mkt: MktPair = self.curr_mkt + price: Decimal = mkt.quantize( + size=price, + quantity_type='price', ) - order = self._staged_order = Order( action=action, price=price, @@ -515,14 +533,15 @@ class OrderMode: # if an order msg is provided update the line # **from** that msg. if order: - if order.price <= 0: + price: float = float(order.price) + if price <= 0: log.error(f'Order has 0 price, cancelling..\n{order}') self.cancel_orders([order.oid]) return None - line.set_level(order.price) + line.set_level(price) self.on_level_change_update_next_order_info( - level=order.price, + level=price, line=line, order=order, # use the corresponding position tracker for the @@ -681,9 +700,9 @@ class OrderMode: ) -> Dialog | None: # NOTE: the `.order` attr **must** be set with the # equivalent order msg in order to be loaded. - order = msg.req + order: Order = msg.req oid = str(msg.oid) - symbol = order.symbol + symbol: str = order.symbol # TODO: MEGA UGGG ZONEEEE! src = msg.src @@ -702,13 +721,22 @@ class OrderMode: order.oid = str(order.oid) order.brokers = [brokername] - # TODO: change this over to `MktPair`, but it's - # gonna be tough since we don't have any such data - # really in our clearing msg schema.. - order.symbol = Symbol.from_fqme( - fqsn=fqme, - info={}, - ) + # ?TODO? change this over to `MktPair`, but it's gonna be + # tough since we don't have any such data really in our + # clearing msg schema.. + # BUT WAIT! WHY do we even want/need this!? + # + # order.symbol = self.curr_mkt + # + # XXX, the old approach.. which i don't quire member why.. + # -[ ] verify we for sure don't require this any more! + # |_https://github.com/pikers/piker/issues/517 + # + # order.symbol = Symbol.from_fqme( + # fqsn=fqme, + # info={}, + # ) + maybe_dialog: Dialog | None = self.submit_order( send_msg=False, order=order, @@ -1101,7 +1129,7 @@ async def process_trade_msg( ) ) ): - msg.req = order + msg.req: Order = order dialog: ( Dialog # NOTE: on an invalid order submission (eg. -- 2.34.1 From 2a24d1d50cf0fbd82228c803be49ea3b88803fb7 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 21:16:32 -0400 Subject: [PATCH 04/13] `.kraken`: add masked pauses for order req debug Such that the next time i inevitably must debug the some order-request error status or precision discrepancy, i have the mkt-symbol branch ready to go. Also, switch to `'action': 'buy'|'sell' as action,` style `case` matching instead of the post-`if` predicate style. --- piker/brokers/kraken/broker.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/piker/brokers/kraken/broker.py b/piker/brokers/kraken/broker.py index eb5963cd..bc84a230 100644 --- a/piker/brokers/kraken/broker.py +++ b/piker/brokers/kraken/broker.py @@ -175,9 +175,8 @@ async def handle_order_requests( case { 'account': 'kraken.spot' as account, - 'action': action, - } if action in {'buy', 'sell'}: - + 'action': 'buy'|'sell', + }: # validate order = BrokerdOrder(**msg) @@ -262,6 +261,12 @@ async def handle_order_requests( } | extra log.info(f'Submitting WS order request:\n{pformat(req)}') + + # NOTE HOWTO, debug order requests + # + # if 'XRP' in pair: + # await tractor.pause() + await ws.send_msg(req) # placehold for sanity checking in relay loop @@ -1085,6 +1090,8 @@ async def handle_order_updates( f'Failed to {action} order {reqid}:\n' f'{errmsg}' ) + # if tractor._state.debug_mode(): + # await tractor.pause() symbol: str = 'N/A' if chain := apiflows.get(reqid): -- 2.34.1 From 705f0e86ac64ef4f14c770778fea288eecd47d28 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 21:22:32 -0400 Subject: [PATCH 05/13] Drop variable regex from `ruff.toml` Same as in other projects, seems to be not parsing and causing `ruff` to crash?!? --- ruff.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index 4b40c900..01873dab 100644 --- a/ruff.toml +++ b/ruff.toml @@ -62,8 +62,9 @@ ignore-init-module-imports = false fixable = ["ALL"] unfixable = [] +# TODO? uhh why no work!? # Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +# dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [format] # Use single quotes in `ruff format`. -- 2.34.1 From 3ff0a86741836b61c32f0fd0be1b2b227040ef00 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 21 Apr 2025 21:31:13 -0400 Subject: [PATCH 06/13] Gracefully close on EoCs thrown in quote throttler Since `tractor.MsgStream.send()` now also raises `trio.EndOfChannel` when the stream was gracefully `Stop`ped by the peer side, also handle that case in `.data._sampling.uniform_rate_send()`. --- piker/data/_sampling.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/piker/data/_sampling.py b/piker/data/_sampling.py index 7bb0231d..37d7623f 100644 --- a/piker/data/_sampling.py +++ b/piker/data/_sampling.py @@ -876,6 +876,7 @@ async def uniform_rate_send( except tractor.RemoteActorError as rme: if rme.type is not tractor._exceptions.StreamOverrun: raise + ctx = stream._ctx chan = ctx.chan log.warning( @@ -892,6 +893,7 @@ async def uniform_rate_send( trio.ClosedResourceError, trio.BrokenResourceError, ConnectionResetError, + trio.EndOfChannel, ): # if the feed consumer goes down then drop # out of this rate limiter -- 2.34.1 From 36cc0cf75034e1d8f245b321f44c208e171cf95c Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Tue, 22 Apr 2025 00:20:48 -0400 Subject: [PATCH 07/13] TOSQUASH: 84ad34f51, lingering `float` casts.. --- piker/ui/order_mode.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index 45f30bb5..47a3bb97 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -181,7 +181,6 @@ class OrderMode: line.update_labels(order_info) # update bound-in staged order - # order.price = level mkt: MktPair = self.curr_mkt order.price: Decimal = mkt.quantize( size=level, @@ -245,7 +244,6 @@ class OrderMode: size=y, quantity_type='price', ) - # order.price = y line._on_level_change = update_order_price @@ -396,7 +394,7 @@ class OrderMode: 'oid': oid, }) - if order.price <= 0: + if float(order.price) <= 0: log.error( '*!? Invalid `Order.price <= 0` ?!*\n' # TODO: make this present multi-line in object form @@ -1194,7 +1192,7 @@ async def process_trade_msg( tm = time.time() mode.on_fill( oid, - price=req.price, + price=float(req.price), time_s=tm, ) mode.lines.remove_line(uuid=oid) @@ -1249,7 +1247,7 @@ async def process_trade_msg( tm = details['broker_time'] mode.on_fill( oid, - price=details['price'], + price=float(details['price']), time_s=tm, pointing='up' if action == 'buy' else 'down', ) -- 2.34.1 From 8b0fac3b6cb706946f7389587022c48d90bc7342 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Tue, 22 Apr 2025 22:29:12 -0400 Subject: [PATCH 08/13] TOSQUASH: 84ad34f51, one more `float` cast for paperboi.. --- piker/clearing/_paper_engine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piker/clearing/_paper_engine.py b/piker/clearing/_paper_engine.py index 0393b2e6..9632c6cc 100644 --- a/piker/clearing/_paper_engine.py +++ b/piker/clearing/_paper_engine.py @@ -508,7 +508,7 @@ async def handle_order_requests( reqid = await client.submit_limit( oid=order.oid, symbol=f'{order.symbol}.{client.broker}', - price=order.price, + price=float(order.price), action=order.action, size=order.size, # XXX: by default 0 tells ``ib_insync`` methods that -- 2.34.1 From 8a768af5bb24f4a998962401af6f0f75995641d8 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 24 Apr 2025 10:37:33 -0400 Subject: [PATCH 09/13] Update legacy type to `tractor.MsgStream` --- piker/clearing/_ems.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/piker/clearing/_ems.py b/piker/clearing/_ems.py index e39c9cdc..aad849aa 100644 --- a/piker/clearing/_ems.py +++ b/piker/clearing/_ems.py @@ -161,7 +161,7 @@ async def clear_dark_triggers( router: Router, brokerd_orders_stream: tractor.MsgStream, - quote_stream: tractor.ReceiveMsgStream, # noqa + quote_stream: tractor.MsgStream, broker: str, fqme: str, @@ -177,6 +177,7 @@ async def clear_dark_triggers( ''' # XXX: optimize this for speed! # TODO: + # - port to the new ringbuf stuff in `tractor.ipc`! # - numba all this! # - this stream may eventually contain multiple symbols quote_stream._raise_on_lag = False -- 2.34.1 From 35cb538a69ed0873fc39fdd3cb43618db2675000 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 24 Apr 2025 10:37:52 -0400 Subject: [PATCH 10/13] Update `binance` spot pairs with `amendAllowed` As per API updates, https://developers.binance.com/docs/binance-spot-api-docs https://developers.binance.com/docs/binance-spot-api-docs/faqs/order_amend_keep_priority I also slightly tweaked the filed mismatch exception note to include the `repr(pair_type)` so the dev can know which pair types should be changed. --- piker/brokers/binance/api.py | 11 ++++++++--- piker/brokers/binance/venues.py | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/piker/brokers/binance/api.py b/piker/brokers/binance/api.py index f163162e..78be9ef8 100644 --- a/piker/brokers/binance/api.py +++ b/piker/brokers/binance/api.py @@ -374,9 +374,14 @@ class Client: pair: Pair = pair_type(**item) except Exception as e: e.add_note( - "\nDon't panic, prolly stupid binance changed their symbology schema again..\n" - 'Check out their API docs here:\n\n' - '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 diff --git a/piker/brokers/binance/venues.py b/piker/brokers/binance/venues.py index 2c025fe1..fdcb5f12 100644 --- a/piker/brokers/binance/venues.py +++ b/piker/brokers/binance/venues.py @@ -144,6 +144,11 @@ class SpotPair(Pair, frozen=True): permissions: list[str] permissionSets: list[list[str]] + # can the paint botz creat liq gaps even easier on this asset? + # Bp + # https://developers.binance.com/docs/binance-spot-api-docs/faqs/order_amend_keep_priority + amendAllowed: bool + # NOTE: see `.data._symcache.SymbologyCache.load()` for why ns_path: str = 'piker.brokers.binance:SpotPair' -- 2.34.1 From bc72e3d2069075d318552d2d8f5d6ef02a86f6ad Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 24 Apr 2025 11:34:32 -0400 Subject: [PATCH 11/13] Drop unused `assets: dict` --- piker/brokers/deribit/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/piker/brokers/deribit/api.py b/piker/brokers/deribit/api.py index bf640c9e..0deceb20 100644 --- a/piker/brokers/deribit/api.py +++ b/piker/brokers/deribit/api.py @@ -328,7 +328,6 @@ class Client: ''' Return the set of currencies for deribit. ''' - assets = {} resp = await self._json_rpc_auth_wrapper( 'public/get_currencies', params={} -- 2.34.1 From d655e81290fb7ea370791a1998c972d45e2d771d Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 24 Apr 2025 12:15:26 -0400 Subject: [PATCH 12/13] max_pain: add piker logging, tweak var names, notes and todos --- examples/max_pain.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/examples/max_pain.py b/examples/max_pain.py index 82908163..36691889 100755 --- a/examples/max_pain.py +++ b/examples/max_pain.py @@ -2,22 +2,29 @@ from decimal import ( Decimal, ) +from pathlib import Path + import numpy as np -import polars as pl +# import polars as pl import trio import tractor from datetime import datetime -from pprint import pformat +# from pprint import pformat from piker.brokers.deribit.api import ( get_client, maybe_open_oi_feed, ) from piker.storage import open_storage_client, StorageClient +from piker.log import get_logger import sys import pyqtgraph as pg from PyQt6 import QtCore from pyqtgraph import ScatterPlotItem, InfiniteLine from PyQt6.QtWidgets import QApplication +from cryptofeed.symbols import Symbol + + +log = get_logger(__name__) # XXX, use 2 newlines between top level LOC (even between these # imports and the next function line ;) @@ -192,10 +199,13 @@ async def max_pain_daemon( ), ], dtype=dtype) - path = await client.write_oi( + path: Path = await client.write_oi( col_sym_key, data, ) + # TODO, use std logging like this throughout for status + # emissions on console! + log.info(f'Wrote OI history to {path}') def get_max_pain( oi_by_strikes: dict[str, dict[str, Decimal]] @@ -258,7 +268,7 @@ async def max_pain_daemon( # hardcoded to something, sorry.) timestamp = msg[1]['timestamp'] max_pain = get_max_pain(oi_by_strikes) - intrinsic_values = get_total_intrinsic_values(oi_by_strikes) + # intrinsic_values = get_total_intrinsic_values(oi_by_strikes) # graph here plot_graph(oi_by_strikes, plot) @@ -298,14 +308,27 @@ async def max_pain_daemon( async def main(): - async with tractor.open_nursery() as n: + async with tractor.open_nursery( + debug_mode=True, + loglevel='info', + ) as an: + from tractor import log + log.get_console_log(level='info') - p: tractor.Portal = await n.start_actor( + ptl: tractor.Portal = await an.start_actor( 'max_pain_daemon', enable_modules=[__name__], infect_asyncio=True, + # ^TODO, we can actually run this in the root-actor now + # if needed as per 2nd "section" in, + # https://pikers.dev/goodboy/tractor/pulls/2 + # + # NOTE, will first require us porting to modern + # `tractor:main` though ofc! + ) - await p.run(max_pain_daemon) + await ptl.run(max_pain_daemon) + if __name__ == '__main__': trio.run(main) -- 2.34.1 From bf33cb93b1e53c62a95b15480db1efa7e4afcac4 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 24 Apr 2025 12:53:32 -0400 Subject: [PATCH 13/13] Fix type-check assertion in ems test to use `is` --- tests/test_ems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ems.py b/tests/test_ems.py index c2f5d7a8..4a9e4a4c 100644 --- a/tests/test_ems.py +++ b/tests/test_ems.py @@ -179,7 +179,7 @@ def test_ems_err_on_bad_broker( # NOTE: emsd should error on the actor's enabled modules # import phase, when looking for a backend named `doggy`. except tractor.RemoteActorError as re: - assert re.type == ModuleNotFoundError + assert re.type is ModuleNotFoundError run_and_tollerate_cancels(load_bad_fqme) -- 2.34.1