Port to order pane apis
							parent
							
								
									bc2f4186fd
								
							
						
					
					
						commit
						206af0d575
					
				| 
						 | 
				
			
			@ -44,6 +44,7 @@ import pydantic
 | 
			
		|||
 | 
			
		||||
from ._event import open_handlers
 | 
			
		||||
from ._style import hcolor, _font, _font_small, DpiAwareFont
 | 
			
		||||
from .. import brokers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FontAndChartAwareLineEdit(QLineEdit):
 | 
			
		||||
| 
						 | 
				
			
			@ -366,11 +367,15 @@ async def handle_field_input(
 | 
			
		|||
 | 
			
		||||
            # process field input
 | 
			
		||||
            if key in (Qt.Key_Enter, Qt.Key_Return):
 | 
			
		||||
 | 
			
		||||
                value = widget.text()
 | 
			
		||||
                key = widget._key
 | 
			
		||||
 | 
			
		||||
                old = getattr(model, key)
 | 
			
		||||
 | 
			
		||||
                try:
 | 
			
		||||
                    setattr(model, key, value)
 | 
			
		||||
 | 
			
		||||
                except pydantic.error_wrappers.ValidationError:
 | 
			
		||||
                    setattr(model, key, old)
 | 
			
		||||
                    widget.setText(str(old))
 | 
			
		||||
| 
						 | 
				
			
			@ -380,16 +385,14 @@ async def handle_field_input(
 | 
			
		|||
 | 
			
		||||
def mk_form(
 | 
			
		||||
 | 
			
		||||
    model: pydantic.BaseModel,
 | 
			
		||||
    parent: QWidget,
 | 
			
		||||
    fields_schema: dict,
 | 
			
		||||
 | 
			
		||||
) -> FieldsForm:
 | 
			
		||||
 | 
			
		||||
    form = FieldsForm(parent=parent)
 | 
			
		||||
    # TODO: generate components from model
 | 
			
		||||
    # instead of schema dict (aka use an ORM)
 | 
			
		||||
    form.model = model
 | 
			
		||||
    form = FieldsForm(parent=parent)
 | 
			
		||||
 | 
			
		||||
    # generate sub-components from schema dict
 | 
			
		||||
    for key, config in fields_schema.items():
 | 
			
		||||
| 
						 | 
				
			
			@ -413,18 +416,6 @@ def mk_form(
 | 
			
		|||
                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
 | 
			
		||||
 | 
			
		||||
    return form
 | 
			
		||||
| 
						 | 
				
			
			@ -438,7 +429,7 @@ async def open_form_input_handling(
 | 
			
		|||
 | 
			
		||||
) -> FieldsForm:
 | 
			
		||||
 | 
			
		||||
    assert form.model, f'{form} must define a `.model`'
 | 
			
		||||
    # assert form.model, f'{form} must define a `.model`'
 | 
			
		||||
 | 
			
		||||
    async with open_handlers(
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -630,31 +621,27 @@ def mk_fill_status_bar(
 | 
			
		|||
    slots = 4
 | 
			
		||||
    bar.set_slots(slots, value=0)
 | 
			
		||||
 | 
			
		||||
    return hbox, bar
 | 
			
		||||
    return hbox, bar, left_label, top_label, bottom_label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mk_order_pane_layout(
 | 
			
		||||
 | 
			
		||||
    parent: QWidget,
 | 
			
		||||
    # accounts: dict[str, Optional[str]],
 | 
			
		||||
    font_size: int = _font_small.px_size - 2
 | 
			
		||||
 | 
			
		||||
) -> FieldsForm:
 | 
			
		||||
 | 
			
		||||
    from ._position import mk_alloc
 | 
			
		||||
    # TODO: some kinda pydantic sub-type
 | 
			
		||||
    # that enforces a composite widget attr er sumthin..
 | 
			
		||||
    # as part of our ORM thingers.
 | 
			
		||||
    alloc = mk_alloc()
 | 
			
		||||
    accounts = brokers.config.load_accounts()
 | 
			
		||||
 | 
			
		||||
    # TODO: maybe just allocate the whole fields form here
 | 
			
		||||
    # and expect an async ctx entry?
 | 
			
		||||
    form = mk_form(
 | 
			
		||||
        parent=parent,
 | 
			
		||||
        model=alloc,
 | 
			
		||||
        fields_schema={
 | 
			
		||||
            'account': {
 | 
			
		||||
                'type': 'select',
 | 
			
		||||
                'default_value': alloc._accounts.keys()
 | 
			
		||||
                'default_value': accounts.keys(),
 | 
			
		||||
            },
 | 
			
		||||
            'size_unit': {
 | 
			
		||||
                'label': '**allocate**:',
 | 
			
		||||
| 
						 | 
				
			
			@ -670,8 +657,8 @@ def mk_order_pane_layout(
 | 
			
		|||
                'type': 'select',
 | 
			
		||||
                'default_value': ['uniform'],
 | 
			
		||||
            },
 | 
			
		||||
            'size': {
 | 
			
		||||
                'label': '**size**:',
 | 
			
		||||
            'limit': {
 | 
			
		||||
                'label': '**limit**:',
 | 
			
		||||
                'type': 'edit',
 | 
			
		||||
                'default_value': 5000,
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			@ -683,7 +670,6 @@ def mk_order_pane_layout(
 | 
			
		|||
        },
 | 
			
		||||
    )
 | 
			
		||||
    form._font_size = font_size
 | 
			
		||||
    alloc._widget = form
 | 
			
		||||
 | 
			
		||||
    # top level pane layout
 | 
			
		||||
    # 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()
 | 
			
		||||
    # 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.left_label = left_label
 | 
			
		||||
    form.bottom_label = bottom_label
 | 
			
		||||
    form.top_label = top_label
 | 
			
		||||
 | 
			
		||||
    # add pp fill bar + spacing
 | 
			
		||||
    vbox.addLayout(hbox, stretch=1/3)
 | 
			
		||||
| 
						 | 
				
			
			@ -702,9 +695,9 @@ def mk_order_pane_layout(
 | 
			
		|||
    feed_label = form.add_field_label(
 | 
			
		||||
        dedent("""
 | 
			
		||||
        brokerd.ib\n
 | 
			
		||||
        |_@localhost:8509\n
 | 
			
		||||
        |_consumers: 4\n
 | 
			
		||||
        |_streams: 9\n
 | 
			
		||||
        |_@{host}:{port}\n
 | 
			
		||||
        |_consumers: {cons}\n
 | 
			
		||||
        |_streams: {streams}\n
 | 
			
		||||
        """),
 | 
			
		||||
        font_size=_font.px_size - 5,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -722,5 +715,4 @@ def mk_order_pane_layout(
 | 
			
		|||
    vbox.setSpacing(36)
 | 
			
		||||
 | 
			
		||||
    form.show()
 | 
			
		||||
 | 
			
		||||
    return form
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -261,14 +261,16 @@ async def handle_viewmode_kb_inputs(
 | 
			
		|||
        ):
 | 
			
		||||
            # hot key to set order slots size
 | 
			
		||||
            num = int(text)
 | 
			
		||||
            pp = order_mode.pp
 | 
			
		||||
            pp_pane = pp.pane
 | 
			
		||||
            pp_pane.model.slots = num
 | 
			
		||||
            edit = pp_pane.fields['slots']
 | 
			
		||||
            pp_pane = order_mode.pane
 | 
			
		||||
            pp_pane.alloc.slots = num
 | 
			
		||||
 | 
			
		||||
            edit = pp_pane.form.fields['slots']
 | 
			
		||||
            edit.setText(text)
 | 
			
		||||
            edit.selectAll()
 | 
			
		||||
 | 
			
		||||
            on_next_release = edit.deselect
 | 
			
		||||
            pp.init_status_ui()
 | 
			
		||||
 | 
			
		||||
            pp_pane.init_status_ui()
 | 
			
		||||
 | 
			
		||||
        else:  # none active
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@ Chart trading, the only way to scalp.
 | 
			
		|||
 | 
			
		||||
"""
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from functools import partial
 | 
			
		||||
from pprint import pformat
 | 
			
		||||
import time
 | 
			
		||||
from typing import Optional, Dict, Callable, Any
 | 
			
		||||
| 
						 | 
				
			
			@ -28,15 +29,16 @@ from pydantic import BaseModel
 | 
			
		|||
import tractor
 | 
			
		||||
import trio
 | 
			
		||||
 | 
			
		||||
from .. import brokers
 | 
			
		||||
from ..clearing._client import open_ems, OrderBook
 | 
			
		||||
from ..data._source import Symbol
 | 
			
		||||
from ..log import get_logger
 | 
			
		||||
from ._editors import LineEditor, ArrowEditor
 | 
			
		||||
from ._lines import order_line, LevelLine
 | 
			
		||||
from ._position import PositionTracker
 | 
			
		||||
from ._position import PositionTracker, OrderModePane, mk_alloc
 | 
			
		||||
from ._window import MultiStatus
 | 
			
		||||
from ..clearing._messages import Order
 | 
			
		||||
# from ._forms import FieldsForm
 | 
			
		||||
from ._forms import open_form_input_handling
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = get_logger(__name__)
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +90,8 @@ class OrderMode:
 | 
			
		|||
    multistatus: MultiStatus
 | 
			
		||||
    pp: PositionTracker
 | 
			
		||||
    allocator: 'Allocator'  # noqa
 | 
			
		||||
    pane: OrderModePane
 | 
			
		||||
 | 
			
		||||
    active: bool = False
 | 
			
		||||
 | 
			
		||||
    name: str = 'order'
 | 
			
		||||
| 
						 | 
				
			
			@ -127,31 +131,16 @@ class OrderMode:
 | 
			
		|||
            ) else False,
 | 
			
		||||
 | 
			
		||||
            **line_kwargs,
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # define a callback applied for each level change to the line
 | 
			
		||||
        # which will recompute the order size based on allocator
 | 
			
		||||
        # settings:
 | 
			
		||||
        def update_from_size_calc(y: float) -> None:
 | 
			
		||||
 | 
			
		||||
            order_info = self.allocator.get_order_info(
 | 
			
		||||
                symbol=symbol,
 | 
			
		||||
                price=y,
 | 
			
		||||
                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
 | 
			
		||||
        # set level update callback to order pane method and update once
 | 
			
		||||
        # immediately
 | 
			
		||||
        line._on_level_change = partial(
 | 
			
		||||
            self.pane.on_level_change_update_next_order_info,
 | 
			
		||||
            line=line,
 | 
			
		||||
            order=order,
 | 
			
		||||
        )
 | 
			
		||||
        line._on_level_change(order.price)
 | 
			
		||||
 | 
			
		||||
        return line
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -493,16 +482,52 @@ async def run_order_mode(
 | 
			
		|||
        ),
 | 
			
		||||
 | 
			
		||||
    ):
 | 
			
		||||
        log.info(f'Opening order mode for {brokername}.{symbol.key}')
 | 
			
		||||
 | 
			
		||||
        view = chart.view
 | 
			
		||||
 | 
			
		||||
        # annotations editors
 | 
			
		||||
        lines = LineEditor(chart=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.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(
 | 
			
		||||
            chart,
 | 
			
		||||
| 
						 | 
				
			
			@ -512,6 +537,7 @@ async def run_order_mode(
 | 
			
		|||
            multistatus,
 | 
			
		||||
            pp_tracker,
 | 
			
		||||
            allocator=alloc,
 | 
			
		||||
            pane=order_pane,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # 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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # default entry sizing
 | 
			
		||||
        if asset_type in ('stock', 'crypto', 'forex'):
 | 
			
		||||
            alloc.size_unit = '$ size'
 | 
			
		||||
| 
						 | 
				
			
			@ -544,11 +569,11 @@ async def run_order_mode(
 | 
			
		|||
            pp_tracker.pane.fields['slots'].setText(str(alloc.slots))
 | 
			
		||||
 | 
			
		||||
            # make entry step 1.0
 | 
			
		||||
            alloc.size = slots
 | 
			
		||||
            pp_tracker.pane.fields['size'].setText(str(alloc.size))
 | 
			
		||||
            alloc.units_size = slots
 | 
			
		||||
            pp_tracker.pane.fields['size'].setText(str(alloc.units_size))
 | 
			
		||||
 | 
			
		||||
        # make fill bar and positioning snapshot
 | 
			
		||||
        pp_tracker.init_status_ui()
 | 
			
		||||
        order_pane.init_status_ui()
 | 
			
		||||
 | 
			
		||||
        # TODO: this should go onto some sort of
 | 
			
		||||
        # data-view strimg thinger..right?
 | 
			
		||||
| 
						 | 
				
			
			@ -572,7 +597,11 @@ async def run_order_mode(
 | 
			
		|||
        async with (
 | 
			
		||||
            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