Port to order pane apis

fsp_feeds
Tyler Goodlet 2021-08-23 14:48:20 -04:00
parent bc2f4186fd
commit 206af0d575
3 changed files with 93 additions and 70 deletions

View File

@ -44,6 +44,7 @@ import pydantic
from ._event import open_handlers from ._event import open_handlers
from ._style import hcolor, _font, _font_small, DpiAwareFont from ._style import hcolor, _font, _font_small, DpiAwareFont
from .. import brokers
class FontAndChartAwareLineEdit(QLineEdit): class FontAndChartAwareLineEdit(QLineEdit):
@ -366,11 +367,15 @@ async def handle_field_input(
# process field input # process field input
if key in (Qt.Key_Enter, Qt.Key_Return): if key in (Qt.Key_Enter, Qt.Key_Return):
value = widget.text() value = widget.text()
key = widget._key key = widget._key
old = getattr(model, key) old = getattr(model, key)
try: try:
setattr(model, key, value) setattr(model, key, value)
except pydantic.error_wrappers.ValidationError: except pydantic.error_wrappers.ValidationError:
setattr(model, key, old) setattr(model, key, old)
widget.setText(str(old)) widget.setText(str(old))
@ -380,16 +385,14 @@ async def handle_field_input(
def mk_form( def mk_form(
model: pydantic.BaseModel,
parent: QWidget, parent: QWidget,
fields_schema: dict, fields_schema: dict,
) -> FieldsForm: ) -> FieldsForm:
form = FieldsForm(parent=parent)
# TODO: generate components from model # TODO: generate components from model
# instead of schema dict (aka use an ORM) # instead of schema dict (aka use an ORM)
form.model = model form = FieldsForm(parent=parent)
# generate sub-components from schema dict # generate sub-components from schema dict
for key, config in fields_schema.items(): for key, config in fields_schema.items():
@ -413,18 +416,6 @@ def mk_form(
values values
) )
def write_model(text: str, key: str):
print(f'{text}')
setattr(form.model, key, text)
print(form.model)
w.currentTextChanged.connect(
partial(
write_model,
key=key,
)
)
w._key = key w._key = key
return form return form
@ -438,7 +429,7 @@ async def open_form_input_handling(
) -> FieldsForm: ) -> FieldsForm:
assert form.model, f'{form} must define a `.model`' # assert form.model, f'{form} must define a `.model`'
async with open_handlers( async with open_handlers(
@ -630,31 +621,27 @@ def mk_fill_status_bar(
slots = 4 slots = 4
bar.set_slots(slots, value=0) bar.set_slots(slots, value=0)
return hbox, bar return hbox, bar, left_label, top_label, bottom_label
def mk_order_pane_layout( def mk_order_pane_layout(
parent: QWidget, parent: QWidget,
# accounts: dict[str, Optional[str]],
font_size: int = _font_small.px_size - 2 font_size: int = _font_small.px_size - 2
) -> FieldsForm: ) -> FieldsForm:
from ._position import mk_alloc accounts = brokers.config.load_accounts()
# TODO: some kinda pydantic sub-type
# that enforces a composite widget attr er sumthin..
# as part of our ORM thingers.
alloc = mk_alloc()
# TODO: maybe just allocate the whole fields form here # TODO: maybe just allocate the whole fields form here
# and expect an async ctx entry? # and expect an async ctx entry?
form = mk_form( form = mk_form(
parent=parent, parent=parent,
model=alloc,
fields_schema={ fields_schema={
'account': { 'account': {
'type': 'select', 'type': 'select',
'default_value': alloc._accounts.keys() 'default_value': accounts.keys(),
}, },
'size_unit': { 'size_unit': {
'label': '**allocate**:', 'label': '**allocate**:',
@ -670,8 +657,8 @@ def mk_order_pane_layout(
'type': 'select', 'type': 'select',
'default_value': ['uniform'], 'default_value': ['uniform'],
}, },
'size': { 'limit': {
'label': '**size**:', 'label': '**limit**:',
'type': 'edit', 'type': 'edit',
'default_value': 5000, 'default_value': 5000,
}, },
@ -683,7 +670,6 @@ def mk_order_pane_layout(
}, },
) )
form._font_size = font_size form._font_size = font_size
alloc._widget = form
# top level pane layout # top level pane layout
# XXX: see ``FieldsForm.__init__()`` for why we can't do basic # XXX: see ``FieldsForm.__init__()`` for why we can't do basic
@ -693,8 +679,15 @@ def mk_order_pane_layout(
# _, h = form.width(), form.height() # _, h = form.width(), form.height()
# print(f'w, h: {w, h}') # print(f'w, h: {w, h}')
hbox, fill_bar = mk_fill_status_bar(form, pane_vbox=vbox) hbox, fill_bar, left_label, bottom_label, top_label = mk_fill_status_bar(
form, pane_vbox=vbox
)
# TODO: would be nice to have some better way of reffing these over
# monkey patching...
form.fill_bar = fill_bar form.fill_bar = fill_bar
form.left_label = left_label
form.bottom_label = bottom_label
form.top_label = top_label
# add pp fill bar + spacing # add pp fill bar + spacing
vbox.addLayout(hbox, stretch=1/3) vbox.addLayout(hbox, stretch=1/3)
@ -702,9 +695,9 @@ def mk_order_pane_layout(
feed_label = form.add_field_label( feed_label = form.add_field_label(
dedent(""" dedent("""
brokerd.ib\n brokerd.ib\n
|_@localhost:8509\n |_@{host}:{port}\n
|_consumers: 4\n |_consumers: {cons}\n
|_streams: 9\n |_streams: {streams}\n
"""), """),
font_size=_font.px_size - 5, font_size=_font.px_size - 5,
) )
@ -722,5 +715,4 @@ def mk_order_pane_layout(
vbox.setSpacing(36) vbox.setSpacing(36)
form.show() form.show()
return form return form

View File

@ -261,14 +261,16 @@ async def handle_viewmode_kb_inputs(
): ):
# hot key to set order slots size # hot key to set order slots size
num = int(text) num = int(text)
pp = order_mode.pp pp_pane = order_mode.pane
pp_pane = pp.pane pp_pane.alloc.slots = num
pp_pane.model.slots = num
edit = pp_pane.fields['slots'] edit = pp_pane.form.fields['slots']
edit.setText(text) edit.setText(text)
edit.selectAll() edit.selectAll()
on_next_release = edit.deselect on_next_release = edit.deselect
pp.init_status_ui()
pp_pane.init_status_ui()
else: # none active else: # none active

View File

@ -19,6 +19,7 @@ Chart trading, the only way to scalp.
""" """
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import partial
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
@ -28,15 +29,16 @@ from pydantic import BaseModel
import tractor import tractor
import trio import trio
from .. import brokers
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 ._editors import LineEditor, ArrowEditor
from ._lines import order_line, LevelLine from ._lines import order_line, LevelLine
from ._position import PositionTracker from ._position import PositionTracker, OrderModePane, mk_alloc
from ._window import MultiStatus from ._window import MultiStatus
from ..clearing._messages import Order from ..clearing._messages import Order
# from ._forms import FieldsForm from ._forms import open_form_input_handling
log = get_logger(__name__) log = get_logger(__name__)
@ -88,6 +90,8 @@ class OrderMode:
multistatus: MultiStatus multistatus: MultiStatus
pp: PositionTracker pp: PositionTracker
allocator: 'Allocator' # noqa allocator: 'Allocator' # noqa
pane: OrderModePane
active: bool = False active: bool = False
name: str = 'order' name: str = 'order'
@ -127,31 +131,16 @@ class OrderMode:
) else False, ) else False,
**line_kwargs, **line_kwargs,
) )
# define a callback applied for each level change to the line # set level update callback to order pane method and update once
# which will recompute the order size based on allocator # immediately
# settings: line._on_level_change = partial(
def update_from_size_calc(y: float) -> None: self.pane.on_level_change_update_next_order_info,
line=line,
order_info = self.allocator.get_order_info( order=order,
symbol=symbol,
price=y,
action=order.action,
) )
line.update_labels(order_info) line._on_level_change(order.price)
order.price = y
order.size = order_info['size']
# return is used to update labels implicitly
return order_info
update_from_size_calc(order.price)
# set level update cb
# line._on_level_change = partial(update_from_size_calc, order=order)
line._on_level_change = update_from_size_calc
return line return line
@ -493,16 +482,52 @@ async def run_order_mode(
), ),
): ):
log.info(f'Opening order mode for {brokername}.{symbol.key}')
view = chart.view view = chart.view
# annotations editors
lines = LineEditor(chart=chart) lines = LineEditor(chart=chart)
arrows = ArrowEditor(chart, {}) arrows = ArrowEditor(chart, {})
log.info("Opening order mode") # allocator
alloc = mk_alloc(
symbol=symbol,
accounts=brokers.config.load_accounts()
)
# form = chart.linked.godwidget.pp_pane.model
form = chart.sidepane
form.model = alloc
alloc = chart.linked.godwidget.pp_pane.model
pp_tracker = PositionTracker(chart, alloc=alloc) pp_tracker = PositionTracker(chart, alloc=alloc)
pp_tracker.hide() pp_tracker.hide()
alloc._position = pp_tracker
# order pane widgets and allocation model
order_pane = OrderModePane(
tracker=pp_tracker,
form=form,
alloc=alloc,
fill_bar=form.fill_bar,
pnl_label=form.left_label,
step_label=form.bottom_label,
limit_label=form.top_label,
)
for key in ('account', 'size_unit', 'disti_weight'):
w = form.fields[key]
def write_model(text: str, key: str):
print(f'{text}')
setattr(alloc, key, text)
print(alloc)
w.currentTextChanged.connect(
partial(
write_model,
key=key,
)
)
mode = OrderMode( mode = OrderMode(
chart, chart,
@ -512,6 +537,7 @@ async def run_order_mode(
multistatus, multistatus,
pp_tracker, pp_tracker,
allocator=alloc, allocator=alloc,
pane=order_pane,
) )
# TODO: create a mode "manager" of sorts? # TODO: create a mode "manager" of sorts?
@ -529,7 +555,6 @@ async def run_order_mode(
pp_tracker.update(msg, position=pp_tracker.startup_pp) pp_tracker.update(msg, position=pp_tracker.startup_pp)
pp_tracker.update(msg) pp_tracker.update(msg)
# default entry sizing # default entry sizing
if asset_type in ('stock', 'crypto', 'forex'): if asset_type in ('stock', 'crypto', 'forex'):
alloc.size_unit = '$ size' alloc.size_unit = '$ size'
@ -544,11 +569,11 @@ async def run_order_mode(
pp_tracker.pane.fields['slots'].setText(str(alloc.slots)) pp_tracker.pane.fields['slots'].setText(str(alloc.slots))
# make entry step 1.0 # make entry step 1.0
alloc.size = slots alloc.units_size = slots
pp_tracker.pane.fields['size'].setText(str(alloc.size)) pp_tracker.pane.fields['size'].setText(str(alloc.units_size))
# make fill bar and positioning snapshot # make fill bar and positioning snapshot
pp_tracker.init_status_ui() order_pane.init_status_ui()
# TODO: this should go onto some sort of # TODO: this should go onto some sort of
# data-view strimg thinger..right? # data-view strimg thinger..right?
@ -572,7 +597,11 @@ async def run_order_mode(
async with ( async with (
chart.view.open_async_input_handler(), chart.view.open_async_input_handler(),
# TODO: config form handler nursery # pp pane kb inputs
open_form_input_handling(
form,
focus_next=chart.linked.godwidget,
),
): ):