Move order mode handling into charting code
parent
f3ae8db04b
commit
e6724b6559
|
@ -18,8 +18,10 @@
|
||||||
High level Qt chart widgets.
|
High level Qt chart widgets.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from pprint import pformat
|
||||||
from typing import Tuple, Dict, Any, Optional, Callable
|
from typing import Tuple, Dict, Any, Optional, Callable
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import time
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -950,23 +952,83 @@ async def _async_main(
|
||||||
linked_charts
|
linked_charts
|
||||||
)
|
)
|
||||||
|
|
||||||
async with open_order_mode(
|
|
||||||
chart,
|
|
||||||
) as order_mode:
|
|
||||||
|
|
||||||
# TODO: this should probably be implicitly spawned
|
|
||||||
# inside the above mngr?
|
|
||||||
|
|
||||||
# spawn EMS actor-service
|
# spawn EMS actor-service
|
||||||
await n.start(
|
async with open_ems(
|
||||||
open_ems,
|
|
||||||
order_mode,
|
|
||||||
brokername,
|
brokername,
|
||||||
symbol,
|
symbol,
|
||||||
)
|
) as (book, trades_stream):
|
||||||
|
|
||||||
# probably where we'll eventually start the user input loop
|
async with open_order_mode(
|
||||||
await trio.sleep_forever()
|
chart,
|
||||||
|
book,
|
||||||
|
) as order_mode:
|
||||||
|
|
||||||
|
def get_index(time: float):
|
||||||
|
# XXX: not sure why the time is so off here
|
||||||
|
# looks like we're gonna have to do some fixing..
|
||||||
|
ohlc = chart._shm.array
|
||||||
|
indexes = ohlc['time'] >= time
|
||||||
|
|
||||||
|
if any(indexes):
|
||||||
|
return ohlc['index'][indexes[-1]]
|
||||||
|
else:
|
||||||
|
return ohlc['index'][-1]
|
||||||
|
|
||||||
|
# Begin order-response streaming
|
||||||
|
|
||||||
|
# this is where we receive **back** messages
|
||||||
|
# about executions **from** the EMS actor
|
||||||
|
async for msg in trades_stream:
|
||||||
|
|
||||||
|
fmsg = pformat(msg)
|
||||||
|
log.info(f'Received order msg: {fmsg}')
|
||||||
|
|
||||||
|
# delete the line from view
|
||||||
|
oid = msg['oid']
|
||||||
|
resp = msg['resp']
|
||||||
|
|
||||||
|
# response to 'action' request (buy/sell)
|
||||||
|
if resp in ('dark_submitted', 'broker_submitted'):
|
||||||
|
|
||||||
|
# show line label once order is live
|
||||||
|
order_mode.on_submit(oid)
|
||||||
|
|
||||||
|
# resp to 'cancel' request or error condition
|
||||||
|
# for action request
|
||||||
|
elif resp in ('broker_cancelled', 'dark_cancelled'):
|
||||||
|
|
||||||
|
# delete level line from view
|
||||||
|
order_mode.on_cancel(oid)
|
||||||
|
|
||||||
|
elif resp in ('dark_executed'):
|
||||||
|
log.info(f'Dark order filled for {fmsg}')
|
||||||
|
|
||||||
|
# for alerts add a triangle and remove the
|
||||||
|
# level line
|
||||||
|
if msg['cmd']['action'] == 'alert':
|
||||||
|
|
||||||
|
# should only be one "fill" for an alert
|
||||||
|
order_mode.on_fill(
|
||||||
|
oid,
|
||||||
|
price=msg['trigger_price'],
|
||||||
|
arrow_index=get_index(time.time())
|
||||||
|
)
|
||||||
|
await order_mode.on_exec(oid, msg)
|
||||||
|
|
||||||
|
# response to completed 'action' request for buy/sell
|
||||||
|
elif resp in ('broker_executed',):
|
||||||
|
await order_mode.on_exec(oid, msg)
|
||||||
|
|
||||||
|
# each clearing tick is responded individually
|
||||||
|
elif resp in ('broker_filled',):
|
||||||
|
action = msg['action']
|
||||||
|
# TODO: some kinda progress system
|
||||||
|
order_mode.on_fill(
|
||||||
|
oid,
|
||||||
|
price=msg['price'],
|
||||||
|
arrow_index=get_index(msg['broker_time']),
|
||||||
|
pointing='up' if action == 'buy' else 'down',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def chart_from_quotes(
|
async def chart_from_quotes(
|
||||||
|
|
|
@ -33,7 +33,7 @@ import numpy as np
|
||||||
from ..log import get_logger
|
from ..log import get_logger
|
||||||
from ._style import _min_points_to_show, hcolor, _font
|
from ._style import _min_points_to_show, hcolor, _font
|
||||||
from ._graphics._lines import level_line, LevelLine
|
from ._graphics._lines import level_line, LevelLine
|
||||||
from .._ems import get_orders, OrderBook
|
from .._ems import OrderBook
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
@ -370,12 +370,16 @@ class ArrowEditor:
|
||||||
x: float,
|
x: float,
|
||||||
y: float,
|
y: float,
|
||||||
color='default',
|
color='default',
|
||||||
pointing: str = 'up',
|
pointing: Optional[str] = None,
|
||||||
) -> pg.ArrowItem:
|
) -> pg.ArrowItem:
|
||||||
"""Add an arrow graphic to view at given (x, y).
|
"""Add an arrow graphic to view at given (x, y).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
angle = 90 if pointing == 'up' else -90
|
angle = {
|
||||||
|
'up': 90,
|
||||||
|
'down': -90,
|
||||||
|
None: 0,
|
||||||
|
}[pointing]
|
||||||
|
|
||||||
yb = pg.mkBrush(hcolor(color))
|
yb = pg.mkBrush(hcolor(color))
|
||||||
arrow = pg.ArrowItem(
|
arrow = pg.ArrowItem(
|
||||||
|
@ -431,34 +435,33 @@ class OrderMode:
|
||||||
self.lines.stage_line(color=self._colors[name])
|
self.lines.stage_line(color=self._colors[name])
|
||||||
|
|
||||||
def on_submit(self, uuid: str) -> dict:
|
def on_submit(self, uuid: str) -> dict:
|
||||||
|
"""On order submitted event, commit the order line
|
||||||
|
and registered order uuid, store ack time stamp.
|
||||||
|
|
||||||
|
TODO: annotate order line with submission type ('live' vs.
|
||||||
|
'dark').
|
||||||
|
|
||||||
|
"""
|
||||||
self.lines.commit_line(uuid)
|
self.lines.commit_line(uuid)
|
||||||
req_msg = self.book._sent_orders.get(uuid)
|
req_msg = self.book._sent_orders.get(uuid)
|
||||||
req_msg['ack_time_ns'] = time.time_ns()
|
req_msg['ack_time_ns'] = time.time_ns()
|
||||||
# self.book._confirmed_orders[uuid] = req_msg
|
|
||||||
return req_msg
|
return req_msg
|
||||||
|
|
||||||
def on_fill(
|
def on_fill(
|
||||||
self,
|
self,
|
||||||
uuid: str,
|
uuid: str,
|
||||||
msg: Dict[str, Any],
|
price: float,
|
||||||
|
arrow_index: float,
|
||||||
|
pointing: Optional[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
log.info(f'New fill\n{pformat(msg)}')
|
|
||||||
line = self.lines._order_lines[uuid]
|
line = self.lines._order_lines[uuid]
|
||||||
|
|
||||||
# XXX: not sure why the time is so off here
|
|
||||||
# looks like we're gonna have to do some fixing..
|
|
||||||
ohlc = self.chart._shm.array
|
|
||||||
indexes = ohlc['time'] >= msg['broker_time']
|
|
||||||
if any(indexes):
|
|
||||||
arrow_index = ohlc['index'][indexes[-1]]
|
|
||||||
else:
|
|
||||||
arrow_index = ohlc['index'][-1]
|
|
||||||
|
|
||||||
self.arrows.add(
|
self.arrows.add(
|
||||||
uuid,
|
uuid,
|
||||||
arrow_index,
|
arrow_index,
|
||||||
msg['price'],
|
price,
|
||||||
pointing='up' if msg['action'] == 'buy' else 'down',
|
pointing=pointing,
|
||||||
color=line.color
|
color=line.color
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -526,11 +529,12 @@ class OrderMode:
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def open_order_mode(
|
async def open_order_mode(
|
||||||
chart,
|
chart,
|
||||||
|
book: OrderBook,
|
||||||
):
|
):
|
||||||
# global _order_lines
|
# global _order_lines
|
||||||
|
|
||||||
view = chart._vb
|
view = chart._vb
|
||||||
book = get_orders()
|
# book = get_orders()
|
||||||
lines = LineEditor(view=view, _order_lines=_order_lines, chart=chart)
|
lines = LineEditor(view=view, _order_lines=_order_lines, chart=chart)
|
||||||
arrows = ArrowEditor(chart, {})
|
arrows = ArrowEditor(chart, {})
|
||||||
|
|
||||||
|
@ -539,11 +543,6 @@ async def open_order_mode(
|
||||||
mode = OrderMode(chart, book, lines, arrows)
|
mode = OrderMode(chart, book, lines, arrows)
|
||||||
view.mode = mode
|
view.mode = mode
|
||||||
|
|
||||||
# # setup local ui event streaming channels for request/resp
|
|
||||||
# # streamging with EMS daemon
|
|
||||||
# global _to_ems, _from_order_book
|
|
||||||
# _to_ems, _from_order_book = trio.open_memory_channel(100)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield mode
|
yield mode
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue