Update status UI in `.on_ui_settings_change()`
Use this method to go through writing all allocator parameters and then reading all changes back into the order mode pane including updating the limit and step labels by the fill bar. Machinery changes: - add `.limit()` and `.step_sizes()` methods to the allocator to provide the appropriate data depending on the pp limit size unit (eg. currency vs. units) - humanize the label display text such that you have nice suffixes and a fixed precision - tweak the fill bar labels to be simpler since the values are now humanized - expect `.on_ui_settings_change()` to be called for every slots hotkey tweakfsp_feeds
parent
0589c3c5b7
commit
5b923ae577
|
@ -1427,6 +1427,10 @@ async def run_fsp(
|
||||||
period=14,
|
period=14,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def settings_change(key: str, value: str) -> bool:
|
||||||
|
print(f'{key}: {value}')
|
||||||
|
return True
|
||||||
|
|
||||||
async with (
|
async with (
|
||||||
portal.open_stream_from(
|
portal.open_stream_from(
|
||||||
|
|
||||||
|
@ -1445,7 +1449,8 @@ async def run_fsp(
|
||||||
# TODO:
|
# TODO:
|
||||||
open_form_input_handling(
|
open_form_input_handling(
|
||||||
sidepane,
|
sidepane,
|
||||||
focus_next=linkedsplits.godwidget
|
focus_next=linkedsplits.godwidget,
|
||||||
|
on_value_change=settings_change,
|
||||||
),
|
),
|
||||||
|
|
||||||
):
|
):
|
||||||
|
@ -1538,14 +1543,14 @@ async def run_fsp(
|
||||||
|
|
||||||
done()
|
done()
|
||||||
|
|
||||||
i = 0
|
# i = 0
|
||||||
# update chart graphics
|
# update chart graphics
|
||||||
async for value in stream:
|
async for value in stream:
|
||||||
|
|
||||||
# chart isn't actively shown so just skip render cycle
|
# chart isn't actively shown so just skip render cycle
|
||||||
if chart.linked.isHidden():
|
if chart.linked.isHidden():
|
||||||
print(f'{i} unseen fsp cyclce')
|
# print(f'{i} unseen fsp cyclce')
|
||||||
i += 1
|
# i += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|
|
@ -363,7 +363,7 @@ async def handle_field_input(
|
||||||
|
|
||||||
key = widget._key
|
key = widget._key
|
||||||
value = widget.text()
|
value = widget.text()
|
||||||
await on_value_change(key, value)
|
on_value_change(key, value)
|
||||||
|
|
||||||
|
|
||||||
def mk_form(
|
def mk_form(
|
||||||
|
@ -539,8 +539,8 @@ def mk_fill_status_bar(
|
||||||
# PnL on lhs
|
# PnL on lhs
|
||||||
bar_labels_lhs = QVBoxLayout(fields)
|
bar_labels_lhs = QVBoxLayout(fields)
|
||||||
left_label = fields.add_field_label(
|
left_label = fields.add_field_label(
|
||||||
dedent(f"""
|
dedent("""
|
||||||
-{30}% PnL
|
{pnl}% PnL
|
||||||
"""),
|
"""),
|
||||||
font_size=bar_label_font_size,
|
font_size=bar_label_font_size,
|
||||||
font_color='gunmetal',
|
font_color='gunmetal',
|
||||||
|
@ -567,16 +567,17 @@ def mk_fill_status_bar(
|
||||||
# https://docs.python.org/3/library/string.html#grammar-token-precision
|
# https://docs.python.org/3/library/string.html#grammar-token-precision
|
||||||
|
|
||||||
top_label = fields.add_field_label(
|
top_label = fields.add_field_label(
|
||||||
dedent(f"""
|
# {limit:.1f} limit
|
||||||
{3.32:.1f}% port
|
dedent("""
|
||||||
|
{limit}
|
||||||
"""),
|
"""),
|
||||||
font_size=bar_label_font_size,
|
font_size=bar_label_font_size,
|
||||||
font_color='gunmetal',
|
font_color='gunmetal',
|
||||||
)
|
)
|
||||||
|
|
||||||
bottom_label = fields.add_field_label(
|
bottom_label = fields.add_field_label(
|
||||||
dedent(f"""
|
dedent("""
|
||||||
{5e3/4/1e3:.2f}k step\n
|
x = {step_size}\n
|
||||||
"""),
|
"""),
|
||||||
font_size=bar_label_font_size,
|
font_size=bar_label_font_size,
|
||||||
font_color='gunmetal',
|
font_color='gunmetal',
|
||||||
|
@ -665,7 +666,7 @@ 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, left_label, bottom_label, top_label = mk_fill_status_bar(
|
hbox, fill_bar, left_label, top_label, bottom_label = mk_fill_status_bar(
|
||||||
form, pane_vbox=vbox
|
form, pane_vbox=vbox
|
||||||
)
|
)
|
||||||
# TODO: would be nice to have some better way of reffing these over
|
# TODO: would be nice to have some better way of reffing these over
|
||||||
|
|
|
@ -262,12 +262,9 @@ 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_pane = order_mode.pane
|
pp_pane = order_mode.pane
|
||||||
pp_pane.alloc.slots = num
|
pp_pane.on_ui_settings_change('slots', num)
|
||||||
|
|
||||||
edit = pp_pane.form.fields['slots']
|
edit = pp_pane.form.fields['slots']
|
||||||
edit.setText(text)
|
|
||||||
edit.selectAll()
|
edit.selectAll()
|
||||||
|
|
||||||
on_next_release = edit.deselect
|
on_next_release = edit.deselect
|
||||||
|
|
||||||
pp_pane.update_status_ui()
|
pp_pane.update_status_ui()
|
||||||
|
|
|
@ -35,6 +35,7 @@ from ._anchors import (
|
||||||
pp_tight_and_right, # wanna keep it straight in the long run
|
pp_tight_and_right, # wanna keep it straight in the long run
|
||||||
gpath_pin,
|
gpath_pin,
|
||||||
)
|
)
|
||||||
|
from ..calc import humanize
|
||||||
from ..clearing._messages import BrokerdPosition, Status
|
from ..clearing._messages import BrokerdPosition, Status
|
||||||
from ..data._source import Symbol
|
from ..data._source import Symbol
|
||||||
from ._label import Label
|
from ._label import Label
|
||||||
|
@ -77,13 +78,17 @@ SizeUnit = Enum(
|
||||||
|
|
||||||
class Allocator(BaseModel):
|
class Allocator(BaseModel):
|
||||||
|
|
||||||
symbol: Symbol
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
validate_assignment = True
|
validate_assignment = True
|
||||||
copy_on_model_validation = False
|
copy_on_model_validation = False
|
||||||
arbitrary_types_allowed = True
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
# required to get the account validator lookup working?
|
||||||
|
extra = 'allow'
|
||||||
|
# underscore_attrs_are_private = False
|
||||||
|
|
||||||
|
symbol: Symbol
|
||||||
|
|
||||||
account: Optional[str] = 'paper'
|
account: Optional[str] = 'paper'
|
||||||
_accounts: bidict[str, Optional[str]]
|
_accounts: bidict[str, Optional[str]]
|
||||||
|
|
||||||
|
@ -108,6 +113,21 @@ class Allocator(BaseModel):
|
||||||
currency_limit: float
|
currency_limit: float
|
||||||
slots: int
|
slots: int
|
||||||
|
|
||||||
|
def step_sizes(
|
||||||
|
self,
|
||||||
|
) -> dict[str, float]:
|
||||||
|
slots = self.slots
|
||||||
|
return {
|
||||||
|
'units': self.units_limit / slots,
|
||||||
|
'currency': self.currency_limit / slots,
|
||||||
|
}
|
||||||
|
|
||||||
|
def limit(self) -> float:
|
||||||
|
if self.size_unit == 'currency':
|
||||||
|
return self.currency_limit
|
||||||
|
else:
|
||||||
|
return self.units_limit
|
||||||
|
|
||||||
def next_order_info(
|
def next_order_info(
|
||||||
self,
|
self,
|
||||||
|
|
||||||
|
@ -127,12 +147,17 @@ class Allocator(BaseModel):
|
||||||
live_size = live_pp.size
|
live_size = live_pp.size
|
||||||
startup_size = startup_pp.size
|
startup_size = startup_pp.size
|
||||||
|
|
||||||
step_size = self.units_limit / self.slots
|
steps = self.step_sizes()
|
||||||
l_sub_pp = self.units_limit - live_size
|
|
||||||
|
|
||||||
if self.size_unit == 'currency':
|
if self.size_unit == 'units':
|
||||||
|
# step_size = self.units_limit / self.slots
|
||||||
|
step_size = steps['units']
|
||||||
|
l_sub_pp = self.units_limit - live_size
|
||||||
|
|
||||||
|
elif self.size_unit == 'currency':
|
||||||
startup_size = startup_pp.size * startup_pp.avg_price / price
|
startup_size = startup_pp.size * startup_pp.avg_price / price
|
||||||
step_size = self.currency_limit / self.slots / price
|
# step_size = self.currency_limit / self.slots / price
|
||||||
|
step_size = steps['currency'] / price
|
||||||
l_sub_pp = (
|
l_sub_pp = (
|
||||||
self.currency_limit - live_size * live_pp.avg_price
|
self.currency_limit - live_size * live_pp.avg_price
|
||||||
) / price
|
) / price
|
||||||
|
@ -184,18 +209,19 @@ class OrderModePane:
|
||||||
|
|
||||||
def on_selection_change(
|
def on_selection_change(
|
||||||
self,
|
self,
|
||||||
key: str,
|
|
||||||
text: str,
|
text: str,
|
||||||
|
key: str,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
'''Called on any order pane drop down selection change.
|
'''Called on any order pane drop down selection change.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
print(f'{text}')
|
print(f'selection input: {text}')
|
||||||
setattr(self.alloc, key, text)
|
setattr(self.alloc, key, text)
|
||||||
print(self.alloc.dict())
|
self.on_ui_settings_change(key, text)
|
||||||
|
|
||||||
async def on_ui_settings_change(
|
def on_ui_settings_change(
|
||||||
self,
|
self,
|
||||||
|
|
||||||
key: str,
|
key: str,
|
||||||
|
@ -205,7 +231,56 @@ class OrderModePane:
|
||||||
'''Called on any order pane edit field value change.
|
'''Called on any order pane edit field value change.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
print(f'settings change: {key}:{value}')
|
print(f'settings change: {key}: {value}')
|
||||||
|
alloc = self.alloc
|
||||||
|
size_unit = alloc.size_unit
|
||||||
|
|
||||||
|
# write any passed settings to allocator
|
||||||
|
if key == 'limit':
|
||||||
|
if size_unit == 'currency':
|
||||||
|
alloc.currency_limit = float(value)
|
||||||
|
else:
|
||||||
|
alloc.units_limit = float(value)
|
||||||
|
|
||||||
|
elif key == 'slots':
|
||||||
|
alloc.slots = int(value)
|
||||||
|
|
||||||
|
elif key == 'size_unit':
|
||||||
|
# TODO: if there's a limit size unit change re-compute
|
||||||
|
# the current settings in the new units
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif key == 'account':
|
||||||
|
print(f'TODO: change account -> {value}')
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unknown setting {key}')
|
||||||
|
|
||||||
|
# read out settings and update UI
|
||||||
|
|
||||||
|
suffix = {'currency': ' $', 'units': ' u'}[size_unit]
|
||||||
|
limit = alloc.limit()
|
||||||
|
|
||||||
|
# TODO: a reverse look up from the position to the equivalent
|
||||||
|
# account(s), if none then look to user config for default?
|
||||||
|
self.update_status_ui()
|
||||||
|
|
||||||
|
step_size = alloc.step_sizes()[size_unit]
|
||||||
|
|
||||||
|
self.step_label.format(
|
||||||
|
step_size=str(humanize(step_size)) + suffix
|
||||||
|
)
|
||||||
|
self.limit_label.format(
|
||||||
|
limit=str(humanize(limit)) + suffix
|
||||||
|
)
|
||||||
|
|
||||||
|
# update size unit in UI
|
||||||
|
self.form.fields['size_unit'].setCurrentText(
|
||||||
|
alloc._size_units[alloc.size_unit]
|
||||||
|
)
|
||||||
|
self.form.fields['slots'].setText(str(alloc.slots))
|
||||||
|
self.form.fields['limit'].setText(str(limit))
|
||||||
|
|
||||||
# TODO: maybe return a diff of settings so if we can an error we
|
# TODO: maybe return a diff of settings so if we can an error we
|
||||||
# can have general input handling code to report it through the
|
# can have general input handling code to report it through the
|
||||||
# UI in some way?
|
# UI in some way?
|
||||||
|
@ -216,7 +291,7 @@ class OrderModePane:
|
||||||
):
|
):
|
||||||
alloc = self.alloc
|
alloc = self.alloc
|
||||||
asset_type = alloc.symbol.type_key
|
asset_type = alloc.symbol.type_key
|
||||||
form = self.form
|
# form = self.form
|
||||||
|
|
||||||
# TODO: pull from piker.toml
|
# TODO: pull from piker.toml
|
||||||
# default config
|
# default config
|
||||||
|
@ -261,16 +336,7 @@ class OrderModePane:
|
||||||
|
|
||||||
limit_text = alloc.units_limit
|
limit_text = alloc.units_limit
|
||||||
|
|
||||||
# update size unit in UI
|
self.on_ui_settings_change('limit', limit_text)
|
||||||
form.fields['size_unit'].setCurrentText(
|
|
||||||
alloc._size_units[alloc.size_unit]
|
|
||||||
)
|
|
||||||
form.fields['slots'].setText(str(alloc.slots))
|
|
||||||
|
|
||||||
form.fields['limit'].setText(str(limit_text))
|
|
||||||
|
|
||||||
# TODO: a reverse look up from the position to the equivalent
|
|
||||||
# account(s), if none then look to user config for default?
|
|
||||||
|
|
||||||
self.update_status_ui(size=startup_size)
|
self.update_status_ui(size=startup_size)
|
||||||
|
|
||||||
|
@ -289,7 +355,7 @@ class OrderModePane:
|
||||||
prop = live_currency_size / alloc.currency_limit
|
prop = live_currency_size / alloc.currency_limit
|
||||||
|
|
||||||
else:
|
else:
|
||||||
prop = size or live_pp.size / alloc.units_limit
|
prop = (size or live_pp.size) / alloc.units_limit
|
||||||
|
|
||||||
# calculate proportion of position size limit
|
# calculate proportion of position size limit
|
||||||
# that exists and display in fill bar
|
# that exists and display in fill bar
|
||||||
|
@ -325,14 +391,6 @@ class OrderModePane:
|
||||||
order.price = level
|
order.price = level
|
||||||
order.size = order_info['size']
|
order.size = order_info['size']
|
||||||
|
|
||||||
def on_settings_change_update_ui(
|
|
||||||
self,
|
|
||||||
) -> None:
|
|
||||||
# TODO:
|
|
||||||
# - recompute both units_limit and currency_limit
|
|
||||||
# - update fill bar slotting if necessary (only on slots?)
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class PositionTracker:
|
class PositionTracker:
|
||||||
'''Track and display a real-time position for a single symbol
|
'''Track and display a real-time position for a single symbol
|
||||||
|
|
Loading…
Reference in New Issue