Move order mode handling into charting code

basic_orders
Tyler Goodlet 2021-01-18 20:28:37 -05:00
parent f3ae8db04b
commit e6724b6559
2 changed files with 99 additions and 38 deletions

View File

@ -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(

View File

@ -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