Get roundtrip alert uuids workin; stage order book api
parent
d0a3deae09
commit
24536ad769
|
@ -23,8 +23,9 @@ from typing import (
|
||||||
AsyncIterator, List, Dict, Callable, Tuple,
|
AsyncIterator, List, Dict, Callable, Tuple,
|
||||||
Any,
|
Any,
|
||||||
)
|
)
|
||||||
import uuid
|
# import uuid
|
||||||
|
|
||||||
|
import pyqtgraph as pg
|
||||||
import trio
|
import trio
|
||||||
from trio_typing import TaskStatus
|
from trio_typing import TaskStatus
|
||||||
import tractor
|
import tractor
|
||||||
|
@ -32,12 +33,14 @@ import tractor
|
||||||
from . import data
|
from . import data
|
||||||
from .log import get_logger
|
from .log import get_logger
|
||||||
from .data._source import Symbol
|
from .data._source import Symbol
|
||||||
|
from .ui._style import hcolor
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
_to_router: trio.abc.SendChannel = None
|
_to_router: trio.abc.SendChannel = None
|
||||||
_from_ui: trio.abc.ReceiveChannel = None
|
_from_ui: trio.abc.ReceiveChannel = None
|
||||||
|
_lines = {}
|
||||||
|
|
||||||
|
|
||||||
_local_book = {}
|
_local_book = {}
|
||||||
|
@ -54,6 +57,21 @@ class OrderBook:
|
||||||
orders: Dict[str, dict] = field(default_factory=dict)
|
orders: Dict[str, dict] = field(default_factory=dict)
|
||||||
_cmds_from_ui: trio.abc.ReceiveChannel = _from_ui
|
_cmds_from_ui: trio.abc.ReceiveChannel = _from_ui
|
||||||
|
|
||||||
|
async def alert(self, price: float) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def buy(self, price: float) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def sell(self, price: float) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def modify(self, oid: str, price) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def cancel(self, oid: str) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
_orders: OrderBook = None
|
_orders: OrderBook = None
|
||||||
|
|
||||||
|
@ -86,8 +104,11 @@ async def send_order_cmds():
|
||||||
symbol = lc.symbol
|
symbol = lc.symbol
|
||||||
tp = order['type']
|
tp = order['type']
|
||||||
price = order['price']
|
price = order['price']
|
||||||
|
oid = order['oid']
|
||||||
|
|
||||||
oid = str(uuid.uuid4())
|
print(f'oid: {oid}')
|
||||||
|
# TODO
|
||||||
|
# oid = str(uuid.uuid4())
|
||||||
|
|
||||||
cmd = {
|
cmd = {
|
||||||
'price': price,
|
'price': price,
|
||||||
|
@ -134,7 +155,7 @@ def mk_check(trigger_price, known_last) -> Callable[[float, float], bool]:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return check_gt
|
return check_gt, 'gt'
|
||||||
|
|
||||||
elif trigger_price <= known_last:
|
elif trigger_price <= known_last:
|
||||||
|
|
||||||
|
@ -144,7 +165,7 @@ def mk_check(trigger_price, known_last) -> Callable[[float, float], bool]:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return check_lt
|
return check_lt, 'lt'
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -233,17 +254,23 @@ async def exec_orders(
|
||||||
if not execs:
|
if not execs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for pred, action in tuple(execs):
|
for oid, pred, action in tuple(execs):
|
||||||
# push trigger msg back to parent as an "alert"
|
# push trigger msg back to parent as an "alert"
|
||||||
# (mocking for eg. a "fill")
|
# (mocking for eg. a "fill")
|
||||||
if pred(price):
|
if pred(price):
|
||||||
res = action(price)
|
name = action(price)
|
||||||
await ctx.send_yield({
|
await ctx.send_yield({
|
||||||
'type': 'alert',
|
'type': 'alert',
|
||||||
'price': price,
|
'price': price,
|
||||||
|
# current shm array index
|
||||||
|
'index': feed.shm._last.value - 1,
|
||||||
|
'name': name,
|
||||||
|
'oid': oid,
|
||||||
})
|
})
|
||||||
execs.remove((pred, action))
|
execs.remove((oid, pred, action))
|
||||||
print(f"GOT ALERT FOR {exec_price} @ \n{tick}")
|
print(
|
||||||
|
f"GOT ALERT FOR {exec_price} @ \n{tick}\n")
|
||||||
|
print(f'execs are {execs}')
|
||||||
|
|
||||||
# feed teardown
|
# feed teardown
|
||||||
|
|
||||||
|
@ -264,10 +291,17 @@ async def stream_and_route(ctx, ui_name):
|
||||||
|
|
||||||
async for cmd in await portal.run(send_order_cmds):
|
async for cmd in await portal.run(send_order_cmds):
|
||||||
|
|
||||||
|
action = cmd.pop('action')
|
||||||
|
|
||||||
|
if action == 'cancel':
|
||||||
|
pass
|
||||||
|
|
||||||
tp = cmd.pop('type')
|
tp = cmd.pop('type')
|
||||||
|
|
||||||
trigger_price = cmd['price']
|
trigger_price = cmd['price']
|
||||||
sym = cmd['symbol']
|
sym = cmd['symbol']
|
||||||
brokers = cmd['brokers']
|
brokers = cmd['brokers']
|
||||||
|
oid = cmd['oid']
|
||||||
|
|
||||||
if tp == 'alert':
|
if tp == 'alert':
|
||||||
log.info(f'Alert {cmd} received in {actor.uid}')
|
log.info(f'Alert {cmd} received in {actor.uid}')
|
||||||
|
@ -295,18 +329,20 @@ async def stream_and_route(ctx, ui_name):
|
||||||
# should be based on the current first price received from the
|
# should be based on the current first price received from the
|
||||||
# feed, instead of being like every other shitty tina platform
|
# feed, instead of being like every other shitty tina platform
|
||||||
# that makes the user choose the predicate operator.
|
# that makes the user choose the predicate operator.
|
||||||
pred = mk_check(trigger_price, last)
|
pred, name = mk_check(trigger_price, last)
|
||||||
|
|
||||||
# create list of executions on first entry
|
# create list of executions on first entry
|
||||||
book.orders.setdefault((broker, sym), []).append(
|
book.orders.setdefault((broker, sym), []).append(
|
||||||
(pred, lambda p: p)
|
(oid, pred, lambda p: name)
|
||||||
)
|
)
|
||||||
|
|
||||||
# continue and wait on next order cmd
|
# continue and wait on next order cmd
|
||||||
|
|
||||||
|
|
||||||
async def spawn_router_stream_alerts(
|
async def spawn_router_stream_alerts(
|
||||||
|
chart,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
# lines: 'LinesEditor',
|
||||||
task_status: TaskStatus[str] = trio.TASK_STATUS_IGNORED,
|
task_status: TaskStatus[str] = trio.TASK_STATUS_IGNORED,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Spawn an EMS daemon and begin sending orders and receiving
|
"""Spawn an EMS daemon and begin sending orders and receiving
|
||||||
|
@ -314,7 +350,7 @@ async def spawn_router_stream_alerts(
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# setup local ui event streaming channels
|
# setup local ui event streaming channels
|
||||||
global _from_ui, _to_router
|
global _from_ui, _to_router, _lines
|
||||||
_to_router, _from_ui = trio.open_memory_channel(100)
|
_to_router, _from_ui = trio.open_memory_channel(100)
|
||||||
|
|
||||||
actor = tractor.current_actor()
|
actor = tractor.current_actor()
|
||||||
|
@ -337,6 +373,27 @@ async def spawn_router_stream_alerts(
|
||||||
|
|
||||||
async for alert in stream:
|
async for alert in stream:
|
||||||
|
|
||||||
|
yb = pg.mkBrush(hcolor('alert_yellow'))
|
||||||
|
|
||||||
|
angle = 90 if alert['name'] == 'lt' else -90
|
||||||
|
|
||||||
|
arrow = pg.ArrowItem(
|
||||||
|
angle=angle,
|
||||||
|
baseAngle=0,
|
||||||
|
headLen=5,
|
||||||
|
headWidth=2,
|
||||||
|
tailLen=None,
|
||||||
|
brush=yb,
|
||||||
|
)
|
||||||
|
arrow.setPos(alert['index'], alert['price'])
|
||||||
|
chart.plotItem.addItem(arrow)
|
||||||
|
|
||||||
|
# delete the line from view
|
||||||
|
oid = alert['oid']
|
||||||
|
print(f'_lines: {_lines}')
|
||||||
|
print(f'deleting line with oid: {oid}')
|
||||||
|
_lines.pop(oid).delete()
|
||||||
|
|
||||||
# TODO: this in another task?
|
# TODO: this in another task?
|
||||||
# not sure if this will ever be a bottleneck,
|
# not sure if this will ever be a bottleneck,
|
||||||
# we probably could do graphics stuff first tho?
|
# we probably could do graphics stuff first tho?
|
||||||
|
@ -345,10 +402,10 @@ async def spawn_router_stream_alerts(
|
||||||
result = await trio.run_process(
|
result = await trio.run_process(
|
||||||
[
|
[
|
||||||
'notify-send',
|
'notify-send',
|
||||||
'piker',
|
|
||||||
f'Alert: {alert}',
|
|
||||||
'-u', 'normal',
|
'-u', 'normal',
|
||||||
'-t', '10000',
|
'-t', '10000',
|
||||||
|
'piker',
|
||||||
|
f'alert: {alert}',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
log.runtime(result)
|
log.runtime(result)
|
||||||
|
|
|
@ -937,6 +937,7 @@ async def _async_main(
|
||||||
# spawn EMS actor-service
|
# spawn EMS actor-service
|
||||||
router_send_chan = await n.start(
|
router_send_chan = await n.start(
|
||||||
spawn_router_stream_alerts,
|
spawn_router_stream_alerts,
|
||||||
|
chart,
|
||||||
symbol,
|
symbol,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
"""
|
"""
|
||||||
UX interaction customs.
|
UX interaction customs.
|
||||||
"""
|
"""
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
import uuid
|
||||||
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph import ViewBox, Point, QtCore, QtGui
|
from pyqtgraph import ViewBox, Point, QtCore, QtGui
|
||||||
|
@ -27,6 +29,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
|
from ._graphics._lines import level_line
|
||||||
|
from .._ems import _lines
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
@ -195,13 +198,30 @@ class SelectRect(QtGui.QGraphicsRectItem):
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinesEditor:
|
||||||
|
view: 'ChartView'
|
||||||
|
chart: 'ChartPlotWidget'
|
||||||
|
active_line: 'LevelLine'
|
||||||
|
|
||||||
|
def stage_line(self) -> 'LevelLine':
|
||||||
|
...
|
||||||
|
|
||||||
|
def commit_line(self) -> 'LevelLine':
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_line(self, line) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class ChartView(ViewBox):
|
class ChartView(ViewBox):
|
||||||
"""Price chart view box with interaction behaviors you'd expect from
|
"""Price chart view box with interaction behaviors you'd expect from
|
||||||
any interactive platform:
|
any interactive platform:
|
||||||
|
|
||||||
- zoom on mouse scroll that auto fits y-axis
|
- zoom on mouse scroll that auto fits y-axis
|
||||||
- no vertical scrolling
|
- vertical scrolling on y-axis
|
||||||
- zoom to a "fixed point" on the y-axis
|
- zoom on x to most recent in view datum
|
||||||
|
- zoom on right-click-n-drag to cursor position
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -306,9 +326,7 @@ class ChartView(ViewBox):
|
||||||
# Scale or translate based on mouse button
|
# Scale or translate based on mouse button
|
||||||
if button & (QtCore.Qt.LeftButton | QtCore.Qt.MidButton):
|
if button & (QtCore.Qt.LeftButton | QtCore.Qt.MidButton):
|
||||||
|
|
||||||
# print(f'left click drag pos {pos}')
|
# zoom y-axis ONLY when click-n-drag on it
|
||||||
|
|
||||||
# zoom only y-axis when click-n-drag on it
|
|
||||||
if axis == 1:
|
if axis == 1:
|
||||||
# set a static y range special value on chart widget to
|
# set a static y range special value on chart widget to
|
||||||
# prevent sizing to data in view.
|
# prevent sizing to data in view.
|
||||||
|
@ -407,11 +425,16 @@ class ChartView(ViewBox):
|
||||||
|
|
||||||
# XXX: should make this an explicit attr
|
# XXX: should make this an explicit attr
|
||||||
# it's assigned inside ``.add_plot()``
|
# it's assigned inside ``.add_plot()``
|
||||||
self.linked_charts._to_router.send_nowait({
|
lc = self.linked_charts
|
||||||
'symbol': chart.name,
|
oid = str(uuid.uuid4())
|
||||||
'brokers': ['kraken'],
|
lc._to_router.send_nowait({
|
||||||
|
'chart': lc,
|
||||||
'type': 'alert',
|
'type': 'alert',
|
||||||
'price': y,
|
'price': y,
|
||||||
|
'oid': oid,
|
||||||
|
# 'symbol': lc.chart.name,
|
||||||
|
# 'brokers': lc.symbol.brokers,
|
||||||
|
# 'price': y,
|
||||||
})
|
})
|
||||||
|
|
||||||
line = level_line(
|
line = level_line(
|
||||||
|
@ -419,6 +442,7 @@ class ChartView(ViewBox):
|
||||||
level=y,
|
level=y,
|
||||||
color='alert_yellow',
|
color='alert_yellow',
|
||||||
)
|
)
|
||||||
|
_lines[oid] = line
|
||||||
log.info(f'clicked {pos}')
|
log.info(f'clicked {pos}')
|
||||||
|
|
||||||
def keyReleaseEvent(self, ev):
|
def keyReleaseEvent(self, ev):
|
||||||
|
|
Loading…
Reference in New Issue