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(
 | 
					            # spawn EMS actor-service
 | 
				
			||||||
                chart,
 | 
					            async with open_ems(
 | 
				
			||||||
            ) as order_mode:
 | 
					                brokername,
 | 
				
			||||||
 | 
					                symbol,
 | 
				
			||||||
 | 
					            ) as (book, trades_stream):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # TODO: this should probably be implicitly spawned
 | 
					                async with open_order_mode(
 | 
				
			||||||
                # inside the above mngr?
 | 
					                    chart,
 | 
				
			||||||
 | 
					                    book,
 | 
				
			||||||
 | 
					                ) as order_mode:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # spawn EMS actor-service
 | 
					                    def get_index(time: float):
 | 
				
			||||||
                await n.start(
 | 
					                        # XXX: not sure why the time is so off here
 | 
				
			||||||
                    open_ems,
 | 
					                        # looks like we're gonna have to do some fixing..
 | 
				
			||||||
                    order_mode,
 | 
					                        ohlc = chart._shm.array
 | 
				
			||||||
                    brokername,
 | 
					                        indexes = ohlc['time'] >= time
 | 
				
			||||||
                    symbol,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # probably where we'll eventually start the user input loop
 | 
					                        if any(indexes):
 | 
				
			||||||
                await trio.sleep_forever()
 | 
					                            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