Start input handling **after** order mode is up
parent
c971997f1a
commit
ca23825aff
|
@ -158,18 +158,20 @@ class GodWidget(QtGui.QWidget):
|
||||||
# self.toolbar_layout.addWidget(self.strategy_box)
|
# self.toolbar_layout.addWidget(self.strategy_box)
|
||||||
|
|
||||||
def load_symbol(
|
def load_symbol(
|
||||||
|
|
||||||
self,
|
self,
|
||||||
providername: str,
|
providername: str,
|
||||||
symbol_key: str,
|
symbol_key: str,
|
||||||
loglevel: str,
|
loglevel: str,
|
||||||
ohlc: bool = True,
|
ohlc: bool = True,
|
||||||
reset: bool = False,
|
reset: bool = False,
|
||||||
) -> None:
|
|
||||||
"""Load a new contract into the charting app.
|
) -> trio.Event:
|
||||||
|
'''Load a new contract into the charting app.
|
||||||
|
|
||||||
Expects a ``numpy`` structured array containing all the ohlcv fields.
|
Expects a ``numpy`` structured array containing all the ohlcv fields.
|
||||||
|
|
||||||
"""
|
'''
|
||||||
# our symbol key style is always lower case
|
# our symbol key style is always lower case
|
||||||
symbol_key = symbol_key.lower()
|
symbol_key = symbol_key.lower()
|
||||||
|
|
||||||
|
@ -178,6 +180,8 @@ class GodWidget(QtGui.QWidget):
|
||||||
|
|
||||||
linkedsplits = self.get_chart_symbol(fqsn)
|
linkedsplits = self.get_chart_symbol(fqsn)
|
||||||
|
|
||||||
|
order_mode_started = trio.Event()
|
||||||
|
|
||||||
if not self.vbox.isEmpty():
|
if not self.vbox.isEmpty():
|
||||||
# XXX: this is CRITICAL especially with pixel buffer caching
|
# XXX: this is CRITICAL especially with pixel buffer caching
|
||||||
self.linkedsplits.hide()
|
self.linkedsplits.hide()
|
||||||
|
@ -200,10 +204,15 @@ class GodWidget(QtGui.QWidget):
|
||||||
providername,
|
providername,
|
||||||
symbol_key,
|
symbol_key,
|
||||||
loglevel,
|
loglevel,
|
||||||
|
order_mode_started,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.set_chart_symbol(fqsn, linkedsplits)
|
self.set_chart_symbol(fqsn, linkedsplits)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# symbol is already loaded and ems ready
|
||||||
|
order_mode_started.set()
|
||||||
|
|
||||||
self.vbox.addWidget(linkedsplits)
|
self.vbox.addWidget(linkedsplits)
|
||||||
|
|
||||||
# chart is already in memory so just focus it
|
# chart is already in memory so just focus it
|
||||||
|
@ -223,6 +232,8 @@ class GodWidget(QtGui.QWidget):
|
||||||
f'tick:{symbol.tick_size}'
|
f'tick:{symbol.tick_size}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return order_mode_started
|
||||||
|
|
||||||
|
|
||||||
class LinkedSplits(QtGui.QWidget):
|
class LinkedSplits(QtGui.QWidget):
|
||||||
'''
|
'''
|
||||||
|
@ -1527,6 +1538,8 @@ async def display_symbol_data(
|
||||||
sym: str,
|
sym: str,
|
||||||
loglevel: str,
|
loglevel: str,
|
||||||
|
|
||||||
|
order_mode_started: trio.Event,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
'''Spawn a real-time displayed and updated chart for provider symbol.
|
'''Spawn a real-time displayed and updated chart for provider symbol.
|
||||||
|
|
||||||
|
@ -1623,7 +1636,6 @@ async def display_symbol_data(
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# load initial fsp chain (otherwise known as "indicators")
|
# load initial fsp chain (otherwise known as "indicators")
|
||||||
n.start_soon(
|
n.start_soon(
|
||||||
spawn_fsps,
|
spawn_fsps,
|
||||||
|
@ -1644,6 +1656,7 @@ async def display_symbol_data(
|
||||||
wap_in_history,
|
wap_in_history,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO: instead we should start based on instrument trading hours?
|
||||||
# wait for a first quote before we start any update tasks
|
# wait for a first quote before we start any update tasks
|
||||||
# quote = await feed.receive()
|
# quote = await feed.receive()
|
||||||
# log.info(f'Received first quote {quote}')
|
# log.info(f'Received first quote {quote}')
|
||||||
|
@ -1651,24 +1664,11 @@ async def display_symbol_data(
|
||||||
n.start_soon(
|
n.start_soon(
|
||||||
check_for_new_bars,
|
check_for_new_bars,
|
||||||
feed,
|
feed,
|
||||||
# delay,
|
|
||||||
ohlcv,
|
ohlcv,
|
||||||
linkedsplits
|
linkedsplits
|
||||||
)
|
)
|
||||||
|
|
||||||
# interactive testing
|
await start_order_mode(chart, symbol, provider, order_mode_started)
|
||||||
# n.start_soon(
|
|
||||||
# test_bed,
|
|
||||||
# ohlcv,
|
|
||||||
# chart,
|
|
||||||
# linkedsplits,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# start async input handling for chart's view
|
|
||||||
# await godwidget._task_stack.enter_async_context(
|
|
||||||
async with chart._vb.open_async_input_handler():
|
|
||||||
|
|
||||||
await start_order_mode(chart, symbol, provider)
|
|
||||||
|
|
||||||
|
|
||||||
async def load_providers(
|
async def load_providers(
|
||||||
|
@ -1773,7 +1773,7 @@ async def _async_main(
|
||||||
symbol, _, provider = sym.rpartition('.')
|
symbol, _, provider = sym.rpartition('.')
|
||||||
|
|
||||||
# this internally starts a ``display_symbol_data()`` task above
|
# this internally starts a ``display_symbol_data()`` task above
|
||||||
godwidget.load_symbol(provider, symbol, loglevel)
|
order_mode_ready = godwidget.load_symbol(provider, symbol, loglevel)
|
||||||
|
|
||||||
# spin up a search engine for the local cached symbol set
|
# spin up a search engine for the local cached symbol set
|
||||||
async with _search.register_symbol_search(
|
async with _search.register_symbol_search(
|
||||||
|
@ -1791,6 +1791,8 @@ async def _async_main(
|
||||||
# the chart's select cache
|
# the chart's select cache
|
||||||
root_n.start_soon(load_providers, brokernames, loglevel)
|
root_n.start_soon(load_providers, brokernames, loglevel)
|
||||||
|
|
||||||
|
await order_mode_ready.wait()
|
||||||
|
|
||||||
# start handling search bar kb inputs
|
# start handling search bar kb inputs
|
||||||
async with (
|
async with (
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,9 @@ from typing import Optional, Dict, Callable, Any
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import trio
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
import trio
|
||||||
|
# from trio_typing import TaskStatus
|
||||||
|
|
||||||
from ._graphics._lines import LevelLine, position_line
|
from ._graphics._lines import LevelLine, position_line
|
||||||
from ._editors import LineEditor, ArrowEditor, _order_lines
|
from ._editors import LineEditor, ArrowEditor, _order_lines
|
||||||
|
@ -108,6 +109,10 @@ class OrderMode:
|
||||||
"""Set execution mode.
|
"""Set execution mode.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# not initialized yet
|
||||||
|
if not self.chart._cursor:
|
||||||
|
return
|
||||||
|
|
||||||
self._action = action
|
self._action = action
|
||||||
self.lines.stage_line(
|
self.lines.stage_line(
|
||||||
|
|
||||||
|
@ -306,10 +311,14 @@ async def open_order_mode(
|
||||||
|
|
||||||
|
|
||||||
async def start_order_mode(
|
async def start_order_mode(
|
||||||
|
|
||||||
chart: 'ChartPlotWidget', # noqa
|
chart: 'ChartPlotWidget', # noqa
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
brokername: str,
|
brokername: str,
|
||||||
|
|
||||||
|
# task_status: TaskStatus[trio.Event] = trio.TASK_STATUS_IGNORED,
|
||||||
|
started: trio.Event,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
'''Activate chart-trader order mode loop:
|
'''Activate chart-trader order mode loop:
|
||||||
- connect to emsd
|
- connect to emsd
|
||||||
|
@ -322,7 +331,11 @@ async def start_order_mode(
|
||||||
# spawn EMS actor-service
|
# spawn EMS actor-service
|
||||||
async with (
|
async with (
|
||||||
open_ems(brokername, symbol) as (book, trades_stream, positions),
|
open_ems(brokername, symbol) as (book, trades_stream, positions),
|
||||||
open_order_mode(symbol, chart, book) as order_mode
|
open_order_mode(symbol, chart, book) as order_mode,
|
||||||
|
|
||||||
|
# # start async input handling for chart's view
|
||||||
|
# # await godwidget._task_stack.enter_async_context(
|
||||||
|
# chart._vb.open_async_input_handler(),
|
||||||
):
|
):
|
||||||
|
|
||||||
# update any exising positions
|
# update any exising positions
|
||||||
|
@ -345,83 +358,90 @@ async def start_order_mode(
|
||||||
# Begin order-response streaming
|
# Begin order-response streaming
|
||||||
done()
|
done()
|
||||||
|
|
||||||
# this is where we receive **back** messages
|
# start async input handling for chart's view
|
||||||
# about executions **from** the EMS actor
|
async with chart._vb.open_async_input_handler():
|
||||||
async for msg in trades_stream:
|
|
||||||
|
|
||||||
fmsg = pformat(msg)
|
# signal to top level symbol loading task we're ready
|
||||||
log.info(f'Received order msg:\n{fmsg}')
|
# to handle input since the ems connection is ready
|
||||||
|
started.set()
|
||||||
|
|
||||||
name = msg['name']
|
# this is where we receive **back** messages
|
||||||
if name in (
|
# about executions **from** the EMS actor
|
||||||
'position',
|
async for msg in trades_stream:
|
||||||
):
|
|
||||||
# show line label once order is live
|
|
||||||
order_mode.on_position_update(msg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
resp = msg['resp']
|
fmsg = pformat(msg)
|
||||||
oid = msg['oid']
|
log.info(f'Received order msg:\n{fmsg}')
|
||||||
|
|
||||||
# response to 'action' request (buy/sell)
|
name = msg['name']
|
||||||
if resp in (
|
if name in (
|
||||||
'dark_submitted',
|
'position',
|
||||||
'broker_submitted'
|
):
|
||||||
):
|
# show line label once order is live
|
||||||
|
order_mode.on_position_update(msg)
|
||||||
# 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',
|
|
||||||
'broker_inactive',
|
|
||||||
'dark_cancelled'
|
|
||||||
):
|
|
||||||
# delete level line from view
|
|
||||||
order_mode.on_cancel(oid)
|
|
||||||
|
|
||||||
elif resp in (
|
|
||||||
'dark_triggered'
|
|
||||||
):
|
|
||||||
log.info(f'Dark order triggered for {fmsg}')
|
|
||||||
|
|
||||||
elif resp in (
|
|
||||||
'alert_triggered'
|
|
||||||
):
|
|
||||||
# should only be one "fill" for an alert
|
|
||||||
# add a triangle and remove the level line
|
|
||||||
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',):
|
|
||||||
|
|
||||||
known_order = book._sent_orders.get(oid)
|
|
||||||
if not known_order:
|
|
||||||
log.warning(f'order {oid} is unknown')
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
action = known_order.action
|
resp = msg['resp']
|
||||||
details = msg['brokerd_msg']
|
oid = msg['oid']
|
||||||
|
|
||||||
# TODO: some kinda progress system
|
# response to 'action' request (buy/sell)
|
||||||
order_mode.on_fill(
|
if resp in (
|
||||||
oid,
|
'dark_submitted',
|
||||||
price=details['price'],
|
'broker_submitted'
|
||||||
pointing='up' if action == 'buy' else 'down',
|
):
|
||||||
|
|
||||||
# TODO: put the actual exchange timestamp
|
# show line label once order is live
|
||||||
arrow_index=get_index(details['broker_time']),
|
order_mode.on_submit(oid)
|
||||||
)
|
|
||||||
|
# resp to 'cancel' request or error condition
|
||||||
|
# for action request
|
||||||
|
elif resp in (
|
||||||
|
'broker_cancelled',
|
||||||
|
'broker_inactive',
|
||||||
|
'dark_cancelled'
|
||||||
|
):
|
||||||
|
# delete level line from view
|
||||||
|
order_mode.on_cancel(oid)
|
||||||
|
|
||||||
|
elif resp in (
|
||||||
|
'dark_triggered'
|
||||||
|
):
|
||||||
|
log.info(f'Dark order triggered for {fmsg}')
|
||||||
|
|
||||||
|
elif resp in (
|
||||||
|
'alert_triggered'
|
||||||
|
):
|
||||||
|
# should only be one "fill" for an alert
|
||||||
|
# add a triangle and remove the level line
|
||||||
|
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',):
|
||||||
|
|
||||||
|
known_order = book._sent_orders.get(oid)
|
||||||
|
if not known_order:
|
||||||
|
log.warning(f'order {oid} is unknown')
|
||||||
|
continue
|
||||||
|
|
||||||
|
action = known_order.action
|
||||||
|
details = msg['brokerd_msg']
|
||||||
|
|
||||||
|
# TODO: some kinda progress system
|
||||||
|
order_mode.on_fill(
|
||||||
|
oid,
|
||||||
|
price=details['price'],
|
||||||
|
pointing='up' if action == 'buy' else 'down',
|
||||||
|
|
||||||
|
# TODO: put the actual exchange timestamp
|
||||||
|
arrow_index=get_index(details['broker_time']),
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue