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.pre_viz_calls
							parent
							
								
									3f24805075
								
							
						
					
					
						commit
						4dc8051853
					
				| 
						 | 
				
			
			@ -41,12 +41,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 (
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +63,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,
 | 
			
		||||
| 
						 | 
				
			
			@ -136,11 +137,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
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +174,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,
 | 
			
		||||
| 
						 | 
				
			
			@ -210,8 +262,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] = {}
 | 
			
		||||
| 
						 | 
				
			
			@ -287,25 +339,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,
 | 
			
		||||
| 
						 | 
				
			
			@ -321,72 +377,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()
 | 
			
		||||
 | 
			
		||||
            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,
 | 
			
		||||
        nurse.start_soon(
 | 
			
		||||
            increment_history_view,
 | 
			
		||||
            ds,
 | 
			
		||||
        )
 | 
			
		||||
                    # 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 (
 | 
			
		||||
| 
						 | 
				
			
			@ -399,7 +408,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]
 | 
			
		||||
| 
						 | 
				
			
			@ -467,22 +476,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.
 | 
			
		||||
| 
						 | 
				
			
			@ -497,20 +505,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
 | 
			
		||||
| 
						 | 
				
			
			@ -536,22 +532,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')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -650,7 +643,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 (
 | 
			
		||||
| 
						 | 
				
			
			@ -686,16 +679,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?
 | 
			
		||||
| 
						 | 
				
			
			@ -724,7 +715,7 @@ def graphics_update_cycle(
 | 
			
		|||
        ):
 | 
			
		||||
            viz.draw_last(
 | 
			
		||||
                array_key=curve_name,
 | 
			
		||||
                # only_last_uppx=True,
 | 
			
		||||
                only_last_uppx=True,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    # volume chart logic..
 | 
			
		||||
| 
						 | 
				
			
			@ -768,7 +759,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(
 | 
			
		||||
| 
						 | 
				
			
			@ -776,7 +767,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():
 | 
			
		||||
| 
						 | 
				
			
			@ -1108,10 +1099,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]] = {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue