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