Merge pull request #145 from pikers/y_zoom

Y zoom
readme_bumpz
goodboy 2021-01-15 21:07:02 -05:00 committed by GitHub
commit 03074d4717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 42 deletions

View File

@ -622,7 +622,8 @@ async def fill_bars(
sym: str, sym: str,
first_bars: list, first_bars: list,
shm: 'ShmArray', # type: ignore # noqa shm: 'ShmArray', # type: ignore # noqa
count: int = 21, # NOTE: any more and we'll overrun the underlying buffer # count: int = 20, # NOTE: any more and we'll overrun the underlying buffer
count: int = 2, # NOTE: any more and we'll overrun the underlying buffer
) -> None: ) -> None:
"""Fill historical bars into shared mem / storage afap. """Fill historical bars into shared mem / storage afap.
@ -647,11 +648,23 @@ async def fill_bars(
next_dt = bars[0].date next_dt = bars[0].date
except RequestError as err: except RequestError as err:
# TODO: retreive underlying ``ib_insync`` error~~ # TODO: retreive underlying ``ib_insync`` error?
if err.code == 162: if err.code == 162:
if 'HMDS query returned no data' in err.message:
# means we hit some kind of historical "dead zone"
# and further requests seem to always cause
# throttling despite the rps being low
break
else:
log.exception( log.exception(
"Data query rate reached: Press `ctrl-alt-f` in TWS") "Data query rate reached: Press `ctrl-alt-f` in TWS")
# TODO: should probably create some alert on screen
# and then somehow get that to trigger an event here
# that restarts/resumes this task?
await tractor.breakpoint() await tractor.breakpoint()

View File

@ -384,7 +384,6 @@ class ChartPlotWidget(pg.PlotWidget):
# self.setViewportMargins(0, 0, 0, 0) # self.setViewportMargins(0, 0, 0, 0)
self._ohlc = array # readonly view of ohlc data self._ohlc = array # readonly view of ohlc data
self.default_view()
self._arrays = {} # readonly view of overlays self._arrays = {} # readonly view of overlays
self._graphics = {} # registry of underlying graphics self._graphics = {} # registry of underlying graphics
@ -406,6 +405,8 @@ class ChartPlotWidget(pg.PlotWidget):
# show background grid # show background grid
self.showGrid(x=True, y=True, alpha=0.5) self.showGrid(x=True, y=True, alpha=0.5)
self.default_view()
# TODO: stick in config # TODO: stick in config
# use cross-hair for cursor? # use cross-hair for cursor?
# self.setCursor(QtCore.Qt.CrossCursor) # self.setCursor(QtCore.Qt.CrossCursor)
@ -478,10 +479,13 @@ class ChartPlotWidget(pg.PlotWidget):
""" """
xlast = self._ohlc[index]['index'] xlast = self._ohlc[index]['index']
print(xlast)
begin = xlast - _bars_to_left_in_follow_mode begin = xlast - _bars_to_left_in_follow_mode
end = xlast + _bars_from_right_in_follow_mode end = xlast + _bars_from_right_in_follow_mode
# remove any custom user yrange setttings
if self._static_yrange == 'axis':
self._static_yrange = None
self.plotItem.vb.setXRange( self.plotItem.vb.setXRange(
min=begin, min=begin,
max=end, max=end,
@ -697,7 +701,12 @@ class ChartPlotWidget(pg.PlotWidget):
data set. data set.
""" """
if self._static_yrange is not None: set_range = True
if self._static_yrange == 'axis':
set_range = False
elif self._static_yrange is not None:
ylow, yhigh = self._static_yrange ylow, yhigh = self._static_yrange
elif yrange is not None: elif yrange is not None:
@ -761,11 +770,19 @@ class ChartPlotWidget(pg.PlotWidget):
ylow = np.nanmin(bars) ylow = np.nanmin(bars)
yhigh = np.nanmax(bars) yhigh = np.nanmax(bars)
if set_range:
# view margins: stay within a % of the "true range" # view margins: stay within a % of the "true range"
diff = yhigh - ylow diff = yhigh - ylow
ylow = ylow - (diff * 0.04) ylow = ylow - (diff * 0.04)
yhigh = yhigh + (diff * 0.04) yhigh = yhigh + (diff * 0.04)
self.setLimits(
yMin=ylow,
yMax=yhigh,
)
self.setYRange(ylow, yhigh)
# def _label_h(self, yhigh: float, ylow: float) -> float:
# # compute contents label "height" in view terms # # compute contents label "height" in view terms
# # to avoid having data "contents" overlap with them # # to avoid having data "contents" overlap with them
# if self._labels: # if self._labels:
@ -791,14 +808,8 @@ class ChartPlotWidget(pg.PlotWidget):
# if label_h > yhigh - ylow: # if label_h > yhigh - ylow:
# label_h = 0 # label_h = 0
# print(f"bounds (ylow, yhigh): {(ylow, yhigh)}")
label_h = 0
self.setLimits( # print(f"bounds (ylow, yhigh): {(ylow, yhigh)}")
yMin=ylow,
yMax=yhigh + label_h,
)
self.setYRange(ylow, yhigh + label_h)
def enterEvent(self, ev): # noqa def enterEvent(self, ev): # noqa
# pg.PlotWidget.enterEvent(self, ev) # pg.PlotWidget.enterEvent(self, ev)
@ -1137,7 +1148,7 @@ async def spawn_fsps(
"""Start an fsp subactor async. """Start an fsp subactor async.
""" """
print(f'FSP NAME: {fsp_name}') # print(f'FSP NAME: {fsp_name}')
portal = await n.run_in_actor( portal = await n.run_in_actor(
# subactor entrypoint # subactor entrypoint

View File

@ -17,6 +17,8 @@
""" """
UX interaction customs. UX interaction customs.
""" """
from typing import Optional
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph import ViewBox, Point, QtCore, QtGui from pyqtgraph import ViewBox, Point, QtCore, QtGui
from pyqtgraph import functions as fn from pyqtgraph import functions as fn
@ -144,7 +146,7 @@ class SelectRect(QtGui.QGraphicsRectItem):
x1, x2 = start_pos.x(), end_pos.x() x1, x2 = start_pos.x(), end_pos.x()
# TODO: heh, could probably use a max-min streamin algo here too # TODO: heh, could probably use a max-min streamin algo here too
ymn, xmn = min(y1, y2), min(x1, x2) _, xmn = min(y1, y2), min(x1, x2)
ymx, xmx = max(y1, y2), max(x1, x2) ymx, xmx = max(y1, y2), max(x1, x2)
pchng = (y2 - y1) / y1 * 100 pchng = (y2 - y1) / y1 * 100
@ -277,7 +279,11 @@ class ChartView(ViewBox):
ev.accept() ev.accept()
self.sigRangeChangedManually.emit(mask) self.sigRangeChangedManually.emit(mask)
def mouseDragEvent(self, ev, axis=None): def mouseDragEvent(
self,
ev,
axis: Optional[int] = None,
) -> None:
# if axis is specified, event will only affect that axis. # if axis is specified, event will only affect that axis.
ev.accept() # we accept all buttons ev.accept() # we accept all buttons
@ -295,6 +301,18 @@ class ChartView(ViewBox):
# Scale or translate based on mouse button # Scale or translate based on mouse button
if ev.button() & (QtCore.Qt.LeftButton | QtCore.Qt.MidButton): if ev.button() & (QtCore.Qt.LeftButton | QtCore.Qt.MidButton):
# zoom only y-axis when click-n-drag on it
if axis == 1:
# set a static y range special value on chart widget to
# prevent sizing to data in view.
self._chart._static_yrange = 'axis'
scale_y = 1.3 ** (dif.y() * -1 / 20)
self.setLimits(yMin=None, yMax=None)
# print(scale_y)
self.scaleBy((0, scale_y))
if self.state['mouseMode'] == ViewBox.RectMode: if self.state['mouseMode'] == ViewBox.RectMode:
down_pos = ev.buttonDownPos() down_pos = ev.buttonDownPos()
@ -333,6 +351,7 @@ class ChartView(ViewBox):
self.sigRangeChangedManually.emit(self.state['mouseEnabled']) self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
elif ev.button() & QtCore.Qt.RightButton: elif ev.button() & QtCore.Qt.RightButton:
# print "vb.rightDrag" # print "vb.rightDrag"
if self.state['aspectLocked'] is not False: if self.state['aspectLocked'] is not False:
mask[0] = 0 mask[0] = 0