Adjust range logic to avoid overlap with labels
By mapping any in view "contents labels" to the range of the ``ViewBox``'s data we can avoid having graphics overlap with labels. Take this approach instead of specifying a min y-range using the std and activate the range compute on resize and mouser scrolling. Also, add y-sticky update for signal plots.bar_select
							parent
							
								
									fc0a03d597
								
							
						
					
					
						commit
						80f191c57d
					
				|  | @ -1,7 +1,7 @@ | |||
| """ | ||||
| High level Qt chart widgets. | ||||
| """ | ||||
| from typing import Tuple, Dict, Any | ||||
| from typing import Tuple, Dict, Any, Optional | ||||
| import time | ||||
| 
 | ||||
| from PyQt5 import QtCore, QtGui | ||||
|  | @ -274,6 +274,7 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|         self, | ||||
|         # the data view we generate graphics from | ||||
|         array: np.ndarray, | ||||
|         yrange: Optional[Tuple[float, float]] = None, | ||||
|         **kwargs, | ||||
|     ): | ||||
|         """Configure chart display settings. | ||||
|  | @ -282,12 +283,15 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|             background=hcolor('papas_special'), | ||||
|             # parent=None, | ||||
|             # plotItem=None, | ||||
|             # useOpenGL=True, | ||||
|             **kwargs | ||||
|         ) | ||||
|         self._array = array  # readonly view of data | ||||
|         self._graphics = {}  # registry of underlying graphics | ||||
|         self._labels = {}  # registry of underlying graphics | ||||
|         self._ysticks = {}  # registry of underlying graphics | ||||
|         self._yrange = yrange | ||||
|         self._vb = self.plotItem.vb | ||||
| 
 | ||||
|         # show only right side axes | ||||
|         self.hideAxis('left') | ||||
|  | @ -301,10 +305,16 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|         # use cross-hair for cursor | ||||
|         self.setCursor(QtCore.Qt.CrossCursor) | ||||
| 
 | ||||
|         # assign callback for rescaling y-axis automatically | ||||
|         # based on ohlc contents | ||||
|         # Assign callback for rescaling y-axis automatically | ||||
|         # based on data contents and ``ViewBox`` state. | ||||
|         self.sigXRangeChanged.connect(self._set_yrange) | ||||
| 
 | ||||
|         vb = self._vb | ||||
|         # for mouse wheel which doesn't seem to emit XRangeChanged | ||||
|         vb.sigRangeChangedManually.connect(self._set_yrange) | ||||
|         # for when the splitter(s) are resized | ||||
|         vb.sigResized.connect(self._set_yrange) | ||||
| 
 | ||||
|     def _update_contents_label(self, index: int) -> None: | ||||
|         if index > 0 and index < len(self._array): | ||||
|             for name, (label, update) in self._labels.items(): | ||||
|  | @ -364,9 +374,10 @@ class ChartPlotWidget(pg.PlotWidget): | |||
| 
 | ||||
|         def update(index: int) -> None: | ||||
|             label.setText( | ||||
|                 "{name} O:{} H:{} L:{} C:{} V:{}".format( | ||||
|                 "{name}[{index}] -> O:{} H:{} L:{} C:{} V:{}".format( | ||||
|                     *self._array[index].item()[2:], | ||||
|                     name=name, | ||||
|                     index=index, | ||||
|                 ) | ||||
|             ) | ||||
| 
 | ||||
|  | @ -412,7 +423,7 @@ class ChartPlotWidget(pg.PlotWidget): | |||
| 
 | ||||
|         def update(index: int) -> None: | ||||
|             data = self._array[index] | ||||
|             label.setText(f"{name}: {index} {data}") | ||||
|             label.setText(f"{name} -> {data}") | ||||
| 
 | ||||
|         self._labels[name] = (label, update) | ||||
|         self._update_contents_label(index=-1) | ||||
|  | @ -427,6 +438,8 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|         # "only update with new items" on the pg.PlotDataItem | ||||
|         curve.update_from_array = curve.setData | ||||
| 
 | ||||
|         self._add_sticky(name) | ||||
| 
 | ||||
|         return curve | ||||
| 
 | ||||
|     def _add_sticky( | ||||
|  | @ -435,7 +448,7 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|         # retreive: Callable[None, np.ndarray], | ||||
|     ) -> YSticky: | ||||
|         # add y-axis "last" value label | ||||
|         last = self._ysticks['last'] = YSticky( | ||||
|         last = self._ysticks[name] = YSticky( | ||||
|             chart=self, | ||||
|             parent=self.getAxis('right'), | ||||
|             # digits=0, | ||||
|  | @ -490,33 +503,43 @@ class ChartPlotWidget(pg.PlotWidget): | |||
|             # likely no data loaded yet | ||||
|             log.error(f"WTF bars_range = {lbar}:{rbar}") | ||||
|             return | ||||
|         elif lbar < 0: | ||||
|             breakpoint() | ||||
| 
 | ||||
|         # TODO: should probably just have some kinda attr mark | ||||
|         # that determines this behavior based on array type | ||||
|         try: | ||||
|             ylow = bars['low'].min() | ||||
|             yhigh = bars['high'].max() | ||||
|             std = np.std(bars['close']) | ||||
|             # std = np.std(bars['close']) | ||||
|         except IndexError: | ||||
|             # must be non-ohlc array? | ||||
|             ylow = bars.min() | ||||
|             yhigh = bars.max() | ||||
|             std = np.std(bars) | ||||
|             # std = np.std(bars) | ||||
| 
 | ||||
|         # view margins: stay within 10% of the "true range" | ||||
|         diff = yhigh - ylow | ||||
|         ylow = ylow - (diff * 0.1) | ||||
|         yhigh = yhigh + (diff * 0.1) | ||||
|         ylow = ylow - (diff * 0.04) | ||||
|         yhigh = yhigh + (diff * 0.01) | ||||
| 
 | ||||
|         # compute contents label "height" in view terms | ||||
|         if self._labels: | ||||
|             label = self._labels[self.name][0] | ||||
|             rect = label.itemRect() | ||||
|             tl, br = rect.topLeft(), rect.bottomRight() | ||||
|             vb = self.plotItem.vb | ||||
|             top, bottom = (vb.mapToView(tl).y(), vb.mapToView(br).y()) | ||||
|             label_h = top - bottom | ||||
|             # print(f'label height {self.name}: {label_h}') | ||||
|         else: | ||||
|             label_h = 0 | ||||
| 
 | ||||
|         chart = self | ||||
|         chart.setLimits( | ||||
|             yMin=ylow, | ||||
|             yMax=yhigh, | ||||
|             minYRange=std | ||||
|             yMax=yhigh + label_h, | ||||
|             # minYRange=std | ||||
|         ) | ||||
|         chart.setYRange(ylow, yhigh) | ||||
|         chart.setYRange(ylow, yhigh + label_h) | ||||
| 
 | ||||
|     def enterEvent(self, ev):  # noqa | ||||
|         # pg.PlotWidget.enterEvent(self, ev) | ||||
|  | @ -577,7 +600,10 @@ async def add_new_bars(delay_s, linked_charts): | |||
|             return new_array | ||||
| 
 | ||||
|         # add new increment/bar | ||||
|         start = time.time() | ||||
|         ohlc = price_chart._array = incr_ohlc_array(ohlc) | ||||
|         diff = time.time() - start | ||||
|         print(f'array append took {diff}') | ||||
| 
 | ||||
|         # TODO: generalize this increment logic | ||||
|         for name, chart in linked_charts.subplots.items(): | ||||
|  | @ -660,7 +686,7 @@ async def _async_main( | |||
|         n.start_soon( | ||||
|             chart_from_fsp, | ||||
|             linked_charts, | ||||
|             fsp.latency, | ||||
|             'rsi', | ||||
|             sym, | ||||
|             bars, | ||||
|             brokermod, | ||||
|  | @ -668,8 +694,10 @@ async def _async_main( | |||
|         ) | ||||
| 
 | ||||
|         # update last price sticky | ||||
|         last = chart._ysticks['last'] | ||||
|         last.update_from_data(*chart._array[-1][['index', 'close']]) | ||||
|         last_price_sticky = chart._ysticks[chart.name] | ||||
|         last_price_sticky.update_from_data( | ||||
|             *chart._array[-1][['index', 'close']] | ||||
|         ) | ||||
| 
 | ||||
|         # graphics update loop | ||||
| 
 | ||||
|  | @ -711,15 +739,14 @@ async def _async_main( | |||
|                                 chart._array, | ||||
|                             ) | ||||
|                             # update sticky(s) | ||||
|                             last = chart._ysticks['last'] | ||||
|                             last.update_from_data( | ||||
|                             last_price_sticky.update_from_data( | ||||
|                                 *chart._array[-1][['index', 'close']]) | ||||
|                             chart._set_yrange() | ||||
| 
 | ||||
| 
 | ||||
| async def chart_from_fsp( | ||||
|     linked_charts, | ||||
|     fsp_func, | ||||
|     func_name, | ||||
|     sym, | ||||
|     bars, | ||||
|     brokermod, | ||||
|  | @ -729,8 +756,6 @@ async def chart_from_fsp( | |||
| 
 | ||||
|     Pass target entrypoint and historical data. | ||||
|     """ | ||||
|     func_name = fsp_func.__name__ | ||||
| 
 | ||||
|     async with tractor.open_nursery() as n: | ||||
|         portal = await n.run_in_actor( | ||||
|             f'fsp.{func_name}',  # name as title of sub-chart | ||||
|  | @ -749,7 +774,7 @@ async def chart_from_fsp( | |||
|         stream = await portal.result() | ||||
| 
 | ||||
|         # receive processed historical data-array as first message | ||||
|         history: np.ndarray = (await stream.__anext__()) | ||||
|         history = (await stream.__anext__()) | ||||
| 
 | ||||
|         # TODO: enforce type checking here | ||||
|         newbars = np.array(history) | ||||
|  | @ -768,9 +793,15 @@ async def chart_from_fsp( | |||
|                 np.full(abs(diff), data[-1], dtype=data.dtype) | ||||
|             ) | ||||
| 
 | ||||
|         value = chart._array[-1] | ||||
|         last_val_sticky = chart._ysticks[chart.name] | ||||
|         last_val_sticky.update_from_data(-1, value) | ||||
| 
 | ||||
|         # update chart graphics | ||||
|         async for value in stream: | ||||
|             chart._array[-1] = value | ||||
|             last_val_sticky.update_from_data(-1, value) | ||||
|             chart._set_yrange() | ||||
|             chart.update_from_array(chart.name, chart._array) | ||||
|             chart._set_yrange() | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue