Compare commits

...

10 Commits

Author SHA1 Message Date
Tyler Goodlet ff58421565 Only draw last uppx cols worth if xy cached on fmtr 2023-02-05 20:14:37 -05:00
Tyler Goodlet c2bc01e1ac Go back to drawing all `Viz`s per chart? 2023-02-05 20:14:36 -05:00
Tyler Goodlet 9e5170033b Pass windowed y-mxmn to `.interact_graphics_cycle()` calls in display loop 2023-02-03 14:01:55 -05:00
Tyler Goodlet 2e0e222f27 Allow y-range input via a `yranges: dict[Viz, tuple[float, float]]` 2023-02-03 14:00:52 -05:00
Tyler Goodlet 0d45495a18 Don't unset `Viz.render` for unit vlm
Such that we still y-range auto-sort inside
`ChartView.interact_graphics_cycle()` still runs on the unit vlm axis
and we always size such that the y-label stays in view.
2023-02-03 11:30:36 -05:00
Tyler Goodlet 43c08018ad Fix profiler f-string 2023-02-03 11:30:36 -05:00
Tyler Goodlet 518d3a9c55 Update profile msgs to new apis 2023-02-03 11:30:36 -05:00
Tyler Goodlet 185090f08f Move axis hiding into `.overlay_plotitem()`
Since we pretty much always want the 'bottom' and any side that is not
declared by the caller move the axis hides into this method. Lets us
drop the same calls in `.ui._fsp` and `._display`.

This also disables the auto-ranging back-linking for now since it
doesn't seem to be working quite yet?
2023-02-03 11:30:36 -05:00
Tyler Goodlet 36fb8abe9d Better handle dynamic registry sampler broadcasts
In situations where clients are (dynamically) subscribing *while*
broadcasts are starting to taking place we need to handle the
`set`-modified-during-iteration case. This scenario seems to be more
common during races on concurrent startup of multiple symbols. The
solution here is to use another set to take note of subscribers which
are successfully sent-to and then skipping them on re-try.

This also contains an attempt to exception-handle throttled stream
overruns caused by higher frequency feeds (like binance) pushing more
quotes then can be handled during (UI) client startup.
2023-02-03 08:13:19 -05:00
Tyler Goodlet d62aa071ae Drop old loop and wait on fsp engine tasks startups 2023-02-03 08:13:19 -05:00
7 changed files with 197 additions and 176 deletions

View File

@ -253,20 +253,30 @@ class Sampler:
# f'consumers: {subs}' # f'consumers: {subs}'
) )
borked: set[tractor.MsgStream] = set() borked: set[tractor.MsgStream] = set()
for stream in subs: sent: set[tractor.MsgStream] = set()
while True:
try: try:
await stream.send({ for stream in (subs - sent):
'index': time_stamp or last_ts, try:
'period': period_s, await stream.send({
}) 'index': time_stamp or last_ts,
except ( 'period': period_s,
trio.BrokenResourceError, })
trio.ClosedResourceError sent.add(stream)
):
log.error( except (
f'{stream._ctx.chan.uid} dropped connection' trio.BrokenResourceError,
) trio.ClosedResourceError
borked.add(stream) ):
log.error(
f'{stream._ctx.chan.uid} dropped connection'
)
borked.add(stream)
else:
break
except RuntimeError:
log.warning(f'Client subs {subs} changed while broadcasting')
continue
for stream in borked: for stream in borked:
try: try:
@ -848,6 +858,16 @@ async def uniform_rate_send(
# rate timing exactly lul # rate timing exactly lul
try: try:
await stream.send({sym: first_quote}) await stream.send({sym: first_quote})
except tractor.RemoteActorError as rme:
if rme.type is not tractor._exceptions.StreamOverrun:
raise
ctx = stream._ctx
chan = ctx.chan
log.warning(
'Throttled quote-stream overrun!\n'
f'{sym}:{ctx.cid}@{chan.uid}'
)
except ( except (
# NOTE: any of these can be raised by ``tractor``'s IPC # NOTE: any of these can be raised by ``tractor``'s IPC
# transport-layer and we want to be highly resilient # transport-layer and we want to be highly resilient

View File

@ -1589,6 +1589,9 @@ async def open_feed(
(brokermod, bfqsns), (brokermod, bfqsns),
) in zip(ctxs, providers.items()): ) in zip(ctxs, providers.items()):
# NOTE: do it asap to avoid overruns during multi-feed setup?
ctx._backpressure = backpressure
for fqsn, flume_msg in flumes_msg_dict.items(): for fqsn, flume_msg in flumes_msg_dict.items():
flume = Flume.from_msg(flume_msg) flume = Flume.from_msg(flume_msg)
assert flume.symbol.fqsn == fqsn assert flume.symbol.fqsn == fqsn

View File

@ -634,6 +634,7 @@ class LinkedSplits(QWidget):
axis.pi = cpw.plotItem axis.pi = cpw.plotItem
cpw.hideAxis('left') cpw.hideAxis('left')
# cpw.removeAxis('left')
cpw.hideAxis('bottom') cpw.hideAxis('bottom')
if ( if (
@ -750,12 +751,12 @@ class LinkedSplits(QWidget):
# NOTE: back-link the new sub-chart to trigger y-autoranging in # NOTE: back-link the new sub-chart to trigger y-autoranging in
# the (ohlc parent) main chart for this linked set. # the (ohlc parent) main chart for this linked set.
if self.chart: # if self.chart:
main_viz = self.chart.get_viz(self.chart.name) # main_viz = self.chart.get_viz(self.chart.name)
self.chart.view.enable_auto_yrange( # self.chart.view.enable_auto_yrange(
src_vb=cpw.view, # src_vb=cpw.view,
viz=main_viz, # viz=main_viz,
) # )
graphics = viz.graphics graphics = viz.graphics
data_key = viz.name data_key = viz.name
@ -1106,6 +1107,12 @@ class ChartPlotWidget(pg.PlotWidget):
pi.chart_widget = self pi.chart_widget = self
pi.hideButtons() pi.hideButtons()
# hide all axes not named by ``axis_side``
for axname in (
({'bottom'} | allowed_sides) - {axis_side}
):
pi.hideAxis(axname)
# compose this new plot's graphics with the current chart's # compose this new plot's graphics with the current chart's
# existing one but with separate axes as neede and specified. # existing one but with separate axes as neede and specified.
self.pi_overlay.add_plotitem( self.pi_overlay.add_plotitem(
@ -1209,17 +1216,21 @@ class ChartPlotWidget(pg.PlotWidget):
pi = overlay pi = overlay
if add_sticky: if add_sticky:
axis = pi.getAxis(add_sticky)
if pi.name not in axis._stickies:
if pi is not self.plotItem: if pi is not self.plotItem:
overlay = self.pi_overlay # overlay = self.pi_overlay
assert pi in overlay.overlays # assert pi in overlay.overlays
overlay_axis = overlay.get_axis( overlay = self.pi_overlay
pi, assert pi in overlay.overlays
add_sticky, axis = overlay.get_axis(
) pi,
assert overlay_axis is axis add_sticky,
)
else:
axis = pi.getAxis(add_sticky)
if pi.name not in axis._stickies:
# TODO: UGH! just make this not here! we should # TODO: UGH! just make this not here! we should
# be making the sticky from code which has access # be making the sticky from code which has access

View File

@ -934,16 +934,17 @@ class Viz(msgspec.Struct): # , frozen=True):
# the most recent datum is being drawn. # the most recent datum is being drawn.
uppx = ceil(gfx.x_uppx()) uppx = ceil(gfx.x_uppx())
alt_renderer = self._alt_r
if alt_renderer:
renderer, gfx = alt_renderer
else:
renderer = self._src_r
if ( if (
(self._in_ds or only_last_uppx) (self._in_ds or only_last_uppx)
and uppx > 0 and uppx > 0
and renderer is not None
): ):
alt_renderer = self._alt_r
if alt_renderer:
renderer, gfx = alt_renderer
else:
renderer = self._src_r
fmtr = renderer.fmtr fmtr = renderer.fmtr
x = fmtr.x_1d x = fmtr.x_1d
y = fmtr.y_1d y = fmtr.y_1d
@ -952,23 +953,24 @@ class Viz(msgspec.Struct): # , frozen=True):
if alt_renderer: if alt_renderer:
iuppx = ceil(uppx / fmtr.flat_index_ratio) iuppx = ceil(uppx / fmtr.flat_index_ratio)
y = y[-iuppx:] if y is not None:
ymn, ymx = y.min(), y.max() y = y[-iuppx:]
try: ymn, ymx = y.min(), y.max()
x_start = x[-iuppx] try:
except IndexError: x_start = x[-iuppx]
# we're less then an x-px wide so just grab the start except IndexError:
# datum index. # we're less then an x-px wide so just grab the start
x_start = x[0] # datum index.
x_start = x[0]
gfx._last_line = QLineF( gfx._last_line = QLineF(
x_start, ymn, x_start, ymn,
x[-1], ymx, x[-1], ymx,
) )
# print( # print(
# f'updating DS curve {self.name}@{time_step}s\n' # f'updating DS curve {self.name}@{time_step}s\n'
# f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}' # f'drawing uppx={uppx} mxmn line: {ymn}, {ymx}'
# ) # )
else: else:
x, y = gfx.draw_last_datum( x, y = gfx.draw_last_datum(

View File

@ -144,12 +144,11 @@ def multi_maxmin(
profiler(f'vlm_viz.maxmin({read_slc})') profiler(f'vlm_viz.maxmin({read_slc})')
return ( return (
mx,
# enforcing price can't be negative? # enforcing price can't be negative?
# TODO: do we even need this? # TODO: do we even need this?
max(mn, 0), max(mn, 0),
mx,
mx_vlm_in_view, # vlm max mx_vlm_in_view, # vlm max
) )
@ -348,8 +347,8 @@ async def graphics_update_loop(
vlm_viz = vlm_chart._vizs.get('volume') if vlm_chart else None vlm_viz = vlm_chart._vizs.get('volume') if vlm_chart else None
( (
last_mx,
last_mn, last_mn,
last_mx,
last_mx_vlm, last_mx_vlm,
) = multi_maxmin( ) = multi_maxmin(
None, None,
@ -377,7 +376,7 @@ async def graphics_update_loop(
# present differently -> likely dark vlm # present differently -> likely dark vlm
tick_size = symbol.tick_size tick_size = symbol.tick_size
tick_margin = 3 * tick_size tick_margin = 4 * tick_size
fast_chart.show() fast_chart.show()
last_quote_s = time.time() last_quote_s = time.time()
@ -544,8 +543,14 @@ def graphics_update_cycle(
# them as an additional graphic. # them as an additional graphic.
clear_types = _tick_groups['clears'] clear_types = _tick_groups['clears']
mx = varz['last_mx'] # TODO: fancier y-range sorting..
mn = varz['last_mn'] # https://github.com/pikers/piker/issues/325
# - a proper streaming mxmn algo as per above issue.
# - we should probably scale the view margin based on the size of
# the true range? This way you can slap in orders outside the
# current L1 (only) book range.
mx = lmx = varz['last_mx']
mn = lmn = varz['last_mn']
mx_vlm_in_view = varz['last_mx_vlm'] mx_vlm_in_view = varz['last_mx_vlm']
# update ohlc sampled price bars # update ohlc sampled price bars
@ -555,24 +560,12 @@ def graphics_update_cycle(
(liv and do_px_step) (liv and do_px_step)
or trigger_all or trigger_all
): ):
# TODO: i think we're double calling this right now
# since .interact_graphics_cycle() also calls it?
# I guess we can add a guard in there?
_, i_read_range, _ = main_viz.update_graphics() _, i_read_range, _ = main_viz.update_graphics()
profiler('`Viz.update_graphics()` call') profiler('`Viz.update_graphics()` call')
(
mx_in_view,
mn_in_view,
mx_vlm_in_view,
) = multi_maxmin(
i_read_range,
main_viz,
ds.vlm_viz,
profiler,
)
mx = mx_in_view + tick_margin
mn = mn_in_view - tick_margin
profiler('{fqsdn} `multi_maxmin()` call')
# don't real-time "shift" the curve to the # don't real-time "shift" the curve to the
# left unless we get one of the following: # left unless we get one of the following:
if ( if (
@ -588,6 +581,23 @@ def graphics_update_cycle(
profiler('view incremented') profiler('view incremented')
# NOTE: do this **after** the tread to ensure we take the yrange
# from the most current view x-domain.
(
mn_in_view,
mx_in_view,
mx_vlm_in_view,
) = multi_maxmin(
i_read_range,
main_viz,
ds.vlm_viz,
profiler,
)
mx = mx_in_view + tick_margin
mn = mn_in_view - tick_margin
profiler(f'{fqsn} `multi_maxmin()` call')
# iterate frames of ticks-by-type such that we only update graphics # iterate frames of ticks-by-type such that we only update graphics
# using the last update per type where possible. # using the last update per type where possible.
ticks_by_type = quote.get('tbt', {}) ticks_by_type = quote.get('tbt', {})
@ -673,14 +683,10 @@ def graphics_update_cycle(
# Y-autoranging: adjust y-axis limits based on state tracking # Y-autoranging: adjust y-axis limits based on state tracking
# of previous "last" L1 values which are in view. # of previous "last" L1 values which are in view.
lmx = varz['last_mx']
lmn = varz['last_mn']
mx_diff = mx - lmx
mn_diff = mn - lmn mn_diff = mn - lmn
mx_diff = mx - lmx
if ( if (
mx_diff mx_diff or mn_diff
or mn_diff
): ):
# complain about out-of-range outliers which can show up # complain about out-of-range outliers which can show up
# in certain annoying feeds (like ib).. # in certain annoying feeds (like ib)..
@ -699,7 +705,12 @@ def graphics_update_cycle(
f'mn_diff: {mn_diff}\n' f'mn_diff: {mn_diff}\n'
) )
# FAST CHART resize case # TODO: track local liv maxmin without doing a recompute all the
# time..plus, just generally the user is more likely to be
# zoomed out enough on the slow chart that this is never an
# issue (the last datum going out of y-range).
# FAST CHART y-auto-range resize case
elif ( elif (
liv liv
and not chart._static_yrange == 'axis' and not chart._static_yrange == 'axis'
@ -710,22 +721,15 @@ def graphics_update_cycle(
main_vb._ic is None main_vb._ic is None
or not main_vb._ic.is_set() or not main_vb._ic.is_set()
): ):
# TODO: incremenal update of the median # print(f'SETTING Y-mxmx -> {main_viz.name}: {(mn, mx)}')
# and maxmin driving the y-autoranging.
# yr = (mn, mx)
main_vb.interact_graphics_cycle( main_vb.interact_graphics_cycle(
# do_overlay_scaling=False, # do_overlay_scaling=False,
do_linked_charts=False, do_linked_charts=False,
yranges={main_viz: (mn, mx)},
) )
# TODO: we should probably scale
# the view margin based on the size
# of the true range? This way you can
# slap in orders outside the current
# L1 (only) book range.
profiler('main vb y-autorange') profiler('main vb y-autorange')
# SLOW CHART resize case # SLOW CHART y-auto-range resize case
( (
_, _,
hist_liv, hist_liv,
@ -740,10 +744,6 @@ def graphics_update_cycle(
) )
profiler('hist `Viz.incr_info()`') profiler('hist `Viz.incr_info()`')
# TODO: track local liv maxmin without doing a recompute all the
# time..plut, just generally the user is more likely to be
# zoomed out enough on the slow chart that this is never an
# issue (the last datum going out of y-range).
# hist_chart = ds.hist_chart # hist_chart = ds.hist_chart
# if ( # if (
# hist_liv # hist_liv
@ -758,7 +758,8 @@ def graphics_update_cycle(
# XXX: update this every draw cycle to ensure y-axis auto-ranging # XXX: update this every draw cycle to ensure y-axis auto-ranging
# only adjusts when the in-view data co-domain actually expands or # only adjusts when the in-view data co-domain actually expands or
# contracts. # contracts.
varz['last_mx'], varz['last_mn'] = mx, mn varz['last_mn'] = mn
varz['last_mx'] = mx
# TODO: a similar, only-update-full-path-on-px-step approach for all # TODO: a similar, only-update-full-path-on-px-step approach for all
# fsp overlays and vlm stuff.. # fsp overlays and vlm stuff..
@ -766,10 +767,12 @@ def graphics_update_cycle(
# run synchronous update on all `Viz` overlays # run synchronous update on all `Viz` overlays
for curve_name, viz in chart._vizs.items(): for curve_name, viz in chart._vizs.items():
if viz.is_ohlc:
continue
# update any overlayed fsp flows # update any overlayed fsp flows
if ( if (
curve_name != fqsn curve_name != fqsn
and not viz.is_ohlc
): ):
update_fsp_chart( update_fsp_chart(
viz, viz,
@ -777,25 +780,25 @@ def graphics_update_cycle(
array_key=curve_name, array_key=curve_name,
) )
# even if we're downsampled bigly # even if we're downsampled bigly
# draw the last datum in the final # draw the last datum in the final
# px column to give the user the mx/mn # px column to give the user the mx/mn
# range of that set. # range of that set.
if ( if (
curve_name != fqsn curve_name != fqsn
and liv and liv
# and not do_px_step # and not do_px_step
# and not do_rt_update # and not do_rt_update
): ):
viz.draw_last( viz.draw_last(
array_key=curve_name, array_key=curve_name,
# TODO: XXX this is currently broken for the # TODO: XXX this is currently broken for the
# `FlattenedOHLC` case since we aren't returning the # `FlattenedOHLC` case since we aren't returning the
# full x/y uppx's worth of src-data from # full x/y uppx's worth of src-data from
# `draw_last_datum()` .. # `draw_last_datum()` ..
only_last_uppx=True, only_last_uppx=True,
) )
profiler('overlays updates') profiler('overlays updates')
@ -885,7 +888,9 @@ def graphics_update_cycle(
fvb.interact_graphics_cycle( fvb.interact_graphics_cycle(
do_linked_charts=False, do_linked_charts=False,
) )
profiler(f'vlm `Viz[{viz.name}].plot.vb._set_yrange()`') profiler(
f'Viz[{viz.name}].plot.vb.interact_graphics_cycle()`'
)
# even if we're downsampled bigly # even if we're downsampled bigly
# draw the last datum in the final # draw the last datum in the final
@ -1314,13 +1319,6 @@ async def display_symbol_data(
name=fqsn, name=fqsn,
axis_title=fqsn, axis_title=fqsn,
) )
# only show a singleton bottom-bottom axis by default.
hist_pi.hideAxis('bottom')
# XXX: TODO: THIS WILL CAUSE A GAP ON OVERLAYS,
# i think it needs to be "removed" instead when there
# are none?
hist_pi.hideAxis('left')
hist_viz = hist_chart.draw_curve( hist_viz = hist_chart.draw_curve(
fqsn, fqsn,
@ -1356,9 +1354,6 @@ async def display_symbol_data(
axis_title=fqsn, axis_title=fqsn,
) )
rt_pi.hideAxis('left')
rt_pi.hideAxis('bottom')
rt_viz = rt_chart.draw_curve( rt_viz = rt_chart.draw_curve(
fqsn, fqsn,
ohlcv, ohlcv,

View File

@ -608,6 +608,7 @@ async def open_vlm_displays(
linked: LinkedSplits, linked: LinkedSplits,
flume: Flume, flume: Flume,
dvlm: bool = True, dvlm: bool = True,
loglevel: str = 'info',
task_status: TaskStatus[ChartPlotWidget] = trio.TASK_STATUS_IGNORED, task_status: TaskStatus[ChartPlotWidget] = trio.TASK_STATUS_IGNORED,
@ -690,7 +691,7 @@ async def open_vlm_displays(
# the axis on the left it's totally not lined up... # the axis on the left it's totally not lined up...
# show volume units value on LHS (for dinkus) # show volume units value on LHS (for dinkus)
# vlm_chart.hideAxis('right') # vlm_chart.hideAxis('right')
# vlm_chart.showAxis('left') vlm_chart.hideAxis('left')
# send back new chart to caller # send back new chart to caller
task_status.started(vlm_chart) task_status.started(vlm_chart)
@ -710,9 +711,9 @@ async def open_vlm_displays(
_, _, vlm_curve = vlm_viz.update_graphics() _, _, vlm_curve = vlm_viz.update_graphics()
# size view to data once at outset # size view to data once at outset
vlm_chart.view._set_yrange( # vlm_chart.view._set_yrange(
viz=vlm_viz # viz=vlm_viz
) # )
# add axis title # add axis title
axis = vlm_chart.getAxis('right') axis = vlm_chart.getAxis('right')
@ -734,22 +735,8 @@ async def open_vlm_displays(
}, },
}, },
}, },
# loglevel, loglevel,
) )
tasks_ready.append(started)
# FIXME: we should error on starting the same fsp right
# since it might collide with existing shm.. or wait we
# had this before??
# dolla_vlm
tasks_ready.append(started)
# profiler(f'created shm for fsp actor: {display_name}')
# wait for all engine tasks to startup
async with trio.open_nursery() as n:
for event in tasks_ready:
n.start_soon(event.wait)
# dolla vlm overlay # dolla vlm overlay
# XXX: the main chart already contains a vlm "units" axis # XXX: the main chart already contains a vlm "units" axis
@ -772,10 +759,6 @@ async def open_vlm_displays(
}, },
) )
# TODO: should this maybe be implicit based on input args to
# `.overlay_plotitem()` above?
dvlm_pi.hideAxis('bottom')
# all to be overlayed curve names # all to be overlayed curve names
dvlm_fields = [ dvlm_fields = [
'dolla_vlm', 'dolla_vlm',
@ -825,6 +808,7 @@ async def open_vlm_displays(
) )
assert viz.plot is pi assert viz.plot is pi
await started.wait()
chart_curves( chart_curves(
dvlm_fields, dvlm_fields,
dvlm_pi, dvlm_pi,
@ -833,19 +817,17 @@ async def open_vlm_displays(
step_mode=True, step_mode=True,
) )
# spawn flow rates fsp **ONLY AFTER** the 'dolla_vlm' fsp is # NOTE: spawn flow rates fsp **ONLY AFTER** the 'dolla_vlm' fsp is
# up since this one depends on it. # up since calculating vlm "rates" obvs first requires the
# underlying vlm event feed ;)
fr_flume, started = await admin.start_engine_task( fr_flume, started = await admin.start_engine_task(
flow_rates, flow_rates,
{ # fsp engine conf { # fsp engine conf
'func_name': 'flow_rates', 'func_name': 'flow_rates',
'zero_on_step': True, 'zero_on_step': True,
}, },
# loglevel, loglevel,
) )
await started.wait()
# chart_curves( # chart_curves(
# dvlm_rate_fields, # dvlm_rate_fields,
# dvlm_pi, # dvlm_pi,
@ -859,10 +841,15 @@ async def open_vlm_displays(
# liquidity events (well at least on low OHLC periods - 1s). # liquidity events (well at least on low OHLC periods - 1s).
vlm_curve.hide() vlm_curve.hide()
vlm_chart.removeItem(vlm_curve) vlm_chart.removeItem(vlm_curve)
# vlm_chart.plotItem.layout.setMinimumWidth(0)
# vlm_chart.removeAxis('left')
vlm_viz = vlm_chart._vizs['volume'] vlm_viz = vlm_chart._vizs['volume']
vlm_viz.render = False
# avoid range sorting on volume once disabled # NOTE: DON'T DO THIS.
# WHY: we want range sorting on volume for the RHS label!
# -> if you don't want that then use this but likely you
# only will if we decide to drop unit vlm..
# vlm_viz.render = False
vlm_chart.view.disable_auto_yrange() vlm_chart.view.disable_auto_yrange()
# Trade rate overlay # Trade rate overlay
@ -886,8 +873,8 @@ async def open_vlm_displays(
}, },
) )
tr_pi.hideAxis('bottom')
await started.wait()
chart_curves( chart_curves(
trade_rate_fields, trade_rate_fields,
tr_pi, tr_pi,

View File

@ -956,6 +956,8 @@ class ChartView(ViewBox):
debug_print: bool = False, debug_print: bool = False,
do_overlay_scaling: bool = True, do_overlay_scaling: bool = True,
do_linked_charts: bool = True, do_linked_charts: bool = True,
yranges: tuple[float, float] | None = None,
): ):
profiler = Profiler( profiler = Profiler(
msg=f'ChartView.interact_graphics_cycle() for {self.name}', msg=f'ChartView.interact_graphics_cycle() for {self.name}',
@ -1044,15 +1046,22 @@ class ChartView(ViewBox):
profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`') profiler(f'{viz.name}@{chart_name} `Viz.update_graphics()`')
out = viz.maxmin(i_read_range=i_read_range) yrange = yranges.get(viz) if yranges else None
if out is None: if yrange is not None:
log.warning(f'No yrange provided for {name}!?') # print(f'INPUT {viz.name} yrange: {yrange}')
return read_slc = slice(*i_read_range)
(
ixrng, else:
read_slc, out = viz.maxmin(i_read_range=i_read_range)
yrange if out is None:
) = out log.warning(f'No yrange provided for {name}!?')
return
(
_, # ixrng,
read_slc,
yrange
) = out
profiler(f'{viz.name}@{chart_name} `Viz.maxmin()`')
pi = viz.plot pi = viz.plot
@ -1077,19 +1086,16 @@ class ChartView(ViewBox):
): ):
ymn, ymx = yrange ymn, ymx = yrange
# print(f'adding {viz.name} to overlay') # print(f'adding {viz.name} to overlay')
# mxmn_groups[viz.name] = out
# viz = chart._vizs[viz_name]
# determine start datum in view # determine start datum in view
arr = viz.shm.array arr = viz.shm.array
in_view = arr[read_slc] in_view = arr[read_slc]
if not in_view.size:
log.warning(f'{viz.name} not in view?')
return
row_start = arr[read_slc.start - 1] row_start = arr[read_slc.start - 1]
# y_med = (ymx - ymn) / 2
# y_med = viz.median_from_range(
# read_slc.start,
# read_slc.stop,
# )
if viz.is_ohlc: if viz.is_ohlc:
y_start = row_start['open'] y_start = row_start['open']
else: else:
@ -1102,7 +1108,6 @@ class ChartView(ViewBox):
y_start, y_start,
ymn, ymn,
ymx, ymx,
# y_med,
read_slc, read_slc,
in_view, in_view,
) )
@ -1124,10 +1129,8 @@ class ChartView(ViewBox):
# y_ref = y_med # y_ref = y_med
# up_rng = (ymx - y_ref) / y_ref # up_rng = (ymx - y_ref) / y_ref
# down_rng = (ymn - y_ref) / y_ref # down_rng = (ymn - y_ref) / y_ref
# mx_up_rng = max(mx_up_rng, up_rng) # mx_up_rng = max(mx_up_rng, up_rng)
# mn_down_rng = min(mn_down_rng, down_rng) # mn_down_rng = min(mn_down_rng, down_rng)
# print( # print(
# f'{viz.name}@{chart_name} group mxmn calc\n' # f'{viz.name}@{chart_name} group mxmn calc\n'
# '--------------------\n' # '--------------------\n'
@ -1158,10 +1161,10 @@ class ChartView(ViewBox):
len(start_datums) < 2 len(start_datums) < 2
or not do_overlay_scaling or not do_overlay_scaling
): ):
# print(f'ONLY ranging major: {viz.name}')
if not major_viz: if not major_viz:
major_viz = viz major_viz = viz
# print(f'ONLY ranging major: {viz.name}')
major_viz.plot.vb._set_yrange( major_viz.plot.vb._set_yrange(
yrange=yrange, yrange=yrange,
) )