Fix overlayed slow chart "treading"

Turns out we were updating the wrong ``Viz``/``DisplayState`` inside the
closure style `increment_history_view()`` (probably due to looping
through the flumes and dynamically closing in that task-func).. Instead
define the history incrementer at module level and pass in the
`DisplayState` explicitly. Further rework the `DisplayState` attrs to be
more focused around the `Viz` associated with the fast and slow chart
and be sure to adjust output from each `Viz.incr_info()` call to latest
update. Oh, and just tweaked the line palette for the moment.

FYI "treading" here is referring to  the x-shifting of the curve when
the last datum is in view such that on new sampled appends the "last"
datum is kept in the same x-location in UI terms.
epoch_indexing_and_dataviz_layer
Tyler Goodlet 2022-12-19 17:11:34 -05:00
parent b6521498f4
commit efbb8e86d4
1 changed files with 101 additions and 109 deletions

View File

@ -43,12 +43,14 @@ from ..data.types import Struct
from ..data._sharedmem import (
ShmArray,
)
from ..data._sampling import _tick_groups
from ._axes import YAxisLabel
from ._chart import (
ChartPlotWidget,
LinkedSplits,
GodWidget,
)
from ._dataviz import Viz
from ._l1 import L1Labels
from ._style import hcolor
from ._fsp import (
@ -63,7 +65,6 @@ from ._forms import (
)
from . import _pg_overrides as pgo
# from ..data._source import tf_in_1s
from ..data._sampling import _tick_groups
from .order_mode import (
open_order_mode,
OrderMode,
@ -138,11 +139,12 @@ class DisplayState(Struct):
maxmin: Callable
flume: Flume
ohlcv: ShmArray
hist_ohlcv: ShmArray
# high level chart handles
# high level chart handles and underlying ``Viz``
chart: ChartPlotWidget
viz: Viz
hist_chart: ChartPlotWidget
hist_viz: Viz
# axis labels
l1: L1Labels
@ -178,6 +180,56 @@ class DisplayState(Struct):
wap_in_history: bool = False
async def increment_history_view(
ds: DisplayState,
):
hist_chart = ds.hist_chart
hist_viz = ds.hist_viz
assert 'hist' in hist_viz.shm.token['shm_name']
# TODO: seems this is more reliable at keeping the slow
# chart incremented in view more correctly?
# - It might make sense to just inline this logic with the
# main display task? => it's a tradeoff of slower task
# wakeups/ctx switches verus logic checks (as normal)
# - we need increment logic that only does the view shift
# call when the uppx permits/needs it
async with hist_viz.flume.index_stream(int(1)) as istream:
async for msg in istream:
# l3 = ds.viz.shm.array[-3:]
# print(
# f'fast step for {ds.flume.symbol.fqsn}:\n'
# f'{list(l3["time"])}\n'
# f'{l3}\n'
# )
# check if slow chart needs an x-domain shift and/or
# y-range resize.
(
uppx,
liv,
do_append,
i_diff_t,
append_diff,
do_rt_update,
should_tread,
) = hist_viz.incr_info(
ds=ds,
is_1m=True,
)
if (
do_append
and liv
):
hist_viz.plot.vb._set_yrange()
# check if tread-in-place x-shift is needed
if should_tread:
hist_chart.increment_view(datums=append_diff)
async def graphics_update_loop(
nurse: trio.Nursery,
@ -216,8 +268,8 @@ async def graphics_update_loop(
# per-multichart-set such that automatic x-domain shifts are only
# done once per time step update.
globalz = {
'i_last': 0, # multiview-global fast (1s) step index
'i_last_slow': 0, # multiview-global slow (1m) step index
'i_last_t': 0, # multiview-global fast (1s) step index
'i_last_slow_t': 0, # multiview-global slow (1m) step index
}
dss: dict[str, DisplayState] = {}
@ -293,25 +345,29 @@ async def graphics_update_loop(
tick_margin = 3 * tick_size
fast_chart.show()
last_quote = time.time()
i_last: float = 0
last_quote_s = time.time()
dss[fqsn] = ds = linked.display_state = DisplayState(**{
'godwidget': godwidget,
'quotes': {},
'maxmin': maxmin,
'flume': flume,
'ohlcv': ohlcv,
'hist_ohlcv': hist_ohlcv,
'chart': fast_chart,
'viz': fast_viz,
'last_price_sticky': last_price_sticky,
'hist_chart': hist_chart,
'hist_viz': hist_viz,
'hist_last_price_sticky': hist_last_price_sticky,
'l1': l1,
'vars': {
'tick_margin': tick_margin,
'i_last': i_last,
'i_last_append': i_last,
'i_last': 0,
'i_last_append': 0,
'last_mx_vlm': last_mx_vlm,
'last_mx': last_mx,
'last_mn': last_mn,
@ -327,72 +383,25 @@ async def graphics_update_loop(
fast_chart.default_view()
ds.hist_vars.update({
'i_last_append': i_last,
'i_last': i_last,
})
# ds.hist_vars.update({
# 'i_last_append': 0,
# 'i_last': 0,
# })
# TODO: probably factor this into some kinda `DisplayState`
# API that can be reused at least in terms of pulling view
# params (eg ``.bars_range()``).
async def increment_history_view():
_, hist_step_size_s, _ = flume.get_ds_info()
nurse.start_soon(
increment_history_view,
ds,
)
async with flume.index_stream(
# int(hist_step_size_s)
# TODO: seems this is more reliable at keeping the slow
# chart incremented in view more correctly?
# - It might make sense to just inline this logic with the
# main display task? => it's a tradeoff of slower task
# wakeups/ctx switches verus logic checks (as normal)
# - we need increment logic that only does the view shift
# call when the uppx permits/needs it
int(1),
) as istream:
async for msg in istream:
# check if slow chart needs an x-domain shift and/or
# y-range resize.
(
uppx,
liv,
do_append,
i_diff,
append_diff,
do_rt_update,
should_incr,
) = hist_viz.incr_info(
state=ds,
is_1m=True,
)
# print(
# f'liv: {liv}\n'
# f'do_append: {do_append}\n'
# f'append_diff: {append_diff}\n'
# )
if (
do_append
and liv
):
viz = hist_chart._vizs[fqsn]
viz.plot.vb._set_yrange(
# yrange=hist_chart.maxmin(name=fqsn)
)
# hist_chart.view._set_yrange(yrange=hist_chart.maxmin())
if should_incr:
hist_chart.increment_view(steps=i_diff)
nurse.start_soon(increment_history_view)
if ds.hist_vars['i_last'] < ds.hist_vars['i_last_append']:
breakpoint()
# main real-time quotes update loop
stream: tractor.MsgStream
async with feed.open_multi_stream() as stream:
assert stream
async for quotes in stream:
quote_period = time.time() - last_quote
quote_period = time.time() - last_quote_s
quote_rate = round(
1/quote_period, 1) if quote_period > 0 else float('inf')
if (
@ -405,7 +414,7 @@ async def graphics_update_loop(
):
log.warning(f'High quote rate {symbol.key}: {quote_rate}')
last_quote = time.time()
last_quote_s = time.time()
for sym, quote in quotes.items():
ds = dss[sym]
@ -473,22 +482,21 @@ def graphics_update_cycle(
# rt "HFT" chart
l1 = ds.l1
# ohlcv = ds.ohlcv
ohlcv = flume.rt_shm
array = ohlcv.array
vars = ds.vars
tick_margin = vars['tick_margin']
varz = ds.vars
tick_margin = varz['tick_margin']
(
uppx,
liv,
do_append,
i_diff,
i_diff_t,
append_diff,
do_rt_update,
should_incr,
) = main_viz.incr_info(state=ds)
should_tread,
) = main_viz.incr_info(ds=ds)
# TODO: we should only run mxmn when we know
# an update is due via ``do_append`` above.
@ -503,20 +511,8 @@ def graphics_update_cycle(
mn = mn_in_view - tick_margin
profiler('`ds.maxmin()` call')
if (
prepend_update_index is not None
and lbar > prepend_update_index
):
# on a history update (usually from the FSP subsys)
# if the segment of history that is being prepended
# isn't in view there is no reason to do a graphics
# update.
log.info('Skipping prepend graphics cycle: frame not in view')
return
# TODO: eventually we want to separate out the utrade (aka
# dark vlm prices) here and show them as an additional
# graphic.
# TODO: eventually we want to separate out the dark vlm and show
# them as an additional graphic.
clear_types = _tick_groups['clears']
# update ohlc sampled price bars
@ -542,22 +538,19 @@ def graphics_update_cycle(
# left unless we get one of the following:
if (
(
should_incr
should_tread
and do_append
and liv
)
or trigger_all
):
# print(f'INCREMENTING {fqsn}')
chart.increment_view(steps=i_diff)
main_viz.plot.vb._set_yrange(
# yrange=(mn, mx),
)
chart.increment_view(datums=append_diff)
main_viz.plot.vb._set_yrange()
# NOTE: since vlm and ohlc charts are axis linked now we don't
# need the double increment request?
# if vlm_chart:
# vlm_chart.increment_view(steps=i_diff)
# vlm_chart.increment_view(datums=append_diff)
profiler('view incremented')
@ -656,7 +649,7 @@ def graphics_update_cycle(
l1.bid_label.update_fields({'level': price, 'size': size})
# check for y-range re-size
if (mx > vars['last_mx']) or (mn < vars['last_mn']):
if (mx > varz['last_mx']) or (mn < varz['last_mn']):
# fast chart resize case
if (
@ -692,16 +685,14 @@ def graphics_update_cycle(
_,
_,
) = hist_viz.incr_info(
state=ds,
ds=ds,
is_1m=True,
)
if hist_liv:
hist_viz.plot.vb._set_yrange(
# yrange=hist_chart.maxmin(name=fqsn),
)
hist_viz.plot.vb._set_yrange()
# XXX: update this every draw cycle to make L1-always-in-view work.
vars['last_mx'], vars['last_mn'] = mx, mn
varz['last_mx'], varz['last_mn'] = mx, mn
# run synchronous update on all linked viz
# TODO: should the "main" (aka source) viz be special?
@ -730,7 +721,7 @@ def graphics_update_cycle(
):
viz.draw_last(
array_key=curve_name,
# only_last_uppx=True,
only_last_uppx=True,
)
# volume chart logic..
@ -774,7 +765,7 @@ def graphics_update_cycle(
profiler('`vlm_chart.update_graphics_from_flow()`')
if (
mx_vlm_in_view != vars['last_mx_vlm']
mx_vlm_in_view != varz['last_mx_vlm']
):
yrange = (0, mx_vlm_in_view * 1.375)
vlm_chart.view._set_yrange(
@ -782,7 +773,7 @@ def graphics_update_cycle(
)
profiler('`vlm_chart.view._set_yrange()`')
# print(f'mx vlm: {last_mx_vlm} -> {mx_vlm_in_view}')
vars['last_mx_vlm'] = mx_vlm_in_view
varz['last_mx_vlm'] = mx_vlm_in_view
# update all downstream FSPs
for curve_name, viz in vlm_vizs.items():
@ -1114,10 +1105,11 @@ async def display_symbol_data(
# - gradient in "lightness" based on liquidity, or lifetime in derivs?
palette = itertools.cycle([
# curve color, last bar curve color
['i3', 'gray'],
['grayer', 'bracket'],
['grayest', 'i3'],
['default_dark', 'default'],
['grayer', 'bracket'],
['i3', 'gray'],
])
pis: dict[str, list[pgo.PlotItem, pgo.PlotItem]] = {}