Compare commits
10 Commits
19136f66a0
...
ff58421565
Author | SHA1 | Date |
---|---|---|
Tyler Goodlet | ff58421565 | |
Tyler Goodlet | c2bc01e1ac | |
Tyler Goodlet | 9e5170033b | |
Tyler Goodlet | 2e0e222f27 | |
Tyler Goodlet | 0d45495a18 | |
Tyler Goodlet | 43c08018ad | |
Tyler Goodlet | 518d3a9c55 | |
Tyler Goodlet | 185090f08f | |
Tyler Goodlet | 36fb8abe9d | |
Tyler Goodlet | d62aa071ae |
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue