Pack multi-chart region linking into helper
Factor the multi-sample-rate region UI connecting into a new helper `link_views_with_region()` which reads in the shm buffer offsets from the `Feed` and appropriately connects the fast and slow chart handlers for the linear region graphics. Add detailed comments writeup for the inter-sampling transform algebra.ib_1m_hist
parent
0000d9a314
commit
5b63585398
|
@ -49,7 +49,9 @@ from ._fsp import (
|
||||||
has_vlm,
|
has_vlm,
|
||||||
open_vlm_displays,
|
open_vlm_displays,
|
||||||
)
|
)
|
||||||
from ..data._sharedmem import ShmArray
|
from ..data._sharedmem import (
|
||||||
|
ShmArray,
|
||||||
|
)
|
||||||
from ..data._source import tf_in_1s
|
from ..data._source import tf_in_1s
|
||||||
from ._forms import (
|
from ._forms import (
|
||||||
FieldsForm,
|
FieldsForm,
|
||||||
|
@ -807,6 +809,140 @@ def graphics_update_cycle(
|
||||||
flow.draw_last(array_key=curve_name)
|
flow.draw_last(array_key=curve_name)
|
||||||
|
|
||||||
|
|
||||||
|
async def link_views_with_region(
|
||||||
|
rt_chart: ChartPlotWidget,
|
||||||
|
hist_chart: ChartPlotWidget,
|
||||||
|
feed: Feed,
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
# these value are be only pulled once during shm init/startup
|
||||||
|
izero_hist = feed.izero_hist
|
||||||
|
izero_rt = feed.izero_rt
|
||||||
|
|
||||||
|
# Add the LinearRegionItem to the ViewBox, but tell the ViewBox
|
||||||
|
# to exclude this item when doing auto-range calculations.
|
||||||
|
rt_pi = rt_chart.plotItem
|
||||||
|
hist_pi = hist_chart.plotItem
|
||||||
|
|
||||||
|
region = pg.LinearRegionItem(
|
||||||
|
movable=False,
|
||||||
|
# color scheme that matches sidepane styling
|
||||||
|
pen=pg.mkPen(hcolor('gunmetal')),
|
||||||
|
brush=pg.mkBrush(hcolor('default_darkest')),
|
||||||
|
)
|
||||||
|
region.setZValue(10) # put linear region "in front" in layer terms
|
||||||
|
|
||||||
|
hist_pi.addItem(region, ignoreBounds=True)
|
||||||
|
|
||||||
|
flow = rt_chart._flows[hist_chart.name]
|
||||||
|
assert flow
|
||||||
|
|
||||||
|
# XXX: no idea why this doesn't work but it's causing
|
||||||
|
# a weird placement of the region on the way-far-left..
|
||||||
|
# region.setClipItem(flow.graphics)
|
||||||
|
|
||||||
|
# poll for datums load and timestep detection
|
||||||
|
for _ in range(100):
|
||||||
|
try:
|
||||||
|
_, _, ratio = feed.get_ds_info()
|
||||||
|
break
|
||||||
|
except IndexError:
|
||||||
|
await trio.sleep(0.01)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Failed to detect sampling periods from shm!?')
|
||||||
|
|
||||||
|
# sampling rate transform math:
|
||||||
|
# -----------------------------
|
||||||
|
# define the fast chart to slow chart as a linear mapping
|
||||||
|
# over the fast index domain `i` to the slow index domain
|
||||||
|
# `j` as:
|
||||||
|
#
|
||||||
|
# j = i - i_offset
|
||||||
|
# ------------ + j_offset
|
||||||
|
# j/i
|
||||||
|
#
|
||||||
|
# conversely the inverse function is:
|
||||||
|
#
|
||||||
|
# i = j/i * (j - j_offset) + i_offset
|
||||||
|
#
|
||||||
|
# Where `j_offset` is our ``izero_hist`` and `i_offset` is our
|
||||||
|
# `izero_rt`, the ``ShmArray`` offsets which correspond to the
|
||||||
|
# indexes in each array where the "current" time is indexed at init.
|
||||||
|
# AKA the index where new data is "appended to" and historical data
|
||||||
|
# if "prepended from".
|
||||||
|
#
|
||||||
|
# more practically (and by default) `i` is normally an index
|
||||||
|
# into 1s samples and `j` is an index into 60s samples (aka 1m).
|
||||||
|
# in the below handlers ``ratio`` is the `j/i` and ``mn``/``mx``
|
||||||
|
# are the low and high index input from the source index domain.
|
||||||
|
|
||||||
|
def update_region_from_pi(
|
||||||
|
window,
|
||||||
|
viewRange: tuple[tuple, tuple],
|
||||||
|
is_manual: bool = True,
|
||||||
|
|
||||||
|
) -> None:
|
||||||
|
# put linear region "in front" in layer terms
|
||||||
|
region.setZValue(10)
|
||||||
|
|
||||||
|
# set the region on the history chart
|
||||||
|
# to the range currently viewed in the
|
||||||
|
# HFT/real-time chart.
|
||||||
|
mn, mx = viewRange[0]
|
||||||
|
ds_mn = (mn - izero_rt)/ratio
|
||||||
|
ds_mx = (mx - izero_rt)/ratio
|
||||||
|
lhmn = ds_mn + izero_hist
|
||||||
|
lhmx = ds_mx + izero_hist
|
||||||
|
# print(
|
||||||
|
# f'rt_view_range: {(mn, mx)}\n'
|
||||||
|
# f'ds_mn, ds_mx: {(ds_mn, ds_mx)}\n'
|
||||||
|
# f'lhmn, lhmx: {(lhmn, lhmx)}\n'
|
||||||
|
# )
|
||||||
|
region.setRegion((
|
||||||
|
lhmn,
|
||||||
|
lhmx,
|
||||||
|
))
|
||||||
|
|
||||||
|
# TODO: if we want to have the slow chart adjust range to
|
||||||
|
# match the fast chart's selection -> results in the
|
||||||
|
# linear region expansion never can go "outside of view".
|
||||||
|
# hmn, hmx = hvr = hist_chart.view.state['viewRange'][0]
|
||||||
|
# print((hmn, hmx))
|
||||||
|
# if (
|
||||||
|
# hvr
|
||||||
|
# and (lhmn < hmn or lhmx > hmx)
|
||||||
|
# ):
|
||||||
|
# hist_pi.setXRange(
|
||||||
|
# lhmn,
|
||||||
|
# lhmx,
|
||||||
|
# padding=0,
|
||||||
|
# )
|
||||||
|
# hist_linked.graphics_cycle()
|
||||||
|
|
||||||
|
# connect region to be updated on plotitem interaction.
|
||||||
|
rt_pi.sigRangeChanged.connect(update_region_from_pi)
|
||||||
|
|
||||||
|
def update_pi_from_region():
|
||||||
|
region.setZValue(10)
|
||||||
|
mn, mx = region.getRegion()
|
||||||
|
# print(f'region_x: {(mn, mx)}')
|
||||||
|
rt_pi.setXRange(
|
||||||
|
((mn - izero_hist) * ratio) + izero_rt,
|
||||||
|
((mx - izero_hist) * ratio) + izero_rt,
|
||||||
|
padding=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO BUG XXX: seems to cause a real perf hit and a recursion error
|
||||||
|
# (but used to work before generalizing for 1s ohlc offset?)..
|
||||||
|
# something to do with the label callback handlers?
|
||||||
|
|
||||||
|
# region.sigRegionChanged.connect(update_pi_from_region)
|
||||||
|
# region.sigRegionChangeFinished.connect(update_pi_from_region)
|
||||||
|
|
||||||
|
|
||||||
async def display_symbol_data(
|
async def display_symbol_data(
|
||||||
godwidget: GodWidget,
|
godwidget: GodWidget,
|
||||||
provider: str,
|
provider: str,
|
||||||
|
@ -851,10 +987,6 @@ async def display_symbol_data(
|
||||||
ohlcv: ShmArray = feed.rt_shm
|
ohlcv: ShmArray = feed.rt_shm
|
||||||
hist_ohlcv: ShmArray = feed.hist_shm
|
hist_ohlcv: ShmArray = feed.hist_shm
|
||||||
|
|
||||||
# this value needs to be pulled once and only once during
|
|
||||||
# startup
|
|
||||||
end_index = feed.startup_hist_index
|
|
||||||
|
|
||||||
symbol = feed.symbols[sym]
|
symbol = feed.symbols[sym]
|
||||||
fqsn = symbol.front_fqsn()
|
fqsn = symbol.front_fqsn()
|
||||||
|
|
||||||
|
@ -918,91 +1050,6 @@ async def display_symbol_data(
|
||||||
# add_label=False,
|
# add_label=False,
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# Add the LinearRegionItem to the ViewBox, but tell the ViewBox
|
|
||||||
# to exclude this item when doing auto-range calculations.
|
|
||||||
rt_pi = chart.plotItem
|
|
||||||
hist_pi = hist_chart.plotItem
|
|
||||||
region = pg.LinearRegionItem(
|
|
||||||
# color scheme that matches sidepane styling
|
|
||||||
pen=pg.mkPen(hcolor('gunmetal')),
|
|
||||||
brush=pg.mkBrush(hcolor('default_darkest')),
|
|
||||||
)
|
|
||||||
region.setZValue(10) # put linear region "in front" in layer terms
|
|
||||||
hist_pi.addItem(region, ignoreBounds=True)
|
|
||||||
flow = chart._flows[hist_chart.name]
|
|
||||||
assert flow
|
|
||||||
# XXX: no idea why this doesn't work but it's causing
|
|
||||||
# a weird placement of the region on the way-far-left..
|
|
||||||
# region.setClipItem(flow.graphics)
|
|
||||||
|
|
||||||
# poll for datums load and timestep detection
|
|
||||||
for _ in range(100):
|
|
||||||
try:
|
|
||||||
_, _, ratio = feed.get_ds_info()
|
|
||||||
break
|
|
||||||
except IndexError:
|
|
||||||
await trio.sleep(0.01)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
'Failed to detect sampling periods from shm!?')
|
|
||||||
|
|
||||||
def update_pi_from_region():
|
|
||||||
region.setZValue(10)
|
|
||||||
mn, mx = region.getRegion()
|
|
||||||
# print(f'region_x: {(mn, mx)}')
|
|
||||||
|
|
||||||
# XXX: seems to cause a real perf hit?
|
|
||||||
rt_pi.setXRange(
|
|
||||||
(mn - end_index) * ratio,
|
|
||||||
(mx - end_index) * ratio,
|
|
||||||
padding=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
region.sigRegionChanged.connect(update_pi_from_region)
|
|
||||||
|
|
||||||
def update_region_from_pi(
|
|
||||||
window,
|
|
||||||
viewRange: tuple[tuple, tuple],
|
|
||||||
is_manual: bool = True,
|
|
||||||
|
|
||||||
) -> None:
|
|
||||||
# set the region on the history chart
|
|
||||||
# to the range currently viewed in the
|
|
||||||
# HFT/real-time chart.
|
|
||||||
mn, mx = viewRange[0]
|
|
||||||
ds_mn = mn/ratio
|
|
||||||
ds_mx = mx/ratio
|
|
||||||
# print(
|
|
||||||
# f'rt_view_range: {(mn, mx)}\n'
|
|
||||||
# f'ds_mn, ds_mx: {(ds_mn, ds_mx)}\n'
|
|
||||||
# )
|
|
||||||
lhmn = ds_mn + end_index
|
|
||||||
lhmx = ds_mx + end_index
|
|
||||||
region.setRegion((
|
|
||||||
lhmn,
|
|
||||||
lhmx,
|
|
||||||
))
|
|
||||||
|
|
||||||
# TODO: if we want to have the slow chart adjust range to
|
|
||||||
# match the fast chart's selection -> results in the
|
|
||||||
# linear region expansion never can go "outside of view".
|
|
||||||
# hmn, hmx = hvr = hist_chart.view.state['viewRange'][0]
|
|
||||||
# print((hmn, hmx))
|
|
||||||
# if (
|
|
||||||
# hvr
|
|
||||||
# and (lhmn < hmn or lhmx > hmx)
|
|
||||||
# ):
|
|
||||||
# hist_pi.setXRange(
|
|
||||||
# lhmn,
|
|
||||||
# lhmx,
|
|
||||||
# padding=0,
|
|
||||||
# )
|
|
||||||
# hist_linked.graphics_cycle()
|
|
||||||
|
|
||||||
# connect region to be updated on plotitem interaction.
|
|
||||||
rt_pi.sigRangeChanged.connect(update_region_from_pi)
|
|
||||||
|
|
||||||
# NOTE: we must immediately tell Qt to show the OHLC chart
|
# NOTE: we must immediately tell Qt to show the OHLC chart
|
||||||
# to avoid a race where the subplots get added/shown to
|
# to avoid a race where the subplots get added/shown to
|
||||||
# the linked set *before* the main price chart!
|
# the linked set *before* the main price chart!
|
||||||
|
@ -1070,6 +1117,12 @@ async def display_symbol_data(
|
||||||
|
|
||||||
godwidget.resize_all()
|
godwidget.resize_all()
|
||||||
|
|
||||||
|
await link_views_with_region(
|
||||||
|
chart,
|
||||||
|
hist_chart,
|
||||||
|
feed,
|
||||||
|
)
|
||||||
|
|
||||||
mode: OrderMode
|
mode: OrderMode
|
||||||
async with (
|
async with (
|
||||||
open_order_mode(
|
open_order_mode(
|
||||||
|
|
Loading…
Reference in New Issue