Add mouse drag order update support to UI

basic_orders
Tyler Goodlet 2021-03-07 16:01:42 -05:00
parent 6851bacd0a
commit de5a69c59c
1 changed files with 118 additions and 82 deletions

View File

@ -244,65 +244,66 @@ class LineEditor:
symbol = chart._lc.symbol symbol = chart._lc.symbol
line = self._stage_line # line = self._stage_line
if not line: # if not line:
# add a "staged" cursor-tracking line to view # add a "staged" cursor-tracking line to view
# and cash it in a a var # and cash it in a a var
line = order_line( if self._active_staged_line:
chart, self.unstage_line()
level=y, line = order_line(
level_digits=symbol.digits(), chart,
size=size,
size_digits=symbol.lot_digits(),
# just for the stage line to avoid level=y,
# flickering while moving the cursor level_digits=symbol.digits(),
# around where it might trigger highlight size=size,
# then non-highlight depending on sensitivity size_digits=symbol.lot_digits(),
always_show_labels=True,
# kwargs # just for the stage line to avoid
color=color, # flickering while moving the cursor
# don't highlight the "staging" line # around where it might trigger highlight
hl_on_hover=hl_on_hover, # then non-highlight depending on sensitivity
dotted=dotted, always_show_labels=True,
)
# line.label._use_extra_fields = size is not None
# cache staging line after creation # kwargs
self._stage_line = line color=color,
# don't highlight the "staging" line
hl_on_hover=hl_on_hover,
dotted=dotted,
)
# line.label._use_extra_fields = size is not None
else: # cache staging line after creation
# apply input settings to existing staging line # self._stage_line = line
# label = line.label
# disable order size and other extras in label # else:
# label._use_extra_fields = size is not None # # apply input settings to existing staging line
# label.size = size # # label = line.label
# label.color = color # # disable order size and other extras in label
# # label._use_extra_fields = size is not None
# # label.size = size
# Use the existing staged line instead but copy # # label.color = color
# over it's current style "properties".
# Saves us allocating more mem / objects repeatedly
line._hoh = hl_on_hover
line._dotted = dotted
line.color = color
line.setMouseHover(hl_on_hover)
line.show()
line.show_labels()
# XXX: must have this to trigger updated # # Use the existing staged line instead but copy
# label contents rendering # # over it's current style "properties".
line.set_level(level=y) # # Saves us allocating more mem / objects repeatedly
# line._hoh = hl_on_hover
# line._dotted = dotted
# line.color = color
# line.setMouseHover(hl_on_hover)
# line.show()
# line.show_labels()
# # XXX: must have this to trigger updated
# # label contents rendering
# line.set_level(level=y)
self._active_staged_line = line self._active_staged_line = line
# hide crosshair y-line and label # hide crosshair y-line and label
cg = cursor.graphics[chart] cursor.hide_xhair()
cg['hl'].hide()
cg['yl'].hide()
# add line to cursor trackers # add line to cursor trackers
cursor._trackers.add(line) cursor._trackers.add(line)
@ -313,45 +314,45 @@ class LineEditor:
"""Inverse of ``.stage_line()``. """Inverse of ``.stage_line()``.
""" """
chart = self.chart._cursor.active_plot # chart = self.chart._cursor.active_plot
chart.setCursor(QtCore.Qt.ArrowCursor) # # chart.setCursor(QtCore.Qt.ArrowCursor)
cursor = chart._cursor cursor = self.chart._cursor
# delete "staged" cursor tracking line from view # delete "staged" cursor tracking line from view
line = self._active_staged_line line = self._active_staged_line
if line: if line:
cursor._trackers.remove(line) cursor._trackers.remove(line)
line.delete()
self._active_staged_line = None self._active_staged_line = None
sl = self._stage_line # sl = self._stage_line
if sl: # if sl:
sl.hide() # sl.hide()
sl.hide_labels() # sl.hide_labels()
# show the crosshair y line and label # show the crosshair y line and label
cg = cursor.graphics[chart] cursor.show_xhair()
cg['hl'].show()
cg['yl'].show()
def create_order_line( def create_order_line(
self, self,
uuid: str, uuid: str,
level: float,
chart: 'ChartPlotWidget', # noqa
size: float, size: float,
) -> LevelLine: ) -> LevelLine:
line = self._active_staged_line line = self._active_staged_line
if not line: if not line:
raise RuntimeError("No line commit is currently staged!?") raise RuntimeError("No line is currently staged!?")
chart = self.chart._cursor.active_plot
y = chart._cursor._datum_xy[1]
sym = chart._lc.symbol sym = chart._lc.symbol
line = order_line( line = order_line(
chart, chart,
# label fields default values # label fields default values
level=y, level=level,
level_digits=sym.digits(), level_digits=sym.digits(),
size=size, size=size,
@ -361,12 +362,14 @@ class LineEditor:
color=line.color, color=line.color,
dotted=line._dotted, dotted=line._dotted,
) )
# for now, until submission reponse arrives # for now, until submission reponse arrives
line.hide_labels() line.hide_labels()
# register for later lookup/deletion # register for later lookup/deletion
self._order_lines[uuid] = line self._order_lines[uuid] = line
return line, y
return line
def commit_line(self, uuid: str) -> LevelLine: def commit_line(self, uuid: str) -> LevelLine:
"""Commit a "staged line" to view. """Commit a "staged line" to view.
@ -375,16 +378,14 @@ class LineEditor:
graphic in view. graphic in view.
""" """
if uuid is None:
breakpoint()
try: try:
line = self._order_lines[uuid] line = self._order_lines[uuid]
except KeyError: except KeyError:
log.warning(f'No line for {uuid} could be found?') log.warning(f'No line for {uuid} could be found?')
return return
else: else:
line.oid = uuid assert line.oid == uuid
# line.oid = uuid
# line.set_level(line.level) # line.set_level(line.level)
line.show_labels() line.show_labels()
# line.label.show() # line.label.show()
@ -448,7 +449,7 @@ class ArrowEditor:
angle = { angle = {
'up': 90, 'up': 90,
'down': -90, 'down': -90,
None: 180, # pointing to right None: 180, # pointing to right (as in an alert)
}[pointing] }[pointing]
arrow = pg.ArrowItem( arrow = pg.ArrowItem(
@ -480,6 +481,11 @@ class ArrowEditor:
class OrderMode: class OrderMode:
"""Major mode for placing orders on a chart view. """Major mode for placing orders on a chart view.
This is the default mode that pairs with "follow mode"
(when wathing the rt price update at the current time step)
and allows entering orders using the ``a, d, f`` keys and
cancelling moused-over orders with the ``c`` key.
""" """
chart: 'ChartPlotWidget' # type: ignore # noqa chart: 'ChartPlotWidget' # type: ignore # noqa
book: OrderBook book: OrderBook
@ -581,6 +587,7 @@ class OrderMode:
msg = self.book._sent_orders.pop(uuid, None) msg = self.book._sent_orders.pop(uuid, None)
if msg is not None: if msg is not None:
self.lines.remove_line(uuid=uuid) self.lines.remove_line(uuid=uuid)
self.chart._cursor.show_xhair()
else: else:
log.warning( log.warning(
f'Received cancel for unsubmitted order {pformat(msg)}' f'Received cancel for unsubmitted order {pformat(msg)}'
@ -601,24 +608,61 @@ class OrderMode:
size = size or self._size size = size or self._size
# make line graphic chart = self.chart._cursor.active_plot
line, y = self.lines.create_order_line( y = chart._cursor._datum_xy[1]
uid,
size=size, symbol = self.chart._lc._symbol
)
line.oid = uid
# send order cmd to ems # send order cmd to ems
self.book.send( self.book.send(
uuid=uid, uuid=uid,
symbol=self.chart._lc._symbol, symbol=symbol.key,
brokers=symbol.brokers,
price=y, price=y,
size=size, size=size,
action=self._action, action=self._action,
exec_mode=self._exec_mode, exec_mode=self._exec_mode,
) )
# make line graphic if order push was
# sucessful
line = self.lines.create_order_line(
uid,
level=y,
chart=chart,
size=size,
)
line.oid = uid
# hook up mouse drag handlers
line._on_drag_start = self.order_line_modify_start
line._on_drag_end = self.order_line_modify_complete
return line return line
def cancel_order_under_cursor(self) -> None:
for line in self.lines.lines_under_cursor():
self.book.cancel(uuid=line.oid)
# order-line modify handlers
def order_line_modify_start(
self,
line: LevelLine,
) -> None:
print(f'Line modify: {line}')
# cancel original order until new position is found
def order_line_modify_complete(
self,
line: LevelLine,
) -> None:
self.book.update(
uuid=line.oid,
# TODO: should we round this to a nearest tick here?
price=line.value(),
)
@asynccontextmanager @asynccontextmanager
async def open_order_mode( async def open_order_mode(
@ -943,7 +987,7 @@ class ChartView(ViewBox):
self.select_box.clear() self.select_box.clear()
# cancel order or clear graphics # cancel order or clear graphics
if key == QtCore.Qt.Key_C: if key == QtCore.Qt.Key_C or key == QtCore.Qt.Key_Delete:
# delete any lines under the cursor # delete any lines under the cursor
mode = self.mode mode = self.mode
for line in mode.lines.lines_under_cursor(): for line in mode.lines.lines_under_cursor():
@ -966,14 +1010,6 @@ class ChartView(ViewBox):
elif key == QtCore.Qt.Key_A: elif key == QtCore.Qt.Key_A:
self.mode.set_exec('alert') self.mode.set_exec('alert')
# delete orders under cursor
elif key == QtCore.Qt.Key_Delete:
# delete any lines under the cursor
mode = self.mode
for line in mode.lines.lines_under_cursor():
mode.book.cancel(uuid=line.oid)
# XXX: Leaving this for light reference purposes, there # XXX: Leaving this for light reference purposes, there
# seems to be some work to at least gawk at for history mgmt. # seems to be some work to at least gawk at for history mgmt.