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
  tweak
fsp_feeds
Tyler Goodlet 2021-08-26 08:40:14 -04:00
parent 0589c3c5b7
commit 5b923ae577
4 changed files with 108 additions and 47 deletions

View File

@ -1427,6 +1427,10 @@ async def run_fsp(
period=14,
)
async def settings_change(key: str, value: str) -> bool:
print(f'{key}: {value}')
return True
async with (
portal.open_stream_from(
@ -1445,7 +1449,8 @@ async def run_fsp(
# TODO:
open_form_input_handling(
sidepane,
focus_next=linkedsplits.godwidget
focus_next=linkedsplits.godwidget,
on_value_change=settings_change,
),
):
@ -1538,14 +1543,14 @@ async def run_fsp(
done()
i = 0
# i = 0
# update chart graphics
async for value in stream:
# chart isn't actively shown so just skip render cycle
if chart.linked.isHidden():
print(f'{i} unseen fsp cyclce')
i += 1
# print(f'{i} unseen fsp cyclce')
# i += 1
continue
now = time.time()

View File

@ -363,7 +363,7 @@ async def handle_field_input(
key = widget._key
value = widget.text()
await on_value_change(key, value)
on_value_change(key, value)
def mk_form(
@ -539,8 +539,8 @@ def mk_fill_status_bar(
# PnL on lhs
bar_labels_lhs = QVBoxLayout(fields)
left_label = fields.add_field_label(
dedent(f"""
-{30}% PnL
dedent("""
{pnl}% PnL
"""),
font_size=bar_label_font_size,
font_color='gunmetal',
@ -567,16 +567,17 @@ def mk_fill_status_bar(
# https://docs.python.org/3/library/string.html#grammar-token-precision
top_label = fields.add_field_label(
dedent(f"""
{3.32:.1f}% port
# {limit:.1f} limit
dedent("""
{limit}
"""),
font_size=bar_label_font_size,
font_color='gunmetal',
)
bottom_label = fields.add_field_label(
dedent(f"""
{5e3/4/1e3:.2f}k step\n
dedent("""
x = {step_size}\n
"""),
font_size=bar_label_font_size,
font_color='gunmetal',
@ -665,7 +666,7 @@ def mk_order_pane_layout(
# _, h = form.width(), form.height()
# 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
)
# TODO: would be nice to have some better way of reffing these over

View File

@ -262,12 +262,9 @@ async def handle_viewmode_kb_inputs(
# hot key to set order slots size
num = int(text)
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.setText(text)
edit.selectAll()
on_next_release = edit.deselect
pp_pane.update_status_ui()

View File

@ -35,6 +35,7 @@ from ._anchors import (
pp_tight_and_right, # wanna keep it straight in the long run
gpath_pin,
)
from ..calc import humanize
from ..clearing._messages import BrokerdPosition, Status
from ..data._source import Symbol
from ._label import Label
@ -77,13 +78,17 @@ SizeUnit = Enum(
class Allocator(BaseModel):
symbol: Symbol
class Config:
validate_assignment = True
copy_on_model_validation = False
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'
_accounts: bidict[str, Optional[str]]
@ -108,6 +113,21 @@ class Allocator(BaseModel):
currency_limit: float
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(
self,
@ -127,12 +147,17 @@ class Allocator(BaseModel):
live_size = live_pp.size
startup_size = startup_pp.size
step_size = self.units_limit / self.slots
steps = self.step_sizes()
if self.size_unit == 'units':
# step_size = self.units_limit / self.slots
step_size = steps['units']
l_sub_pp = self.units_limit - live_size
if self.size_unit == 'currency':
elif self.size_unit == 'currency':
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 = (
self.currency_limit - live_size * live_pp.avg_price
) / price
@ -184,18 +209,19 @@ class OrderModePane:
def on_selection_change(
self,
key: str,
text: str,
key: str,
) -> None:
'''Called on any order pane drop down selection change.
'''
print(f'{text}')
print(f'selection input: {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,
key: str,
@ -206,6 +232,55 @@ class OrderModePane:
'''
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
# can have general input handling code to report it through the
# UI in some way?
@ -216,7 +291,7 @@ class OrderModePane:
):
alloc = self.alloc
asset_type = alloc.symbol.type_key
form = self.form
# form = self.form
# TODO: pull from piker.toml
# default config
@ -261,16 +336,7 @@ class OrderModePane:
limit_text = alloc.units_limit
# update size unit in UI
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.on_ui_settings_change('limit', limit_text)
self.update_status_ui(size=startup_size)
@ -289,7 +355,7 @@ class OrderModePane:
prop = live_currency_size / alloc.currency_limit
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
# that exists and display in fill bar
@ -325,14 +391,6 @@ class OrderModePane:
order.price = level
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:
'''Track and display a real-time position for a single symbol