Drop the open ctx mng; add wip pp label
parent
2d42da6f1a
commit
34a773821e
|
@ -65,7 +65,7 @@ from .. import data
|
||||||
from ..log import get_logger
|
from ..log import get_logger
|
||||||
from ._exec import run_qtractor
|
from ._exec import run_qtractor
|
||||||
from ._interaction import ChartView
|
from ._interaction import ChartView
|
||||||
from .order_mode import start_order_mode
|
from .order_mode import run_order_mode
|
||||||
from .. import fsp
|
from .. import fsp
|
||||||
from ..data import feed
|
from ..data import feed
|
||||||
|
|
||||||
|
@ -1574,7 +1574,7 @@ async def display_symbol_data(
|
||||||
linkedsplits
|
linkedsplits
|
||||||
)
|
)
|
||||||
|
|
||||||
await start_order_mode(chart, symbol, provider, order_mode_started)
|
await run_order_mode(chart, symbol, provider, order_mode_started)
|
||||||
|
|
||||||
|
|
||||||
async def load_provider_search(
|
async def load_provider_search(
|
||||||
|
|
|
@ -18,23 +18,24 @@
|
||||||
Chart trading, the only way to scalp.
|
Chart trading, the only way to scalp.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from contextlib import asynccontextmanager
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Dict, Callable, Any
|
from typing import Optional, Dict, Callable, Any
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import pyqtgraph as pg
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
import tractor
|
||||||
import trio
|
import trio
|
||||||
|
|
||||||
from ._lines import LevelLine, position_line
|
from ._anchors import marker_right_points
|
||||||
from ._editors import LineEditor, ArrowEditor
|
|
||||||
from ._window import MultiStatus, main_window
|
|
||||||
from ..clearing._client import open_ems, OrderBook
|
from ..clearing._client import open_ems, OrderBook
|
||||||
from ..data._source import Symbol
|
from ..data._source import Symbol
|
||||||
from ..log import get_logger
|
from ..log import get_logger
|
||||||
|
from ._editors import LineEditor, ArrowEditor
|
||||||
|
from ._label import Label
|
||||||
|
from ._lines import LevelLine, position_line
|
||||||
|
from ._window import MultiStatus, main_window
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
@ -91,6 +92,10 @@ class OrderMode:
|
||||||
lines: LineEditor
|
lines: LineEditor
|
||||||
arrows: ArrowEditor
|
arrows: ArrowEditor
|
||||||
status_bar: MultiStatus
|
status_bar: MultiStatus
|
||||||
|
|
||||||
|
# pp status info
|
||||||
|
label: Label
|
||||||
|
|
||||||
name: str = 'order'
|
name: str = 'order'
|
||||||
|
|
||||||
_colors = {
|
_colors = {
|
||||||
|
@ -381,50 +386,11 @@ class OrderMode:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
async def run_order_mode(
|
||||||
async def open_order_mode(
|
|
||||||
symbol: Symbol,
|
|
||||||
chart: pg.PlotWidget,
|
|
||||||
book: OrderBook,
|
|
||||||
):
|
|
||||||
status_bar: MultiStatus = main_window().status_bar
|
|
||||||
view = chart._vb
|
|
||||||
lines = LineEditor(chart=chart)
|
|
||||||
arrows = ArrowEditor(chart, {})
|
|
||||||
|
|
||||||
log.info("Opening order mode")
|
|
||||||
|
|
||||||
mode = OrderMode(chart, book, lines, arrows, status_bar)
|
|
||||||
view.mode = mode
|
|
||||||
|
|
||||||
asset_type = symbol.type_key
|
|
||||||
|
|
||||||
if asset_type == 'stock':
|
|
||||||
mode._size = 100.0
|
|
||||||
|
|
||||||
elif asset_type in ('future', 'option', 'futures_option'):
|
|
||||||
mode._size = 1.0
|
|
||||||
|
|
||||||
else: # to be safe
|
|
||||||
mode._size = 1.0
|
|
||||||
|
|
||||||
try:
|
|
||||||
yield mode
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# XXX special teardown handling like for ex.
|
|
||||||
# - cancelling orders if needed?
|
|
||||||
# - closing positions if desired?
|
|
||||||
# - switching special condition orders to safer/more reliable variants
|
|
||||||
log.info("Closing order mode")
|
|
||||||
|
|
||||||
|
|
||||||
async def start_order_mode(
|
|
||||||
|
|
||||||
chart: 'ChartPlotWidget', # noqa
|
chart: 'ChartPlotWidget', # noqa
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
brokername: str,
|
brokername: str,
|
||||||
|
|
||||||
started: trio.Event,
|
started: trio.Event,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -436,19 +402,90 @@ async def start_order_mode(
|
||||||
'''
|
'''
|
||||||
done = chart.window().status_bar.open_status('starting order mode..')
|
done = chart.window().status_bar.open_status('starting order mode..')
|
||||||
|
|
||||||
|
book: OrderBook
|
||||||
|
trades_stream: tractor.MsgStream
|
||||||
|
positions: dict
|
||||||
|
|
||||||
# spawn EMS actor-service
|
# spawn EMS actor-service
|
||||||
async with (
|
async with (
|
||||||
open_ems(brokername, symbol) as (book, trades_stream, positions),
|
|
||||||
open_order_mode(symbol, chart, book) as order_mode,
|
open_ems(brokername, symbol) as (
|
||||||
|
book,
|
||||||
|
trades_stream,
|
||||||
|
positions
|
||||||
|
),
|
||||||
|
|
||||||
# # start async input handling for chart's view
|
# # start async input handling for chart's view
|
||||||
# # await godwidget._task_stack.enter_async_context(
|
# # await godwidget._task_stack.enter_async_context(
|
||||||
# chart._vb.open_async_input_handler(),
|
# chart._vb.open_async_input_handler(),
|
||||||
):
|
):
|
||||||
|
status_bar: MultiStatus = main_window().status_bar
|
||||||
|
view = chart._vb
|
||||||
|
lines = LineEditor(chart=chart)
|
||||||
|
arrows = ArrowEditor(chart, {})
|
||||||
|
|
||||||
|
log.info("Opening order mode")
|
||||||
|
|
||||||
|
pp_label = Label(
|
||||||
|
view=view,
|
||||||
|
color='default_light',
|
||||||
|
|
||||||
|
# this is "static" label
|
||||||
|
# update_on_range_change=False,
|
||||||
|
|
||||||
|
fmt_str='\n'.join((
|
||||||
|
'size: {entry_count}',
|
||||||
|
'% port: {percent_of_port}',
|
||||||
|
# '$val: {base_unit_value}',
|
||||||
|
)),
|
||||||
|
fields={
|
||||||
|
'entry_count': 0,
|
||||||
|
'percent_of_port': 0,
|
||||||
|
'base_unit_value': 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
pp_label.render()
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QPointF
|
||||||
|
|
||||||
|
# order line endpoint anchor
|
||||||
|
def bottom_marker_right() -> QPointF:
|
||||||
|
|
||||||
|
return QPointF(
|
||||||
|
marker_right_points(chart)[0] - pp_label.w,
|
||||||
|
view.height() - pp_label.h,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: position on botto if l1/book is on top side
|
||||||
|
pp_label.scene_anchor = bottom_marker_right
|
||||||
|
pp_label.hide()
|
||||||
|
|
||||||
|
mode = OrderMode(
|
||||||
|
chart,
|
||||||
|
book,
|
||||||
|
lines,
|
||||||
|
arrows,
|
||||||
|
status_bar,
|
||||||
|
label=pp_label,
|
||||||
|
)
|
||||||
|
|
||||||
|
view.mode = mode
|
||||||
|
|
||||||
|
asset_type = symbol.type_key
|
||||||
|
|
||||||
|
# default entry sizing
|
||||||
|
if asset_type == 'stock':
|
||||||
|
mode._size = 100.0
|
||||||
|
|
||||||
|
elif asset_type in ('future', 'option', 'futures_option'):
|
||||||
|
mode._size = 1.0
|
||||||
|
|
||||||
|
else: # to be safe
|
||||||
|
mode._size = 1.0
|
||||||
|
|
||||||
# update any exising positions
|
# update any exising positions
|
||||||
for sym, msg in positions.items():
|
for sym, msg in positions.items():
|
||||||
order_mode.on_position_update(msg)
|
mode.on_position_update(msg)
|
||||||
|
|
||||||
def get_index(time: float):
|
def get_index(time: float):
|
||||||
|
|
||||||
|
@ -485,13 +522,13 @@ async def start_order_mode(
|
||||||
'position',
|
'position',
|
||||||
):
|
):
|
||||||
# show line label once order is live
|
# show line label once order is live
|
||||||
order_mode.on_position_update(msg)
|
mode.on_position_update(msg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
resp = msg['resp']
|
resp = msg['resp']
|
||||||
oid = msg['oid']
|
oid = msg['oid']
|
||||||
|
|
||||||
dialog = order_mode.dialogs[oid]
|
dialog = mode.dialogs[oid]
|
||||||
# record message to dialog tracking
|
# record message to dialog tracking
|
||||||
dialog.msgs[oid] = msg
|
dialog.msgs[oid] = msg
|
||||||
|
|
||||||
|
@ -502,7 +539,7 @@ async def start_order_mode(
|
||||||
):
|
):
|
||||||
|
|
||||||
# show line label once order is live
|
# show line label once order is live
|
||||||
order_mode.on_submit(oid)
|
mode.on_submit(oid)
|
||||||
|
|
||||||
# resp to 'cancel' request or error condition
|
# resp to 'cancel' request or error condition
|
||||||
# for action request
|
# for action request
|
||||||
|
@ -512,7 +549,7 @@ async def start_order_mode(
|
||||||
'dark_cancelled'
|
'dark_cancelled'
|
||||||
):
|
):
|
||||||
# delete level line from view
|
# delete level line from view
|
||||||
order_mode.on_cancel(oid)
|
mode.on_cancel(oid)
|
||||||
|
|
||||||
elif resp in (
|
elif resp in (
|
||||||
'dark_triggered'
|
'dark_triggered'
|
||||||
|
@ -524,23 +561,23 @@ async def start_order_mode(
|
||||||
):
|
):
|
||||||
# should only be one "fill" for an alert
|
# should only be one "fill" for an alert
|
||||||
# add a triangle and remove the level line
|
# add a triangle and remove the level line
|
||||||
order_mode.on_fill(
|
mode.on_fill(
|
||||||
oid,
|
oid,
|
||||||
price=msg['trigger_price'],
|
price=msg['trigger_price'],
|
||||||
arrow_index=get_index(time.time()),
|
arrow_index=get_index(time.time()),
|
||||||
)
|
)
|
||||||
order_mode.lines.remove_line(uuid=oid)
|
mode.lines.remove_line(uuid=oid)
|
||||||
await order_mode.on_exec(oid, msg)
|
await mode.on_exec(oid, msg)
|
||||||
|
|
||||||
# response to completed 'action' request for buy/sell
|
# response to completed 'action' request for buy/sell
|
||||||
elif resp in (
|
elif resp in (
|
||||||
'broker_executed',
|
'broker_executed',
|
||||||
):
|
):
|
||||||
# right now this is just triggering a system alert
|
# right now this is just triggering a system alert
|
||||||
await order_mode.on_exec(oid, msg)
|
await mode.on_exec(oid, msg)
|
||||||
|
|
||||||
if msg['brokerd_msg']['remaining'] == 0:
|
if msg['brokerd_msg']['remaining'] == 0:
|
||||||
order_mode.lines.remove_line(uuid=oid)
|
mode.lines.remove_line(uuid=oid)
|
||||||
|
|
||||||
# each clearing tick is responded individually
|
# each clearing tick is responded individually
|
||||||
elif resp in ('broker_filled',):
|
elif resp in ('broker_filled',):
|
||||||
|
@ -554,7 +591,7 @@ async def start_order_mode(
|
||||||
details = msg['brokerd_msg']
|
details = msg['brokerd_msg']
|
||||||
|
|
||||||
# TODO: some kinda progress system
|
# TODO: some kinda progress system
|
||||||
order_mode.on_fill(
|
mode.on_fill(
|
||||||
oid,
|
oid,
|
||||||
price=details['price'],
|
price=details['price'],
|
||||||
pointing='up' if action == 'buy' else 'down',
|
pointing='up' if action == 'buy' else 'down',
|
||||||
|
|
Loading…
Reference in New Issue