Compare commits

...

10 Commits

Author SHA1 Message Date
wattygetlood 75faf83004 Set isn't serializable on std msgpack 2021-11-07 13:14:38 -05:00
wattygetlood a270b2e033 Only load 4 ib requests worth of bars on windows... 2021-11-07 13:14:38 -05:00
wattygetlood 09fd8ef742 Hack search view on windows to 1/2 window height; needs a better solution 2021-11-07 13:14:38 -05:00
wattygetlood 422f203fc3 Size the window to aproximately 1/3 the screen space 2021-11-07 13:14:38 -05:00
wattygetlood 8fbd0cd067 No support for notifications (yet) on windows 2021-11-07 13:14:38 -05:00
wattygetlood 68ed1164a1 Fix divide-by-zero when quote read is too fast in throttle task 2021-11-07 13:14:38 -05:00
wattygetlood 2f73f809f1 Fix default `brokers.toml` copying since module move 2021-11-07 13:14:38 -05:00
wattygetlood ba7b01b704 Configure window size based on screen dims on windows 2021-11-07 13:14:38 -05:00
Tyler Goodlet 83299c3a8b Start nts 2021-11-05 15:56:43 -04:00
Tyler Goodlet 0d17f4ff4c Drop print around unshown fsp updates 2021-11-05 15:48:03 -04:00
10 changed files with 117 additions and 58 deletions

28
notes_to_self.rst 100644
View File

@ -0,0 +1,28 @@
Notes to self
=============
chicken scratch we shan't forget, consider this staging
for actual feature issues on wtv git wrapper-provider we're
using (no we shan't stick with GH long term likely).
cool chart features
-------------------
- allow right-click to spawn shell with current in view
data passed to the new process via ``msgpack-numpy``.
- expand OHLC datum to lower time frame.
- auto-highlight current time range on tick feed
features from IB charting
-------------------------
- vlm diffing from ticks and compare when bar arrives from historical
- should help isolate dark vlm / trades
chart ux ideas
--------------
- hotkey to zoom to order intersection (horizontal line) with previous
price levels (+ some margin obvs).
- L1 "lines" (queue size repr) should normalize to some fixed x width
such that when levels with more vlm appear other smaller levels are
scaled down giving an immediate indication of the liquidity diff.

View File

@ -1157,6 +1157,11 @@ async def backfill_bars(
https://github.com/pikers/piker/issues/128 https://github.com/pikers/piker/issues/128
""" """
if platform.system() == 'Windows':
log.warning(
'Decreasing history query count to 4 since, windows...')
count = 4
out, fails = await get_bars(sym) out, fails = await get_bars(sym)
if out is None: if out is None:
raise RuntimeError("Could not pull currrent history?!") raise RuntimeError("Could not pull currrent history?!")

View File

@ -1046,7 +1046,7 @@ async def _emsd_main(
# signal to client that we're started and deliver # signal to client that we're started and deliver
# all known pps and accounts for this ``brokerd``. # all known pps and accounts for this ``brokerd``.
await ems_ctx.started((pp_msgs, relay.accounts)) await ems_ctx.started((pp_msgs, list(relay.accounts)))
# establish 2-way stream with requesting order-client and # establish 2-way stream with requesting order-client and
# begin handling inbound order requests and updates # begin handling inbound order requests and updates

View File

@ -60,7 +60,7 @@ def repodir():
""" """
dirpath = os.path.abspath( dirpath = os.path.abspath(
# we're 3 levels down in **this** module file # we're 3 levels down in **this** module file
dirname(dirname(dirname(os.path.realpath(__file__)))) dirname(dirname(os.path.realpath(__file__)))
) )
return dirpath return dirpath
@ -73,7 +73,7 @@ def load(
path = path or get_broker_conf_path() path = path or get_broker_conf_path()
if not os.path.isfile(path): if not os.path.isfile(path):
shutil.copyfile( shutil.copyfile(
os.path.join(repodir(), 'data/brokers.toml'), os.path.join(repodir(), 'config', 'brokers.toml'),
path, path,
) )

View File

@ -172,6 +172,7 @@ async def sample_and_broadcast(
# iterate stream delivered by broker # iterate stream delivered by broker
async for quotes in quote_stream: async for quotes in quote_stream:
# TODO: ``numba`` this! # TODO: ``numba`` this!
for sym, quote in quotes.items(): for sym, quote in quotes.items():
@ -184,12 +185,8 @@ async def sample_and_broadcast(
# start writing the shm buffer with appropriate # start writing the shm buffer with appropriate
# trade data # trade data
for tick in quote['ticks']:
# TODO: we should probably not write every single
# value to an OHLC sample stream XD
# for a tick stream sure.. but this is excessive..
ticks = quote['ticks']
for tick in ticks:
ticktype = tick['type'] ticktype = tick['type']
# write trade events to shm last OHLC sample # write trade events to shm last OHLC sample
@ -261,8 +258,7 @@ async def sample_and_broadcast(
except ( except (
trio.BrokenResourceError, trio.BrokenResourceError,
trio.ClosedResourceError, trio.ClosedResourceError
trio.EndOfChannel,
): ):
# XXX: do we need to deregister here # XXX: do we need to deregister here
# if it's done in the fee bus code? # if it's done in the fee bus code?
@ -272,10 +268,6 @@ async def sample_and_broadcast(
f'{stream._ctx.chan.uid} dropped ' f'{stream._ctx.chan.uid} dropped '
'`brokerd`-quotes-feed connection' '`brokerd`-quotes-feed connection'
) )
if tick_throttle:
assert stream.closed()
# await stream.aclose()
subs.remove((stream, tick_throttle)) subs.remove((stream, tick_throttle))
@ -291,8 +283,12 @@ async def uniform_rate_send(
) -> None: ) -> None:
sleep_period = 1/rate - 0.0001 # 100us sleep_period = 1/rate - 0.000616
last_send = time.time() last_send = time.time()
aname = stream._ctx.chan.uid[0]
fsp = False
if 'fsp' in aname:
fsp = True
while True: while True:
@ -312,33 +308,21 @@ async def uniform_rate_send(
sym, next_quote = quote_stream.receive_nowait() sym, next_quote = quote_stream.receive_nowait()
ticks = next_quote.get('ticks') ticks = next_quote.get('ticks')
# XXX: idea for frame type data structure we could use on the
# wire instead of a simple list?
# frames = {
# 'index': ['type_a', 'type_c', 'type_n', 'type_n'],
# 'type_a': [tick0, tick1, tick2, .., tickn],
# 'type_b': [tick0, tick1, tick2, .., tickn],
# 'type_c': [tick0, tick1, tick2, .., tickn],
# ...
# 'type_n': [tick0, tick1, tick2, .., tickn],
# }
if ticks: if ticks:
first_quote['ticks'].extend(ticks) first_quote['ticks'].extend(ticks)
except trio.WouldBlock: except trio.WouldBlock:
now = time.time() now = time.time()
rate = 1 / (now - last_send) diff = now - last_send
rate = 1 / diff if diff else float('inf')
last_send = now
log.debug( # log.info(f'{rate} Hz sending quotes') # \n{first_quote}')
f'`{sym}` throttled send hz: {round(rate, ndigits=1)}'
)
# TODO: now if only we could sync this to the display # TODO: now if only we could sync this to the display
# rate timing exactly lul # rate timing exactly lul
try: try:
await stream.send({sym: first_quote}) await stream.send({sym: first_quote})
last_send = now
break break
except trio.ClosedResourceError: except trio.ClosedResourceError:
# if the feed consumer goes down then drop # if the feed consumer goes down then drop

View File

@ -800,14 +800,11 @@ async def update_chart_from_fsp(
profiler.finish() profiler.finish()
# update chart graphics # update chart graphics
i = 0
last = time.time() last = time.time()
async for value in stream: async for value in stream:
# chart isn't actively shown so just skip render cycle # chart isn't actively shown so just skip render cycle
if chart.linked.isHidden(): if chart.linked.isHidden():
print(f'{i} unseen fsp cyclce')
i += 1
continue continue
else: else:

View File

@ -61,7 +61,9 @@ _do_overrides()
# XXX: pretty sure none of this shit works on linux as per: # XXX: pretty sure none of this shit works on linux as per:
# https://bugreports.qt.io/browse/QTBUG-53022 # https://bugreports.qt.io/browse/QTBUG-53022
# it seems to work on windows.. no idea wtf is up. # it seems to work on windows.. no idea wtf is up.
is_windows = False
if platform.system() == "Windows": if platform.system() == "Windows":
is_windows = True
# Proper high DPI scaling is available in Qt >= 5.6.0. This attibute # Proper high DPI scaling is available in Qt >= 5.6.0. This attibute
# must be set before creating the application # must be set before creating the application
@ -182,6 +184,8 @@ def run_qtractor(
window.main_widget = main_widget window.main_widget = main_widget
window.setCentralWidget(instance) window.setCentralWidget(instance)
if is_windows:
window.configure_to_desktop()
# actually render to screen # actually render to screen
window.show() window.show()

View File

@ -49,7 +49,7 @@ from PyQt5 import QtCore
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from PyQt5.QtCore import ( from PyQt5.QtCore import (
Qt, Qt,
# QSize, QSize,
QModelIndex, QModelIndex,
QItemSelectionModel, QItemSelectionModel,
) )
@ -112,6 +112,7 @@ class CompleterView(QTreeView):
model = QStandardItemModel(self) model = QStandardItemModel(self)
self.labels = labels self.labels = labels
self._last_window_h: Optional[int] = None
# a std "tabular" config # a std "tabular" config
self.setItemDelegate(FontScaledDelegate(self)) self.setItemDelegate(FontScaledDelegate(self))
@ -126,6 +127,10 @@ class CompleterView(QTreeView):
# self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored) # self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
# ux settings # ux settings
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding,
)
self.setItemsExpandable(True) self.setItemsExpandable(True)
self.setExpandsOnDoubleClick(False) self.setExpandsOnDoubleClick(False)
self.setAnimated(False) self.setAnimated(False)
@ -152,24 +157,41 @@ class CompleterView(QTreeView):
self._font_size = size self._font_size = size
self.setStyleSheet(f"font: {size}px") self.setStyleSheet(f"font: {size}px")
#def resizeEvent(self, event: 'QEvent') -> None:
# self.resize_to_results()
# super().resizeEvent(event)
def resize(self): def resize_to_results(self):
model = self.model() model = self.model()
cols = model.columnCount() cols = model.columnCount()
for i in range(cols): for i in range(cols):
self.resizeColumnToContents(i) self.resizeColumnToContents(i)
# inclusive of search bar and header "rows" in pixel terms
rows = 100
# max_rows = 8 # 6 + search and headers
row_px = self.rowHeight(self.currentIndex()) row_px = self.rowHeight(self.currentIndex())
# print(f'font_h: {font_h}\n px_height: {px_height}')
# TODO: probably make this more general / less hacky # TODO: probably make this more general / less hacky
self.setMinimumSize(self.width(), rows * row_px) # we should figure out the exact number of rows to allow
self.setMaximumSize(self.width() + 10, rows * row_px) # inclusive of search bar and header "rows", in pixel terms.
window_h = self.window().height()
rows = round(window_h * 0.5 / row_px) - 4
# TODO: the problem here is that this view widget is **not** resizing/scaling
# when the parent layout is adjusted, not sure what exactly is up...
# only "scale up" the results view when the window size has increased/
if not self._last_window_h or self._last_window_h < window_h:
self.setMaximumSize(self.width(), rows * row_px)
self.setMinimumSize(self.width(), rows * row_px)
#elif not self._last_window_h or self._last_window_h > window_h:
# self.setMinimumSize(self.width(), rows * row_px)
# self.setMaximumSize(self.width(), rows * row_px)
self.resize(self.width(), rows * row_px)
self._last_window_h = window_h
self.setFixedWidth(333) self.setFixedWidth(333)
self.update()
def is_selecting_d1(self) -> bool: def is_selecting_d1(self) -> bool:
cidx = self.selectionModel().currentIndex() cidx = self.selectionModel().currentIndex()
@ -334,7 +356,7 @@ class CompleterView(QTreeView):
else: else:
model.setItem(idx.row(), 1, QStandardItem()) model.setItem(idx.row(), 1, QStandardItem())
self.resize() self.resize_to_results()
return idx return idx
else: else:
@ -404,7 +426,7 @@ class CompleterView(QTreeView):
def show_matches(self) -> None: def show_matches(self) -> None:
self.show() self.show()
self.resize() self.resize_to_results()
class SearchBar(Edit): class SearchBar(Edit):
@ -457,7 +479,7 @@ class SearchWidget(QtWidgets.QWidget):
# size it as we specify # size it as we specify
self.setSizePolicy( self.setSizePolicy(
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed,
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding,
) )
self.godwidget = godwidget self.godwidget = godwidget

View File

@ -151,8 +151,8 @@ class MainWindow(QtGui.QMainWindow):
# XXX: for tiling wms this should scale # XXX: for tiling wms this should scale
# with the alloted window size. # with the alloted window size.
# TODO: detect for tiling and if untrue set some size? # TODO: detect for tiling and if untrue set some size?
# size = (300, 500) size = (300, 500)
size = (0, 0) #size = (0, 0)
title = 'piker chart (ur symbol is loading bby)' title = 'piker chart (ur symbol is loading bby)'
@ -163,7 +163,8 @@ class MainWindow(QtGui.QMainWindow):
self._status_bar: QStatusBar = None self._status_bar: QStatusBar = None
self._status_label: QLabel = None self._status_label: QLabel = None
self._size: Optional[tuple[int, int]] = None
@property @property
def mode_label(self) -> QtGui.QLabel: def mode_label(self) -> QtGui.QLabel:
@ -267,6 +268,22 @@ class MainWindow(QtGui.QMainWindow):
assert screen, "Wow Qt is dumb as shit and has no screen..." assert screen, "Wow Qt is dumb as shit and has no screen..."
return screen return screen
def configure_to_desktop(
self,
size: Optional[tuple[int, int]] = None,
) -> None:
# https://stackoverflow.com/a/18975846
if not size and not self._size:
app = QtGui.QApplication.instance()
geo = self.current_screen().geometry()
h, w = geo.height(), geo.width()
self.setMaximumSize(w, h)
# use approx 1/3 of the area of the screen by default
self._size = round(w * .666), round(h * .666)
self.resize(*size or self._size)
# singleton app per actor # singleton app per actor
_qt_win: QtGui.QMainWindow = None _qt_win: QtGui.QMainWindow = None

View File

@ -22,6 +22,7 @@ from contextlib import asynccontextmanager
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import partial from functools import partial
from pprint import pformat from pprint import pformat
import platform
import time import time
from typing import Optional, Dict, Callable, Any from typing import Optional, Dict, Callable, Any
import uuid import uuid
@ -429,16 +430,17 @@ class OrderMode:
# TODO: make this not trash. # TODO: make this not trash.
# XXX: linux only for now # XXX: linux only for now
result = await trio.run_process( if platform.system() != "Windows":
[ result = await trio.run_process(
'notify-send', [
'-u', 'normal', 'notify-send',
'-t', '10000', '-u', 'normal',
'piker', '-t', '10000',
f'alert: {msg}', 'piker',
], f'alert: {msg}',
) ],
log.runtime(result) )
log.runtime(result)
def on_cancel( def on_cancel(
self, self,