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, |  | ||||||
|                 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, | ||||||
|  |             ), | ||||||
| 
 | 
 | ||||||
|         ): |         ): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue