Compare commits
No commits in common. "8a17a75ba2bc1b155410c97671293c35eec67ca2" and "fa0d088ebc01a84adcfcca03a71d91d8ae1da3ad" have entirely different histories.
8a17a75ba2
...
fa0d088ebc
|
|
@ -76,6 +76,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
# TODO: numba all of this
|
# TODO: numba all of this
|
||||||
def mk_check(
|
def mk_check(
|
||||||
|
|
||||||
trigger_price: float,
|
trigger_price: float,
|
||||||
known_last: float,
|
known_last: float,
|
||||||
action: str,
|
action: str,
|
||||||
|
|
@ -1189,16 +1190,12 @@ async def process_client_order_cmds(
|
||||||
submitting live orders immediately if requested by the client.
|
submitting live orders immediately if requested by the client.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# TODO, only allow `msgspec.Struct` form!
|
# cmd: dict
|
||||||
cmd: dict
|
|
||||||
async for cmd in client_order_stream:
|
async for cmd in client_order_stream:
|
||||||
log.info(
|
log.info(f'Received order cmd:\n{pformat(cmd)}')
|
||||||
f'Received order cmd:\n'
|
|
||||||
f'{pformat(cmd)}\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
# CAWT DAMN we need struct support!
|
# CAWT DAMN we need struct support!
|
||||||
oid: str = str(cmd['oid'])
|
oid = str(cmd['oid'])
|
||||||
|
|
||||||
# register this stream as an active order dialog (msg flow) for
|
# register this stream as an active order dialog (msg flow) for
|
||||||
# this order id such that translated message from the brokerd
|
# this order id such that translated message from the brokerd
|
||||||
|
|
@ -1304,7 +1301,7 @@ async def process_client_order_cmds(
|
||||||
case {
|
case {
|
||||||
'oid': oid,
|
'oid': oid,
|
||||||
'symbol': fqme,
|
'symbol': fqme,
|
||||||
'price': price,
|
'price': trigger_price,
|
||||||
'size': size,
|
'size': size,
|
||||||
'action': ('buy' | 'sell') as action,
|
'action': ('buy' | 'sell') as action,
|
||||||
'exec_mode': ('live' | 'paper'),
|
'exec_mode': ('live' | 'paper'),
|
||||||
|
|
@ -1336,7 +1333,7 @@ async def process_client_order_cmds(
|
||||||
|
|
||||||
symbol=sym,
|
symbol=sym,
|
||||||
action=action,
|
action=action,
|
||||||
price=price,
|
price=trigger_price,
|
||||||
size=size,
|
size=size,
|
||||||
account=req.account,
|
account=req.account,
|
||||||
)
|
)
|
||||||
|
|
@ -1358,11 +1355,7 @@ async def process_client_order_cmds(
|
||||||
# (``translate_and_relay_brokerd_events()`` above) will
|
# (``translate_and_relay_brokerd_events()`` above) will
|
||||||
# handle relaying the ems side responses back to
|
# handle relaying the ems side responses back to
|
||||||
# the client/cmd sender from this request
|
# the client/cmd sender from this request
|
||||||
log.info(
|
log.info(f'Sending live order to {broker}:\n{pformat(msg)}')
|
||||||
f'Sending live order to {broker}:\n'
|
|
||||||
f'{pformat(msg)}'
|
|
||||||
)
|
|
||||||
|
|
||||||
await brokerd_order_stream.send(msg)
|
await brokerd_order_stream.send(msg)
|
||||||
|
|
||||||
# an immediate response should be ``BrokerdOrderAck``
|
# an immediate response should be ``BrokerdOrderAck``
|
||||||
|
|
@ -1378,7 +1371,7 @@ async def process_client_order_cmds(
|
||||||
case {
|
case {
|
||||||
'oid': oid,
|
'oid': oid,
|
||||||
'symbol': fqme,
|
'symbol': fqme,
|
||||||
'price': price,
|
'price': trigger_price,
|
||||||
'size': size,
|
'size': size,
|
||||||
'exec_mode': exec_mode,
|
'exec_mode': exec_mode,
|
||||||
'action': action,
|
'action': action,
|
||||||
|
|
@ -1406,12 +1399,7 @@ async def process_client_order_cmds(
|
||||||
if isnan(last):
|
if isnan(last):
|
||||||
last = flume.rt_shm.array[-1]['close']
|
last = flume.rt_shm.array[-1]['close']
|
||||||
|
|
||||||
trigger_price: float = float(price)
|
pred = mk_check(trigger_price, last, action)
|
||||||
pred = mk_check(
|
|
||||||
trigger_price,
|
|
||||||
last,
|
|
||||||
action,
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE: for dark orders currently we submit
|
# NOTE: for dark orders currently we submit
|
||||||
# the triggered live order at a price 5 ticks
|
# the triggered live order at a price 5 ticks
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ Clearing sub-system message and protocols.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from decimal import Decimal
|
|
||||||
from typing import (
|
from typing import (
|
||||||
Literal,
|
Literal,
|
||||||
)
|
)
|
||||||
|
|
@ -72,15 +71,7 @@ class Order(Struct):
|
||||||
symbol: str # | MktPair
|
symbol: str # | MktPair
|
||||||
account: str # should we set a default as '' ?
|
account: str # should we set a default as '' ?
|
||||||
|
|
||||||
# https://docs.python.org/3/library/decimal.html#decimal-objects
|
price: float
|
||||||
#
|
|
||||||
# ?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"
|
size: float # -ve is "sell", +ve is "buy"
|
||||||
|
|
||||||
brokers: list[str] = []
|
brokers: list[str] = []
|
||||||
|
|
@ -187,7 +178,7 @@ class BrokerdOrder(Struct):
|
||||||
time_ns: int
|
time_ns: int
|
||||||
|
|
||||||
symbol: str # fqme
|
symbol: str # fqme
|
||||||
price: Decimal
|
price: float
|
||||||
size: float
|
size: float
|
||||||
|
|
||||||
# TODO: if we instead rely on a +ve/-ve size to determine
|
# TODO: if we instead rely on a +ve/-ve size to determine
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@ async def handle_order_requests(
|
||||||
reqid = await client.submit_limit(
|
reqid = await client.submit_limit(
|
||||||
oid=order.oid,
|
oid=order.oid,
|
||||||
symbol=f'{order.symbol}.{client.broker}',
|
symbol=f'{order.symbol}.{client.broker}',
|
||||||
price=float(order.price),
|
price=order.price,
|
||||||
action=order.action,
|
action=order.action,
|
||||||
size=order.size,
|
size=order.size,
|
||||||
# XXX: by default 0 tells ``ib_insync`` methods that
|
# XXX: by default 0 tells ``ib_insync`` methods that
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ Chart trading, the only way to scalp.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from decimal import Decimal
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import time
|
import time
|
||||||
|
|
@ -42,6 +41,7 @@ from piker.accounting import (
|
||||||
Position,
|
Position,
|
||||||
mk_allocator,
|
mk_allocator,
|
||||||
MktPair,
|
MktPair,
|
||||||
|
Symbol,
|
||||||
)
|
)
|
||||||
from piker.clearing import (
|
from piker.clearing import (
|
||||||
open_ems,
|
open_ems,
|
||||||
|
|
@ -143,15 +143,6 @@ class OrderMode:
|
||||||
}
|
}
|
||||||
_staged_order: Order | None = None
|
_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(
|
def on_level_change_update_next_order_info(
|
||||||
self,
|
self,
|
||||||
level: float,
|
level: float,
|
||||||
|
|
@ -181,11 +172,7 @@ class OrderMode:
|
||||||
line.update_labels(order_info)
|
line.update_labels(order_info)
|
||||||
|
|
||||||
# update bound-in staged order
|
# update bound-in staged order
|
||||||
mkt: MktPair = self.curr_mkt
|
order.price = level
|
||||||
order.price: Decimal = mkt.quantize(
|
|
||||||
size=level,
|
|
||||||
quantity_type='price',
|
|
||||||
)
|
|
||||||
order.size = order_info['size']
|
order.size = order_info['size']
|
||||||
|
|
||||||
# when an order is changed we flip the settings side-pane to
|
# when an order is changed we flip the settings side-pane to
|
||||||
|
|
@ -200,9 +187,7 @@ class OrderMode:
|
||||||
|
|
||||||
) -> LevelLine:
|
) -> LevelLine:
|
||||||
|
|
||||||
# TODO, if we instead just always decimalize at the ems layer
|
level = order.price
|
||||||
# we can avoid this back-n-forth casting?
|
|
||||||
level = float(order.price)
|
|
||||||
|
|
||||||
line = order_line(
|
line = order_line(
|
||||||
chart or self.chart,
|
chart or self.chart,
|
||||||
|
|
@ -239,11 +224,7 @@ class OrderMode:
|
||||||
# the order mode allocator but we still need to update the
|
# the order mode allocator but we still need to update the
|
||||||
# "staged" order message we'll send to the ems
|
# "staged" order message we'll send to the ems
|
||||||
def update_order_price(y: float) -> None:
|
def update_order_price(y: float) -> None:
|
||||||
mkt: MktPair = self.curr_mkt
|
order.price = y
|
||||||
order.price: Decimal = mkt.quantize(
|
|
||||||
size=y,
|
|
||||||
quantity_type='price',
|
|
||||||
)
|
|
||||||
|
|
||||||
line._on_level_change = update_order_price
|
line._on_level_change = update_order_price
|
||||||
|
|
||||||
|
|
@ -294,31 +275,34 @@ class OrderMode:
|
||||||
chart = cursor.linked.chart
|
chart = cursor.linked.chart
|
||||||
if (
|
if (
|
||||||
not chart
|
not chart
|
||||||
and
|
and cursor
|
||||||
cursor
|
and cursor.active_plot
|
||||||
and
|
|
||||||
cursor.active_plot
|
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
chart = cursor.active_plot
|
chart = cursor.active_plot
|
||||||
price: float = cursor._datum_xy[1]
|
price = cursor._datum_xy[1]
|
||||||
if not price:
|
if not price:
|
||||||
# zero prices are not supported by any means
|
# zero prices are not supported by any means
|
||||||
# since that's illogical / a no-op.
|
# since that's illogical / a no-op.
|
||||||
return
|
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
|
# TODO: should we be enforcing this precision
|
||||||
# at a different layer in the stack?
|
# at a different layer in the stack? right now
|
||||||
# |_ might require `MktPair` tracking in the EMS?
|
# any precision error will literally be relayed
|
||||||
# |_ right now any precision error will be relayed
|
# all the way back from the backend.
|
||||||
# all the way back from the backend and vice-versa..
|
|
||||||
#
|
price = round(
|
||||||
mkt: MktPair = self.curr_mkt
|
price,
|
||||||
price: Decimal = mkt.quantize(
|
ndigits=mkt.price_tick_digits,
|
||||||
size=price,
|
|
||||||
quantity_type='price',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
order = self._staged_order = Order(
|
order = self._staged_order = Order(
|
||||||
action=action,
|
action=action,
|
||||||
price=price,
|
price=price,
|
||||||
|
|
@ -394,7 +378,7 @@ class OrderMode:
|
||||||
'oid': oid,
|
'oid': oid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if float(order.price) <= 0:
|
if order.price <= 0:
|
||||||
log.error(
|
log.error(
|
||||||
'*!? Invalid `Order.price <= 0` ?!*\n'
|
'*!? Invalid `Order.price <= 0` ?!*\n'
|
||||||
# TODO: make this present multi-line in object form
|
# TODO: make this present multi-line in object form
|
||||||
|
|
@ -531,15 +515,14 @@ class OrderMode:
|
||||||
# if an order msg is provided update the line
|
# if an order msg is provided update the line
|
||||||
# **from** that msg.
|
# **from** that msg.
|
||||||
if order:
|
if order:
|
||||||
price: float = float(order.price)
|
if order.price <= 0:
|
||||||
if price <= 0:
|
|
||||||
log.error(f'Order has 0 price, cancelling..\n{order}')
|
log.error(f'Order has 0 price, cancelling..\n{order}')
|
||||||
self.cancel_orders([order.oid])
|
self.cancel_orders([order.oid])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
line.set_level(price)
|
line.set_level(order.price)
|
||||||
self.on_level_change_update_next_order_info(
|
self.on_level_change_update_next_order_info(
|
||||||
level=price,
|
level=order.price,
|
||||||
line=line,
|
line=line,
|
||||||
order=order,
|
order=order,
|
||||||
# use the corresponding position tracker for the
|
# use the corresponding position tracker for the
|
||||||
|
|
@ -698,9 +681,9 @@ class OrderMode:
|
||||||
) -> Dialog | None:
|
) -> Dialog | None:
|
||||||
# NOTE: the `.order` attr **must** be set with the
|
# NOTE: the `.order` attr **must** be set with the
|
||||||
# equivalent order msg in order to be loaded.
|
# equivalent order msg in order to be loaded.
|
||||||
order: Order = msg.req
|
order = msg.req
|
||||||
oid = str(msg.oid)
|
oid = str(msg.oid)
|
||||||
symbol: str = order.symbol
|
symbol = order.symbol
|
||||||
|
|
||||||
# TODO: MEGA UGGG ZONEEEE!
|
# TODO: MEGA UGGG ZONEEEE!
|
||||||
src = msg.src
|
src = msg.src
|
||||||
|
|
@ -719,22 +702,13 @@ class OrderMode:
|
||||||
order.oid = str(order.oid)
|
order.oid = str(order.oid)
|
||||||
order.brokers = [brokername]
|
order.brokers = [brokername]
|
||||||
|
|
||||||
# ?TODO? change this over to `MktPair`, but it's gonna be
|
# TODO: change this over to `MktPair`, but it's
|
||||||
# tough since we don't have any such data really in our
|
# gonna be tough since we don't have any such data
|
||||||
# clearing msg schema..
|
# really in our clearing msg schema..
|
||||||
# BUT WAIT! WHY do we even want/need this!?
|
order.symbol = Symbol.from_fqme(
|
||||||
#
|
fqsn=fqme,
|
||||||
# order.symbol = self.curr_mkt
|
info={},
|
||||||
#
|
)
|
||||||
# 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(
|
maybe_dialog: Dialog | None = self.submit_order(
|
||||||
send_msg=False,
|
send_msg=False,
|
||||||
order=order,
|
order=order,
|
||||||
|
|
@ -1127,7 +1101,7 @@ async def process_trade_msg(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
msg.req: Order = order
|
msg.req = order
|
||||||
dialog: (
|
dialog: (
|
||||||
Dialog
|
Dialog
|
||||||
# NOTE: on an invalid order submission (eg.
|
# NOTE: on an invalid order submission (eg.
|
||||||
|
|
@ -1192,7 +1166,7 @@ async def process_trade_msg(
|
||||||
tm = time.time()
|
tm = time.time()
|
||||||
mode.on_fill(
|
mode.on_fill(
|
||||||
oid,
|
oid,
|
||||||
price=float(req.price),
|
price=req.price,
|
||||||
time_s=tm,
|
time_s=tm,
|
||||||
)
|
)
|
||||||
mode.lines.remove_line(uuid=oid)
|
mode.lines.remove_line(uuid=oid)
|
||||||
|
|
@ -1247,7 +1221,7 @@ async def process_trade_msg(
|
||||||
tm = details['broker_time']
|
tm = details['broker_time']
|
||||||
mode.on_fill(
|
mode.on_fill(
|
||||||
oid,
|
oid,
|
||||||
price=float(details['price']),
|
price=details['price'],
|
||||||
time_s=tm,
|
time_s=tm,
|
||||||
pointing='up' if action == 'buy' else 'down',
|
pointing='up' if action == 'buy' else 'down',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ def test_ems_err_on_bad_broker(
|
||||||
# NOTE: emsd should error on the actor's enabled modules
|
# NOTE: emsd should error on the actor's enabled modules
|
||||||
# import phase, when looking for a backend named `doggy`.
|
# import phase, when looking for a backend named `doggy`.
|
||||||
except tractor.RemoteActorError as re:
|
except tractor.RemoteActorError as re:
|
||||||
assert re.type is ModuleNotFoundError
|
assert re.type == ModuleNotFoundError
|
||||||
|
|
||||||
run_and_tollerate_cancels(load_bad_fqme)
|
run_and_tollerate_cancels(load_bad_fqme)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue