Finally, support full `MktPair` + `Asset` msgs
Previously we weren't necessarily serializing mkt pairs (for IPC msging) entirely bc the assets `.src/.dst` were being sent just by their str-names. This now properly supports fully serializing `Asset`s as `dict`-msgs such that use of `MktPair.to_dict()` can be transmitted over `tractor.MsgStream`s and deserialized entirely back to struct from on the receiver end. Deats: - implement `Asset.to_dict()` and `.from_msg()` - adjust `MktPair.to_dict()` and `.from_msg()` to use these methods. - drop all the hacky "if .src/.dst is str" handling. - add better `MktPair.from_fqme()` input handling for expiry and venue; ensure that either can be extracted from passed fqme *and* if so they are also popped from any duplicate passed in `**kwargs**`.account_tests
parent
c8c28df62f
commit
309b91676d
|
@ -130,8 +130,26 @@ class Asset(Struct, frozen=True):
|
||||||
# should not be explicitly required in our generic API.
|
# should not be explicitly required in our generic API.
|
||||||
info: dict | None = None
|
info: dict | None = None
|
||||||
|
|
||||||
# TODO?
|
# `None` is not toml-compat so drop info
|
||||||
# _to_dict_skip = {'info'}
|
# if no extra data added..
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
dct = super().to_dict()
|
||||||
|
if (info := dct.pop('info', None)):
|
||||||
|
dct['info'] = info
|
||||||
|
|
||||||
|
assert dct['tx_tick']
|
||||||
|
return dct
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_msg(
|
||||||
|
cls,
|
||||||
|
msg: dict[str, Any],
|
||||||
|
) -> Asset:
|
||||||
|
return Asset(
|
||||||
|
tx_tick=Decimal(str(msg.pop('tx_tick'))),
|
||||||
|
info=msg.pop('info', None),
|
||||||
|
**msg,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -288,6 +306,8 @@ class MktPair(Struct, frozen=True):
|
||||||
# strike price, call or put, swap type, exercise model, etc.
|
# strike price, call or put, swap type, exercise model, etc.
|
||||||
contract_info: list[str] | None = None
|
contract_info: list[str] | None = None
|
||||||
|
|
||||||
|
# TODO: rename to sectype since all of these can
|
||||||
|
# be considered "securities"?
|
||||||
_atype: str = ''
|
_atype: str = ''
|
||||||
|
|
||||||
# allow explicit disable of the src part of the market
|
# allow explicit disable of the src part of the market
|
||||||
|
@ -298,6 +318,18 @@ class MktPair(Struct, frozen=True):
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.fqme
|
return self.fqme
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
d = super().to_dict()
|
||||||
|
d['src'] = self.src.to_dict()
|
||||||
|
d['dst'] = self.dst.to_dict()
|
||||||
|
|
||||||
|
if self.contract_info is None:
|
||||||
|
d.pop('contract_info')
|
||||||
|
|
||||||
|
# d.pop('_fqme_without_src')
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_msg(
|
def from_msg(
|
||||||
cls,
|
cls,
|
||||||
|
@ -309,35 +341,20 @@ class MktPair(Struct, frozen=True):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
dst_asset_msg = msg.pop('dst')
|
dst_asset_msg = msg.pop('dst')
|
||||||
|
dst = Asset.from_msg(dst_asset_msg) # .copy()
|
||||||
|
|
||||||
src_asset_msg = msg.pop('src')
|
src_asset_msg = msg.pop('src')
|
||||||
|
src = Asset.from_msg(src_asset_msg) # .copy()
|
||||||
|
|
||||||
if isinstance(dst_asset_msg, str):
|
|
||||||
src: str = str(src_asset_msg)
|
|
||||||
assert isinstance(src, str)
|
|
||||||
return cls.from_fqme(
|
|
||||||
dst_asset_msg,
|
|
||||||
src=src,
|
|
||||||
**msg,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# NOTE: we call `.copy()` here to ensure
|
|
||||||
# type casting!
|
|
||||||
dst = Asset(**dst_asset_msg).copy()
|
|
||||||
if not isinstance(src_asset_msg, str):
|
|
||||||
src = Asset(**src_asset_msg).copy()
|
|
||||||
else:
|
|
||||||
src = str(src_asset_msg)
|
|
||||||
|
|
||||||
return cls(
|
|
||||||
dst=dst,
|
|
||||||
src=src,
|
|
||||||
**msg,
|
|
||||||
# XXX NOTE: ``msgspec`` can encode `Decimal`
|
# XXX NOTE: ``msgspec`` can encode `Decimal`
|
||||||
# but it doesn't decide to it by default since
|
# but it doesn't decide to it by default since
|
||||||
# we aren't spec-cing these msgs as structs, SO
|
# we aren't spec-cing these msgs as structs, SO
|
||||||
# we have to ensure we do a struct type case (which `.copy()`
|
# we have to ensure we do a struct type case (which `.copy()`
|
||||||
# does) to ensure we get the right type!
|
# does) to ensure we get the right type!
|
||||||
|
return cls(
|
||||||
|
dst=dst,
|
||||||
|
src=src,
|
||||||
|
**msg,
|
||||||
).copy()
|
).copy()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -365,7 +382,20 @@ class MktPair(Struct, frozen=True):
|
||||||
):
|
):
|
||||||
_fqme = f'{fqme}.{broker}'
|
_fqme = f'{fqme}.{broker}'
|
||||||
|
|
||||||
broker, mkt_ep_key, venue, suffix = unpack_fqme(_fqme)
|
broker, mkt_ep_key, venue, expiry = unpack_fqme(_fqme)
|
||||||
|
|
||||||
|
kven: str = kwargs.pop('venue', venue)
|
||||||
|
if venue:
|
||||||
|
assert venue == kven
|
||||||
|
else:
|
||||||
|
venue = kven
|
||||||
|
|
||||||
|
exp: str = kwargs.pop('expiry', expiry)
|
||||||
|
if expiry:
|
||||||
|
assert exp == expiry
|
||||||
|
else:
|
||||||
|
expiry = exp
|
||||||
|
|
||||||
dst: Asset = Asset.guess_from_mkt_ep_key(
|
dst: Asset = Asset.guess_from_mkt_ep_key(
|
||||||
mkt_ep_key,
|
mkt_ep_key,
|
||||||
atype=kwargs.get('_atype'),
|
atype=kwargs.get('_atype'),
|
||||||
|
@ -384,7 +414,7 @@ class MktPair(Struct, frozen=True):
|
||||||
venue=venue,
|
venue=venue,
|
||||||
# XXX NOTE: we presume this token
|
# XXX NOTE: we presume this token
|
||||||
# if the expiry for now!
|
# if the expiry for now!
|
||||||
expiry=suffix,
|
expiry=expiry,
|
||||||
|
|
||||||
price_tick=price_tick,
|
price_tick=price_tick,
|
||||||
size_tick=size_tick,
|
size_tick=size_tick,
|
||||||
|
|
Loading…
Reference in New Issue