Add history chart and "linear region" for syncing

Add a first draft of a working `pyqtgraph.LinearRegionItem` link between
a history view chart (+ data set) and the normal real-time "HFT" chart
set.

Add the history view (aka more downsampled data view) chart set to the
rt/hft set's splitter as it's "first widget". Hook up linear region
callbacks to enable syncing between charts including compenstating for
the downsampling rate ration (in this case hardcoded 60 since 1s to 1M,
but we'll actually compute it going forward obvs).

More to come dawgys..
history_view
Tyler Goodlet 2022-08-30 20:15:31 -04:00
parent 9846396df2
commit bb4dc448b3
1 changed files with 71 additions and 31 deletions

View File

@ -29,7 +29,7 @@ from typing import Optional, Any, Callable
import numpy as np import numpy as np
import tractor import tractor
import trio import trio
import pendulum # import pendulum
import pyqtgraph as pg import pyqtgraph as pg
# from .. import brokers # from .. import brokers
@ -720,12 +720,15 @@ async def display_symbol_data(
) as feed: ) as feed:
ohlcv: ShmArray = feed.rt_shm ohlcv: ShmArray = feed.rt_shm
bars = ohlcv.array hist_ohlcv: ShmArray = feed.hist_shm
end_index = hist_ohlcv.index
# bars = ohlcv.array
symbol = feed.symbols[sym] symbol = feed.symbols[sym]
fqsn = symbol.front_fqsn() fqsn = symbol.front_fqsn()
times = feed.hist_shm.array['time'] # times = feed.hist_shm.array['time']
end = pendulum.from_timestamp(times[-1]) # end = pendulum.from_timestamp(times[-1])
# start = pendulum.from_timestamp(times[times != times[-1]][-1]) # start = pendulum.from_timestamp(times[times != times[-1]][-1])
# step_size_s = (end - start).seconds # step_size_s = (end - start).seconds
@ -739,7 +742,7 @@ async def display_symbol_data(
f'step:{tf_key} ' f'step:{tf_key} '
) )
linked = godwidget.linkedsplits linked = godwidget.rt_linked
linked._symbol = symbol linked._symbol = symbol
# generate order mode side-pane UI # generate order mode side-pane UI
@ -749,10 +752,27 @@ async def display_symbol_data(
# add as next-to-y-axis singleton pane # add as next-to-y-axis singleton pane
godwidget.pp_pane = pp_pane godwidget.pp_pane = pp_pane
# create top history view chart above the "main rt chart".
hist_linked = godwidget.hist_linked
hist_linked._symbol = symbol
hist_chart = hist_linked.plot_ohlc_main(
symbol,
feed.hist_shm,
# in the case of history chart we explicitly set `False`
# to avoid internal pane creation.
sidepane=False,
)
hist_chart.default_view(
bars_from_y=int(len(hist_ohlcv.array)), # size to data
y_offset=6116*2, # push it a little away from the y-axis
)
# create main OHLC chart # create main OHLC chart
chart = linked.plot_ohlc_main( chart = linked.plot_ohlc_main(
symbol, symbol,
ohlcv, ohlcv,
# in the case of history chart we explicitly set `False`
# to avoid internal pane creation.
sidepane=pp_pane, sidepane=pp_pane,
) )
@ -760,13 +780,13 @@ async def display_symbol_data(
chart._feeds[symbol.key] = feed chart._feeds[symbol.key] = feed
chart.setFocus() chart.setFocus()
# XXX: FOR SOME REASON THIS IS CAUSING HANGZ!?!
# plot historical vwap if available # plot historical vwap if available
wap_in_history = False wap_in_history = False
# if (
# XXX: FOR SOME REASON THIS IS CAUSING HANGZ!?! # brokermod._show_wap_in_history
# if brokermod._show_wap_in_history: # and 'bar_wap' in bars.dtype.fields
# ):
# if 'bar_wap' in bars.dtype.fields:
# wap_in_history = True # wap_in_history = True
# chart.draw_curve( # chart.draw_curve(
# name='bar_wap', # name='bar_wap',
@ -775,39 +795,51 @@ async def display_symbol_data(
# add_label=False, # add_label=False,
# ) # )
# size view to data once at outset
# chart.cv._set_yrange()
# Add the LinearRegionItem to the ViewBox, but tell the ViewBox # Add the LinearRegionItem to the ViewBox, but tell the ViewBox
# to exclude this item when doing auto-range calculations. # to exclude this item when doing auto-range calculations.
hist_pi = chart.plotItem rt_pi = chart.plotItem
hist_pi = hist_chart.plotItem
region = pg.LinearRegionItem() region = pg.LinearRegionItem()
region.setZValue(10) region.setZValue(10)
# hist_pi.addItem(region, ignoreBounds=True) hist_pi.addItem(region, ignoreBounds=True)
flow = chart._flows[chart.name] flow = chart._flows[hist_chart.name]
region.setClipItem(flow.graphics) 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)
def update(): def update():
region.setZValue(10) region.setZValue(10)
minX, maxX = region.getRegion() mn, mx = region.getRegion()
# p1.setXRange(minX, maxX, padding=0) # print(f'region_x: {(mn, mx)}')
# XXX: seems to cause a real perf hit?
rt_pi.setXRange(
(mn - end_index) * 60,
(mx - end_index) * 60,
padding=0,
)
region.sigRegionChanged.connect(update) region.sigRegionChanged.connect(update)
def update_region_from_pi( def update_region_from_pi(
window, window,
viewRange: tuple[tuple, tuple], viewRange: tuple[tuple, tuple],
) -> None: ) -> None:
# set the region on the history chart # set the region on the history chart
# to the range currently viewed in the # to the range currently viewed in the
# HFT/real-time chart. # HFT/real-time chart.
rgn = viewRange[0] rgn = viewRange[0]
# region.setRegion(rgn) # print(f'rt_view_range: {rgn}')
mn, mx = rgn
region.setRegion((
mn/60 + end_index,
mx/60 + end_index,
))
# connect region to be updated on plotitem interaction. # connect region to be updated on plotitem interaction.
hist_pi.sigRangeChanged.connect(update_region_from_pi) rt_pi.sigRangeChanged.connect(update_region_from_pi)
# causes recursion error right now!?..
# region.setRegion([l, r])
# 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
@ -816,6 +848,11 @@ async def display_symbol_data(
linked.focus() linked.focus()
await trio.sleep(0) await trio.sleep(0)
linked.splitter.insertWidget(0, hist_linked)
# XXX: if we wanted it at the bottom?
# linked.splitter.addWidget(hist_linked)
linked.focus()
vlm_chart: Optional[ChartPlotWidget] = None vlm_chart: Optional[ChartPlotWidget] = None
async with trio.open_nursery() as ln: async with trio.open_nursery() as ln:
@ -851,8 +888,9 @@ async def display_symbol_data(
) )
await trio.sleep(0) await trio.sleep(0)
# size view to data prior to order mode init
chart.default_view() chart.default_view()
l, lbar, rbar, r = chart.bars_range()
async with ( async with (
open_order_mode( open_order_mode(
@ -863,12 +901,14 @@ async def display_symbol_data(
) )
): ):
if not vlm_chart: if not vlm_chart:
# trigger another view reset if no sub-chart
chart.default_view() chart.default_view()
# let Qt run to render all widgets and make sure the # let Qt run to render all widgets and make sure the
# sidepanes line up vertically. # sidepanes line up vertically.
await trio.sleep(0) await trio.sleep(0)
linked.resize_sidepanes() linked.resize_sidepanes()
linked.set_split_sizes()
# NOTE: we pop the volume chart from the subplots set so # NOTE: we pop the volume chart from the subplots set so
# that it isn't double rendered in the display loop # that it isn't double rendered in the display loop