Fix sidepane alignment with FSP charts

Call the resize method only after all FSP subcharts have rendered
such that the main OHLC chart's final width is read.

Further tweaks:
- drop rsi by default
- drop the stream drain stuff
- fix failed-to-read shm logging
py3.10_support
Tyler Goodlet 2021-12-20 13:53:29 -05:00
parent 56b65a1cde
commit 644ac6661c
1 changed files with 43 additions and 48 deletions

View File

@ -61,7 +61,10 @@ log = get_logger(__name__)
_quote_throttle_rate: int = 58 # Hz _quote_throttle_rate: int = 58 # Hz
def try_read(array: np.ndarray) -> Optional[np.ndarray]: def try_read(
array: np.ndarray
) -> Optional[np.ndarray]:
''' '''
Try to read the last row from a shared mem array or ``None`` Try to read the last row from a shared mem array or ``None``
if the array read returns a zero-length array result. if the array read returns a zero-length array result.
@ -85,10 +88,9 @@ def try_read(array: np.ndarray) -> Optional[np.ndarray]:
# something we need anyway, maybe there should be some kind of # something we need anyway, maybe there should be some kind of
# signal that a prepend is taking place and this consumer can # signal that a prepend is taking place and this consumer can
# respond (eg. redrawing graphics) accordingly. # respond (eg. redrawing graphics) accordingly.
log.warning(f'Read-race on shm array: {graphics_name}@{shm.token}')
# the array read was emtpy # the array read was emtpy
return None return None
def update_fsp_chart( def update_fsp_chart(
@ -101,8 +103,10 @@ def update_fsp_chart(
array = shm.array array = shm.array
last_row = try_read(array) last_row = try_read(array)
# guard against unreadable case # guard against unreadable case
if not last_row: if not last_row:
log.warning(f'Read-race on shm array: {graphics_name}@{shm.token}')
return return
# update graphics # update graphics
@ -175,7 +179,6 @@ def chart_maxmin(
async def update_chart_from_quotes( async def update_chart_from_quotes(
linked: LinkedSplits, linked: LinkedSplits,
stream: tractor.MsgStream, stream: tractor.MsgStream,
ohlcv: np.ndarray, ohlcv: np.ndarray,
@ -247,17 +250,16 @@ async def update_chart_from_quotes(
chart.show() chart.show()
last_quote = time.time() last_quote = time.time()
# NOTE: all code below this loop is expected to be synchronous
# and thus draw instructions are not picked up jntil the next
# wait / iteration.
async for quotes in stream: async for quotes in stream:
now = time.time() now = time.time()
quote_period = now - last_quote quote_period = time.time() - last_quote
quote_rate = round(1/quote_period, 1) if quote_period else float('inf') quote_rate = round(
1/quote_period, 1) if quote_period > 0 else float('inf')
if ( if (
quote_period <= 1/_quote_throttle_rate quote_period <= 1/_quote_throttle_rate
and quote_rate > _quote_throttle_rate + 2 and quote_rate > _quote_throttle_rate * 1.5
): ):
log.warning(f'High quote rate {symbol.key}: {quote_rate}') log.warning(f'High quote rate {symbol.key}: {quote_rate}')
last_quote = now last_quote = now
@ -454,7 +456,8 @@ def maybe_mk_fsp_shm(
readonly: bool = True, readonly: bool = True,
) -> (ShmArray, bool): ) -> (ShmArray, bool):
'''Allocate a single row shm array for an symbol-fsp pair if none '''
Allocate a single row shm array for an symbol-fsp pair if none
exists, otherwise load the shm already existing for that token. exists, otherwise load the shm already existing for that token.
''' '''
@ -481,7 +484,6 @@ def maybe_mk_fsp_shm(
@acm @acm
async def open_fsp_sidepane( async def open_fsp_sidepane(
linked: LinkedSplits, linked: LinkedSplits,
conf: dict[str, dict[str, str]], conf: dict[str, dict[str, str]],
@ -570,6 +572,7 @@ async def open_fsp_cluster(
async def maybe_open_fsp_cluster( async def maybe_open_fsp_cluster(
workers: int = 2, workers: int = 2,
**kwargs, **kwargs,
) -> AsyncGenerator[int, dict[str, tractor.Portal]]: ) -> AsyncGenerator[int, dict[str, tractor.Portal]]:
kwargs.update( kwargs.update(
@ -589,7 +592,6 @@ async def maybe_open_fsp_cluster(
async def start_fsp_displays( async def start_fsp_displays(
cluster_map: dict[str, tractor.Portal], cluster_map: dict[str, tractor.Portal],
linkedsplits: LinkedSplits, linkedsplits: LinkedSplits,
fsps: dict[str, str], fsps: dict[str, str],
@ -603,7 +605,8 @@ async def start_fsp_displays(
display_in_own_task: bool = False, display_in_own_task: bool = False,
) -> None: ) -> None:
'''Create sub-actors (under flat tree) '''
Create sub-actors (under flat tree)
for each entry in config and attach to local graphics update tasks. for each entry in config and attach to local graphics update tasks.
Pass target entrypoint and historical data. Pass target entrypoint and historical data.
@ -668,9 +671,7 @@ async def start_fsp_displays(
async def update_chart_from_fsp( async def update_chart_from_fsp(
portal: tractor.Portal, portal: tractor.Portal,
linkedsplits: LinkedSplits, linkedsplits: LinkedSplits,
brokermod: ModuleType, brokermod: ModuleType,
sym: str, sym: str,
@ -687,7 +688,8 @@ async def update_chart_from_fsp(
profiler: pg.debug.Profiler, profiler: pg.debug.Profiler,
) -> None: ) -> None:
'''FSP stream chart update loop. '''
FSP stream chart update loop.
This is called once for each entry in the fsp This is called once for each entry in the fsp
config map. config map.
@ -792,9 +794,7 @@ async def update_chart_from_fsp(
level_line(chart, 80, orient_v='top') level_line(chart, 80, orient_v='top')
chart._set_yrange() chart._set_yrange()
done() # status updates
done()
chart.linked.resize_sidepanes()
profiler(f'fsp:{func_name} starting update loop') profiler(f'fsp:{func_name} starting update loop')
profiler.finish() profiler.finish()
@ -912,7 +912,6 @@ def has_vlm(ohlcv: ShmArray) -> bool:
@acm @acm
async def maybe_open_vlm_display( async def maybe_open_vlm_display(
linked: LinkedSplits, linked: LinkedSplits,
ohlcv: ShmArray, ohlcv: ShmArray,
@ -992,20 +991,10 @@ async def maybe_open_vlm_display(
# size view to data once at outset # size view to data once at outset
chart._set_yrange() chart._set_yrange()
# size pain to parent chart
# TODO: this appears to nearly fix a bug where the vlm sidepane
# could be sized correctly nearly immediately (since the
# order pane is already sized), right now it doesn't seem to
# fully align until the VWAP fsp-actor comes up...
await trio.sleep(0)
chart.linked.resize_sidepanes()
await trio.sleep(0)
yield chart yield chart
async def display_symbol_data( async def display_symbol_data(
godwidget: GodWidget, godwidget: GodWidget,
provider: str, provider: str,
sym: str, sym: str,
@ -1116,24 +1105,24 @@ async def display_symbol_data(
'chart_kwargs': {'style': 'step'} 'chart_kwargs': {'style': 'step'}
}, },
'rsi': { # 'rsi': {
'func_name': 'rsi', # literal python func ref lookup name # 'func_name': 'rsi', # literal python func ref lookup name
# map of parameters to place on the fsp sidepane widget # # map of parameters to place on the fsp sidepane widget
# which should map to dynamic inputs available to the # # which should map to dynamic inputs available to the
# fsp function at runtime. # # fsp function at runtime.
'params': { # 'params': {
'period': { # 'period': {
'default_value': 14, # 'default_value': 14,
'widget_kwargs': {'readonly': True}, # 'widget_kwargs': {'readonly': True},
}, # },
}, # },
# ``ChartPlotWidget`` options passthrough # # ``ChartPlotWidget`` options passthrough
'chart_kwargs': { # 'chart_kwargs': {
'static_yrange': (0, 100), # 'static_yrange': (0, 100),
}, # },
}, # },
} }
if has_vlm(ohlcv): # and provider != 'binance': if has_vlm(ohlcv): # and provider != 'binance':
@ -1201,4 +1190,10 @@ async def display_symbol_data(
order_mode_started order_mode_started
) )
): ):
# let Qt run to render all widgets and make sure the
# sidepanes line up vertically.
await trio.sleep(0)
linkedsplits.resize_sidepanes()
# let the app run.
await trio.sleep_forever() await trio.sleep_forever()