From 6889a25926aae5834605929a5ede01662f21a5e0 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Wed, 2 Mar 2022 09:19:32 -0500 Subject: [PATCH 1/5] Drop pp bar clipping, hopefully fix slot sizing --- piker/ui/_forms.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/piker/ui/_forms.py b/piker/ui/_forms.py index 1f1a7cd5..841f2e85 100644 --- a/piker/ui/_forms.py +++ b/piker/ui/_forms.py @@ -542,7 +542,7 @@ class FillStatusBar(QProgressBar): ''' border_px: int = 2 - slot_margin_px: int = 2 + slot_margin_px: int = 1 def __init__( self, @@ -553,7 +553,10 @@ class FillStatusBar(QProgressBar): ) -> None: super().__init__(parent=parent) + self.approx_h = approx_height_px + self.setMinimumHeight(int(round(approx_height_px))) + self.font_size = font_size self.setFormat('') # label format @@ -573,9 +576,10 @@ class FillStatusBar(QProgressBar): approx_h, slots, ) - clipped = int(slots * tot_slot_h + 2*self.border_px) - self.setMaximumHeight(clipped) - slot_height_px = tot_slot_h - 2*self.slot_margin_px + slot_height_px = tot_slot_h + r/slots - self.slot_margin_px + + # clipped = int(slots * tot_slot_h)# + 2*self.border_px) + # self.setMaximumHeight(clipped) self.setOrientation(Qt.Vertical) self.setStyleSheet( @@ -606,6 +610,7 @@ class FillStatusBar(QProgressBar): }} """ ) + # sets a discrete "block" per slot # margin-bottom: {slot_margin_px*2}px; # margin-top: {slot_margin_px*2}px; From 16b9e39e1168f8d2ba8368ace0919c2d1e9b5686 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Wed, 2 Mar 2022 09:20:17 -0500 Subject: [PATCH 2/5] Dis-allow an allocator limit less then the current pp size --- piker/clearing/_allocate.py | 21 +++++++++++++---- piker/ui/_position.py | 47 +++++++++++++++++++++++++++++-------- piker/ui/order_mode.py | 3 ++- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/piker/clearing/_allocate.py b/piker/clearing/_allocate.py index 7b61a4bd..6ef38692 100644 --- a/piker/clearing/_allocate.py +++ b/piker/clearing/_allocate.py @@ -29,7 +29,8 @@ from ._messages import BrokerdPosition, Status class Position(BaseModel): - '''Basic pp (personal position) model with attached fills history. + ''' + Basic pp (personal position) model with attached fills history. This type should be IPC wire ready? @@ -61,6 +62,15 @@ class Position(BaseModel): self.avg_price = avg_price self.size = size + @property + def dsize(self) -> float: + ''' + The "dollar" size of the pp, normally in trading (fiat) unit + terms. + + ''' + return self.avg_price * self.size + _size_units = bidict({ 'currency': '$ size', @@ -114,7 +124,8 @@ class Allocator(BaseModel): def step_sizes( self, ) -> (float, float): - '''Return the units size for each unit type as a tuple. + ''' + Return the units size for each unit type as a tuple. ''' slots = self.slots @@ -142,7 +153,8 @@ class Allocator(BaseModel): action: str, ) -> dict: - '''Generate order request info for the "next" submittable order + ''' + Generate order request info for the "next" submittable order depending on position / order entry config. ''' @@ -250,7 +262,8 @@ class Allocator(BaseModel): pp: Position, ) -> float: - '''Calc and return the number of slots used by this ``Position``. + ''' + Calc and return the number of slots used by this ``Position``. ''' abs_pp_size = abs(pp.size) diff --git a/piker/ui/_position.py b/piker/ui/_position.py index e057154e..0abb6459 100644 --- a/piker/ui/_position.py +++ b/piker/ui/_position.py @@ -71,10 +71,10 @@ async def update_pnl_from_feed( log.info(f'Starting pnl display for {pp.alloc.account}') if live.size < 0: - types = ('ask', 'last', 'last', 'utrade') + types = ('ask', 'last', 'last', 'dark_trade') elif live.size > 0: - types = ('bid', 'last', 'last', 'utrade') + types = ('bid', 'last', 'last', 'dark_trade') else: log.info(f'No position (yet) for {tracker.alloc.account}@{key}') @@ -119,7 +119,8 @@ async def update_pnl_from_feed( @dataclass class SettingsPane: - '''Composite set of widgets plus an allocator model for configuring + ''' + Composite set of widgets plus an allocator model for configuring order entry sizes and position limits per tradable instrument. ''' @@ -151,7 +152,8 @@ class SettingsPane: key: str, ) -> None: - '''Called on any order pane drop down selection change. + ''' + Called on any order pane drop down selection change. ''' log.info(f'selection input {key}:{text}') @@ -164,7 +166,8 @@ class SettingsPane: value: str, ) -> bool: - '''Called on any order pane edit field value change. + ''' + Called on any order pane edit field value change. ''' mode = self.order_mode @@ -219,16 +222,36 @@ class SettingsPane: else: value = puterize(value) if key == 'limit': + pp = mode.current_pp.live_pp + if size_unit == 'currency': + dsize = pp.dsize + if dsize > value: + log.error( + f'limit must > then current pp: {dsize}' + ) + raise ValueError + alloc.currency_limit = value + else: + size = pp.size + if size > value: + log.error( + f'limit must > then current pp: {size}' + ) + raise ValueError + alloc.units_limit = value elif key == 'slots': + if value <= 0: + raise ValueError('slots must be > 0') alloc.slots = int(value) else: - raise ValueError(f'Unknown setting {key}') + log.error(f'Unknown setting {key}') + raise ValueError log.info(f'settings change: {key}: {value}') @@ -363,7 +386,8 @@ def position_line( marker: Optional[LevelMarker] = None, ) -> LevelLine: - '''Convenience routine to create a line graphic representing a "pp" + ''' + Convenience routine to create a line graphic representing a "pp" aka the acro for a, "{piker, private, personal, puny, } position". @@ -417,7 +441,8 @@ def position_line( class PositionTracker: - '''Track and display real-time positions for a single symbol + ''' + Track and display real-time positions for a single symbol over multiple accounts on a single chart. Graphically composed of a level line and marker as well as labels @@ -497,7 +522,8 @@ class PositionTracker: @property def pane(self) -> FieldsForm: - '''Return handle to pp side pane form. + ''' + Return handle to pp side pane form. ''' return self.chart.linked.godwidget.pp_pane @@ -507,7 +533,8 @@ class PositionTracker: marker: LevelMarker ) -> None: - '''Update all labels. + ''' + Update all labels. Meant to be called from the maker ``.paint()`` for immediate, lag free label draws. diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index 437e4ca5..9f4dbadb 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -107,7 +107,8 @@ def on_level_change_update_next_order_info( @dataclass class OrderMode: - '''Major UX mode for placing orders on a chart view providing so + ''' + Major UX mode for placing orders on a chart view providing so called, "chart trading". This is the other "main" mode that pairs with "view mode" (when From 2c9612ebd860c632492ac1f6bee756fd0e1e2d93 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 3 Mar 2022 10:46:05 -0500 Subject: [PATCH 3/5] Force exact pp bar size --- piker/ui/_forms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/piker/ui/_forms.py b/piker/ui/_forms.py index 841f2e85..e6c28112 100644 --- a/piker/ui/_forms.py +++ b/piker/ui/_forms.py @@ -554,8 +554,9 @@ class FillStatusBar(QProgressBar): ) -> None: super().__init__(parent=parent) - self.approx_h = approx_height_px - self.setMinimumHeight(int(round(approx_height_px))) + self.approx_h = int(round(approx_height_px)) + self.setMinimumHeight(self.approx_h) + self.setMaximumHeight(self.approx_h) self.font_size = font_size @@ -570,10 +571,9 @@ class FillStatusBar(QProgressBar): ) -> None: - approx_h = self.approx_h # TODO: compute "used height" thus far and mostly fill the rest tot_slot_h, r = divmod( - approx_h, + self.approx_h, slots, ) slot_height_px = tot_slot_h + r/slots - self.slot_margin_px From af3d624281ce0c6c55a15c1783633c68f419ad31 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 3 Mar 2022 17:15:55 -0500 Subject: [PATCH 4/5] Just give up on discretized pp bar for now --- piker/ui/_forms.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/piker/ui/_forms.py b/piker/ui/_forms.py index e6c28112..3b33e032 100644 --- a/piker/ui/_forms.py +++ b/piker/ui/_forms.py @@ -21,6 +21,7 @@ Text entry "forms" widgets (mostly for configuration and UI user input). from __future__ import annotations from contextlib import asynccontextmanager from functools import partial +from math import floor from typing import ( Optional, Any, Callable, Awaitable ) @@ -571,17 +572,12 @@ class FillStatusBar(QProgressBar): ) -> None: - # TODO: compute "used height" thus far and mostly fill the rest - tot_slot_h, r = divmod( - self.approx_h, - slots, - ) - slot_height_px = tot_slot_h + r/slots - self.slot_margin_px - - # clipped = int(slots * tot_slot_h)# + 2*self.border_px) - # self.setMaximumHeight(clipped) - self.setOrientation(Qt.Vertical) + h = self.height() + + # TODO: compute "used height" thus far and mostly fill the rest + tot_slot_h, r = divmod(h, slots) + self.setStyleSheet( f""" QProgressBar {{ @@ -596,21 +592,27 @@ class FillStatusBar(QProgressBar): border: {self.border_px}px solid {hcolor('default_light')}; border-radius: 2px; }} - QProgressBar::chunk {{ background-color: {hcolor('default_spotlight')}; color: {hcolor('bracket')}; border-radius: 2px; - - margin: {self.slot_margin_px}px; - height: {slot_height_px}px; - }} """ ) - # sets a discrete "block" per slot + + # to set a discrete "block" per slot... + # XXX: couldn't get the discrete math to work here such + # that it was always correctly showing a discretized value + # up to the limit; not sure if it's the ``.setRange()`` + # / ``.setValue()`` api or not but i was able to get something + # close screwing with the divmod above above but after so large + # a value it would always be less chunks then the correct + # value.. + # margin: {self.slot_margin_px}px; + # height: {slot_height_px}px; + # margin-bottom: {slot_margin_px*2}px; # margin-top: {slot_margin_px*2}px; From 01f5f2d01555be03e2cab60abba6dfa93642aee5 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 3 Mar 2022 17:49:21 -0500 Subject: [PATCH 5/5] Don't require a rt quote, increase client connect timeout --- piker/brokers/ib.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index 9f1bd49b..ab440ffe 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -517,9 +517,9 @@ class Client: contract, ticker, details = await self.get_sym_details(symbol) # ensure a last price gets filled in before we deliver quote - for _ in range(1): + for _ in range(100): if isnan(ticker.last): - await asyncio.sleep(0.1) + await asyncio.sleep(0.01) log.warning(f'Quote for {symbol} timed out: market is closed?') ticker = await ticker.updateEvent else: @@ -792,7 +792,7 @@ async def load_aio_clients( try_ports = list(ports.values()) ports = try_ports if port is None else [port] # we_connected = [] - connect_timeout = 1 if platform.system() != 'Windows' else 2 + connect_timeout = 2 combos = list(itertools.product(hosts, ports)) # allocate new and/or reload disconnected but cached clients @@ -1428,8 +1428,14 @@ async def stream_quotes( ''' # TODO: support multiple subscriptions sym = symbols[0] + details: Optional[dict] = None - with trio.fail_after(16): + contract, first_ticker, details = await _trio_run_client_method( + method='get_sym_details', + symbol=sym, + ) + + with trio.move_on_after(1): contract, first_ticker, details = await _trio_run_client_method( method='get_quote', symbol=sym,