Add initial history (view) to charting sys
Adds an additional `GodWidget.hist_linked: LinkedSplits` alongside the renamed `.rt_linked` to enable 2 sets of linked charts with different sampled data sets/flows. The history set is added without "all the fixins" for now (i.e. no order mode sidepane or search integration) such that it is merely a top level chart which shows a much longer term history and can be added to the UI via embedding the entire history linked-splits instance into the real-time linked set's splitter. Further impl deats: - adjust the `GodWidget._chart_cache: dict[str, tuple]]` to store both linked split chart sets per symbol so that symbol switching will continue to work with the added history chart (set). - rework `.load_symbol()` to operate on both the real-time (HFT) chart set and the history set. - rework `LinkedSplits.set_split_sizes()` to compensate for the history chart and do more detailed height calcs arithmetic to make it appear by default as a minor sub-chart. - adjust `LinkedSplits.add_plot()` and `ChartPlotWidget` internals to allow adding a plot without a sidepane and/or container `ChartnPane` composite widget by checking for a `sidepane == False` input. - make `.default_view()` accept a manual y-axis offset kwarg. - adjust search mode to provide history linked splits to `.set_chart_symbol()` call.history_view
							parent
							
								
									f0d417ce42
								
							
						
					
					
						commit
						9846396df2
					
				| 
						 | 
					@ -115,7 +115,9 @@ class GodWidget(QWidget):
 | 
				
			||||||
        # self.vbox.addLayout(self.hbox)
 | 
					        # self.vbox.addLayout(self.hbox)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._chart_cache: dict[str, LinkedSplits] = {}
 | 
					        self._chart_cache: dict[str, LinkedSplits] = {}
 | 
				
			||||||
        self.linkedsplits: Optional[LinkedSplits] = None
 | 
					
 | 
				
			||||||
 | 
					        self.hist_linked: Optional[LinkedSplits] = None
 | 
				
			||||||
 | 
					        self.rt_linked: Optional[LinkedSplits] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # assigned in the startup func `_async_main()`
 | 
					        # assigned in the startup func `_async_main()`
 | 
				
			||||||
        self._root_n: trio.Nursery = None
 | 
					        self._root_n: trio.Nursery = None
 | 
				
			||||||
| 
						 | 
					@ -123,6 +125,10 @@ class GodWidget(QWidget):
 | 
				
			||||||
        self._widgets: dict[str, QWidget] = {}
 | 
					        self._widgets: dict[str, QWidget] = {}
 | 
				
			||||||
        self._resizing: bool = False
 | 
					        self._resizing: bool = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def linkedsplits(self) -> LinkedSplits:
 | 
				
			||||||
 | 
					        return self.rt_linked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # def init_timeframes_ui(self):
 | 
					    # def init_timeframes_ui(self):
 | 
				
			||||||
    #     self.tf_layout = QHBoxLayout()
 | 
					    #     self.tf_layout = QHBoxLayout()
 | 
				
			||||||
    #     self.tf_layout.setSpacing(0)
 | 
					    #     self.tf_layout.setSpacing(0)
 | 
				
			||||||
| 
						 | 
					@ -148,19 +154,19 @@ class GodWidget(QWidget):
 | 
				
			||||||
    def set_chart_symbol(
 | 
					    def set_chart_symbol(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        symbol_key: str,  # of form <fqsn>.<providername>
 | 
					        symbol_key: str,  # of form <fqsn>.<providername>
 | 
				
			||||||
        linkedsplits: LinkedSplits,  # type: ignore
 | 
					        all_linked: tuple[LinkedSplits, LinkedSplits],  # type: ignore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        # re-sort org cache symbol list in LIFO order
 | 
					        # re-sort org cache symbol list in LIFO order
 | 
				
			||||||
        cache = self._chart_cache
 | 
					        cache = self._chart_cache
 | 
				
			||||||
        cache.pop(symbol_key, None)
 | 
					        cache.pop(symbol_key, None)
 | 
				
			||||||
        cache[symbol_key] = linkedsplits
 | 
					        cache[symbol_key] = all_linked
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_chart_symbol(
 | 
					    def get_chart_symbol(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        symbol_key: str,
 | 
					        symbol_key: str,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> LinkedSplits:  # type: ignore
 | 
					    ) -> tuple[LinkedSplits, LinkedSplits]:  # type: ignore
 | 
				
			||||||
        return self._chart_cache.get(symbol_key)
 | 
					        return self._chart_cache.get(symbol_key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def load_symbol(
 | 
					    async def load_symbol(
 | 
				
			||||||
| 
						 | 
					@ -182,28 +188,30 @@ class GodWidget(QWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # fully qualified symbol name (SNS i guess is what we're making?)
 | 
					        # fully qualified symbol name (SNS i guess is what we're making?)
 | 
				
			||||||
        fqsn = '.'.join([symbol_key, providername])
 | 
					        fqsn = '.'.join([symbol_key, providername])
 | 
				
			||||||
 | 
					        all_linked = self.get_chart_symbol(fqsn)
 | 
				
			||||||
        linkedsplits = self.get_chart_symbol(fqsn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        order_mode_started = trio.Event()
 | 
					        order_mode_started = trio.Event()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.vbox.isEmpty():
 | 
					        if not self.vbox.isEmpty():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for linked in [self.rt_linked, self.hist_linked]:
 | 
				
			||||||
            # XXX: this is CRITICAL especially with pixel buffer caching
 | 
					            # XXX: this is CRITICAL especially with pixel buffer caching
 | 
				
			||||||
            self.linkedsplits.hide()
 | 
					                linked.hide()
 | 
				
			||||||
            self.linkedsplits.unfocus()
 | 
					                linked.unfocus()
 | 
				
			||||||
 | 
					                # self.hist_linked.hide()
 | 
				
			||||||
 | 
					                # self.hist_linked.unfocus()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # XXX: pretty sure we don't need this
 | 
					                # XXX: pretty sure we don't need this
 | 
				
			||||||
            # remove any existing plots?
 | 
					                # remove any existing plots?
 | 
				
			||||||
            # XXX: ahh we might want to support cache unloading..
 | 
					                # XXX: ahh we might want to support cache unloading..
 | 
				
			||||||
            # self.vbox.removeWidget(self.linkedsplits)
 | 
					                # self.vbox.removeWidget(linked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # switching to a new viewable chart
 | 
					        # switching to a new viewable chart
 | 
				
			||||||
        if linkedsplits is None or reset:
 | 
					        if all_linked is None or reset:
 | 
				
			||||||
            from ._display import display_symbol_data
 | 
					            from ._display import display_symbol_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # we must load a fresh linked charts set
 | 
					            # we must load a fresh linked charts set
 | 
				
			||||||
            linkedsplits = LinkedSplits(self)
 | 
					            self.rt_linked = rt_charts = LinkedSplits(self)
 | 
				
			||||||
 | 
					            self.hist_linked = hist_charts = LinkedSplits(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # spawn new task to start up and update new sub-chart instances
 | 
					            # spawn new task to start up and update new sub-chart instances
 | 
				
			||||||
            self._root_n.start_soon(
 | 
					            self._root_n.start_soon(
 | 
				
			||||||
| 
						 | 
					@ -215,44 +223,53 @@ class GodWidget(QWidget):
 | 
				
			||||||
                order_mode_started,
 | 
					                order_mode_started,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.set_chart_symbol(fqsn, linkedsplits)
 | 
					            # self.vbox.addWidget(hist_charts)
 | 
				
			||||||
            self.vbox.addWidget(linkedsplits)
 | 
					            self.vbox.addWidget(rt_charts)
 | 
				
			||||||
 | 
					            self.set_chart_symbol(
 | 
				
			||||||
 | 
					                fqsn,
 | 
				
			||||||
 | 
					                (hist_charts, rt_charts),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for linked in [hist_charts, rt_charts]:
 | 
				
			||||||
 | 
					                linked.show()
 | 
				
			||||||
 | 
					                linked.focus()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            linkedsplits.show()
 | 
					 | 
				
			||||||
            linkedsplits.focus()
 | 
					 | 
				
			||||||
            await trio.sleep(0)
 | 
					            await trio.sleep(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            # symbol is already loaded and ems ready
 | 
					            # symbol is already loaded and ems ready
 | 
				
			||||||
            order_mode_started.set()
 | 
					            order_mode_started.set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # TODO:
 | 
					            self.hist_linked, self.rt_linked = all_linked
 | 
				
			||||||
            # - we'll probably want per-instrument/provider state here?
 | 
					 | 
				
			||||||
            #   change the order config form over to the new chart
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # chart is already in memory so just focus it
 | 
					            for linked in all_linked:
 | 
				
			||||||
            linkedsplits.show()
 | 
					                # TODO:
 | 
				
			||||||
            linkedsplits.focus()
 | 
					                # - we'll probably want per-instrument/provider state here?
 | 
				
			||||||
            linkedsplits.graphics_cycle()
 | 
					                #   change the order config form over to the new chart
 | 
				
			||||||
            await trio.sleep(0)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # XXX: since the pp config is a singleton widget we have to
 | 
					                # chart is already in memory so just focus it
 | 
				
			||||||
            # also switch it over to the new chart's interal-layout
 | 
					                linked.show()
 | 
				
			||||||
            # self.linkedsplits.chart.qframe.hbox.removeWidget(self.pp_pane)
 | 
					                linked.focus()
 | 
				
			||||||
            chart = linkedsplits.chart
 | 
					                linked.graphics_cycle()
 | 
				
			||||||
 | 
					                await trio.sleep(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # resume feeds *after* rendering chart view asap
 | 
					                # XXX: since the pp config is a singleton widget we have to
 | 
				
			||||||
            if chart:
 | 
					                # also switch it over to the new chart's interal-layout
 | 
				
			||||||
                chart.resume_all_feeds()
 | 
					                # linked.chart.qframe.hbox.removeWidget(self.pp_pane)
 | 
				
			||||||
 | 
					                chart = linked.chart
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # TODO: we need a check to see if the chart
 | 
					                # resume feeds *after* rendering chart view asap
 | 
				
			||||||
                # last had the xlast in view, if so then shift so it's
 | 
					                if chart:
 | 
				
			||||||
                # still in view, if the user was viewing history then
 | 
					                    chart.resume_all_feeds()
 | 
				
			||||||
                # do nothing yah?
 | 
					 | 
				
			||||||
                chart.default_view()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.linkedsplits = linkedsplits
 | 
					                    # TODO: we need a check to see if the chart
 | 
				
			||||||
        symbol = linkedsplits.symbol
 | 
					                    # last had the xlast in view, if so then shift so it's
 | 
				
			||||||
 | 
					                    # still in view, if the user was viewing history then
 | 
				
			||||||
 | 
					                    # do nothing yah?
 | 
				
			||||||
 | 
					                    chart.default_view()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set window titlebar info
 | 
				
			||||||
 | 
					        symbol = self.rt_linked.symbol
 | 
				
			||||||
        if symbol is not None:
 | 
					        if symbol is not None:
 | 
				
			||||||
            self.window.setWindowTitle(
 | 
					            self.window.setWindowTitle(
 | 
				
			||||||
                f'{symbol.front_fqsn()} '
 | 
					                f'{symbol.front_fqsn()} '
 | 
				
			||||||
| 
						 | 
					@ -399,10 +416,18 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
            # elif ln >= 2:
 | 
					            # elif ln >= 2:
 | 
				
			||||||
            #     prop = 3/8
 | 
					            #     prop = 3/8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        major = 1 - prop
 | 
					        h = self.height()
 | 
				
			||||||
        min_h_ind = int((self.height() * prop) / ln)
 | 
					        histview_h = h * 1.6/6
 | 
				
			||||||
 | 
					        h = h - histview_h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sizes = [int(self.height() * major)]
 | 
					        major = 1 - prop
 | 
				
			||||||
 | 
					        min_h_ind = int((h * prop) / ln)
 | 
				
			||||||
 | 
					        sizes = [
 | 
				
			||||||
 | 
					            int(histview_h),
 | 
				
			||||||
 | 
					            int(h * major),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # give all subcharts the same remaining proportional height
 | 
				
			||||||
        sizes.extend([min_h_ind] * ln)
 | 
					        sizes.extend([min_h_ind] * ln)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.splitter.setSizes(sizes)
 | 
					        self.splitter.setSizes(sizes)
 | 
				
			||||||
| 
						 | 
					@ -498,10 +523,15 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
            'bottom': xaxis,
 | 
					            'bottom': xaxis,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        qframe = ChartnPane(
 | 
					        if sidepane is not False:
 | 
				
			||||||
            sidepane=sidepane,
 | 
					            parent = qframe = ChartnPane(
 | 
				
			||||||
            parent=self.splitter,
 | 
					                sidepane=sidepane,
 | 
				
			||||||
        )
 | 
					                parent=self.splitter,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            parent = self.splitter
 | 
				
			||||||
 | 
					            qframe = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cpw = ChartPlotWidget(
 | 
					        cpw = ChartPlotWidget(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # this name will be used to register the primary
 | 
					            # this name will be used to register the primary
 | 
				
			||||||
| 
						 | 
					@ -509,7 +539,7 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
            name=name,
 | 
					            name=name,
 | 
				
			||||||
            data_key=array_key or name,
 | 
					            data_key=array_key or name,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            parent=qframe,
 | 
					            parent=parent,
 | 
				
			||||||
            linkedsplits=self,
 | 
					            linkedsplits=self,
 | 
				
			||||||
            axisItems=axes,
 | 
					            axisItems=axes,
 | 
				
			||||||
            **cpw_kwargs,
 | 
					            **cpw_kwargs,
 | 
				
			||||||
| 
						 | 
					@ -537,20 +567,22 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
            self.xaxis_chart = cpw
 | 
					            self.xaxis_chart = cpw
 | 
				
			||||||
            cpw.showAxis('bottom')
 | 
					            cpw.showAxis('bottom')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        qframe.chart = cpw
 | 
					        if qframe is not None:
 | 
				
			||||||
        qframe.hbox.addWidget(cpw)
 | 
					            qframe.chart = cpw
 | 
				
			||||||
 | 
					            qframe.hbox.addWidget(cpw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # so we can look this up and add back to the splitter
 | 
					            # so we can look this up and add back to the splitter
 | 
				
			||||||
        # on a symbol switch
 | 
					            # on a symbol switch
 | 
				
			||||||
        cpw.qframe = qframe
 | 
					            cpw.qframe = qframe
 | 
				
			||||||
        assert cpw.parent() == qframe
 | 
					            assert cpw.parent() == qframe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # add sidepane **after** chart; place it on axis side
 | 
					            # add sidepane **after** chart; place it on axis side
 | 
				
			||||||
        qframe.hbox.addWidget(
 | 
					            qframe.hbox.addWidget(
 | 
				
			||||||
            sidepane,
 | 
					                sidepane,
 | 
				
			||||||
            alignment=Qt.AlignTop
 | 
					                alignment=Qt.AlignTop
 | 
				
			||||||
        )
 | 
					            )
 | 
				
			||||||
        cpw.sidepane = sidepane
 | 
					
 | 
				
			||||||
 | 
					            cpw.sidepane = sidepane
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cpw.plotItem.vb.linkedsplits = self
 | 
					        cpw.plotItem.vb.linkedsplits = self
 | 
				
			||||||
        cpw.setFrameStyle(
 | 
					        cpw.setFrameStyle(
 | 
				
			||||||
| 
						 | 
					@ -613,7 +645,9 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
        if not _is_main:
 | 
					        if not _is_main:
 | 
				
			||||||
            # track by name
 | 
					            # track by name
 | 
				
			||||||
            self.subplots[name] = cpw
 | 
					            self.subplots[name] = cpw
 | 
				
			||||||
            self.splitter.addWidget(qframe)
 | 
					            if qframe is not None:
 | 
				
			||||||
 | 
					                self.splitter.addWidget(qframe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # scale split regions
 | 
					            # scale split regions
 | 
				
			||||||
            self.set_split_sizes()
 | 
					            self.set_split_sizes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -648,7 +682,7 @@ class LinkedSplits(QWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        main_chart = self.chart
 | 
					        main_chart = self.chart
 | 
				
			||||||
        if main_chart:
 | 
					        if main_chart and main_chart.sidepane:
 | 
				
			||||||
            sp_w = main_chart.sidepane.width()
 | 
					            sp_w = main_chart.sidepane.width()
 | 
				
			||||||
            for name, cpw in self.subplots.items():
 | 
					            for name, cpw in self.subplots.items():
 | 
				
			||||||
                cpw.sidepane.setMinimumWidth(sp_w)
 | 
					                cpw.sidepane.setMinimumWidth(sp_w)
 | 
				
			||||||
| 
						 | 
					@ -711,6 +745,7 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # NOTE: must be set bfore calling ``.mk_vb()``
 | 
					        # NOTE: must be set bfore calling ``.mk_vb()``
 | 
				
			||||||
        self.linked = linkedsplits
 | 
					        self.linked = linkedsplits
 | 
				
			||||||
 | 
					        self.sidepane: Optional[FieldsForm] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # source of our custom interactions
 | 
					        # source of our custom interactions
 | 
				
			||||||
        self.cv = cv = self.mk_vb(name)
 | 
					        self.cv = cv = self.mk_vb(name)
 | 
				
			||||||
| 
						 | 
					@ -867,7 +902,8 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def default_view(
 | 
					    def default_view(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        bars_from_y: int = 616,
 | 
					        bars_from_y: int = int(616 * 3/8),
 | 
				
			||||||
 | 
					        y_offset: int = 0,
 | 
				
			||||||
        do_ds: bool = True,
 | 
					        do_ds: bool = True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
| 
						 | 
					@ -906,8 +942,12 @@ class ChartPlotWidget(pg.PlotWidget):
 | 
				
			||||||
        # terms now that we've scaled either by user control
 | 
					        # terms now that we've scaled either by user control
 | 
				
			||||||
        # or to the default set of bars as per the immediate block
 | 
					        # or to the default set of bars as per the immediate block
 | 
				
			||||||
        # above.
 | 
					        # above.
 | 
				
			||||||
        marker_pos, l1_len = self.pre_l1_xs()
 | 
					        if not y_offset:
 | 
				
			||||||
        end = xlast + l1_len + 1
 | 
					            marker_pos, l1_len = self.pre_l1_xs()
 | 
				
			||||||
 | 
					            end = xlast + l1_len + 1
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            end = xlast + y_offset + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        begin = end - (r - l)
 | 
					        begin = end - (r - l)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # for debugging
 | 
					        # for debugging
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -635,7 +635,12 @@ class SearchWidget(QtWidgets.QWidget):
 | 
				
			||||||
            # Re-order the symbol cache on the chart to display in
 | 
					            # Re-order the symbol cache on the chart to display in
 | 
				
			||||||
            # LIFO order. this is normally only done internally by
 | 
					            # LIFO order. this is normally only done internally by
 | 
				
			||||||
            # the chart on new symbols being loaded into memory
 | 
					            # the chart on new symbols being loaded into memory
 | 
				
			||||||
            chart.set_chart_symbol(fqsn, chart.linkedsplits)
 | 
					            chart.set_chart_symbol(
 | 
				
			||||||
 | 
					                fqsn, (
 | 
				
			||||||
 | 
					                    chart.linkedsplits,
 | 
				
			||||||
 | 
					                    self.godwidget.hist_linked,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.view.set_section_entries(
 | 
					            self.view.set_section_entries(
 | 
				
			||||||
                'cache',
 | 
					                'cache',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue