Port to order pane apis
parent
bc2f4186fd
commit
206af0d575
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
line._on_level_change(order.price)
|
||||||
action=order.action,
|
|
||||||
)
|
|
||||||
line.update_labels(order_info)
|
|
||||||
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,
|
||||||
|
),
|
||||||
|
|
||||||
):
|
):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue