Change over the UI layer to use `MktPair`

Including changing to `LinkedSplits.mkt: MktPair` and adding an explicit
setter method for setting it and being sure that nothing breaks
in the display system init!

For this commit we leave in warning access to `LinkedSplits.symbol` but
will remove in following commit.
master
Tyler Goodlet 2023-05-24 15:30:17 -04:00
parent 39af215d61
commit 1b577eebf6
9 changed files with 120 additions and 86 deletions

View File

@ -907,7 +907,7 @@ async def open_feed(
for fqme, flume_msg in flumes_msg_dict.items():
flume = Flume.from_msg(flume_msg)
# assert flume.symbol.fqme == fqme
# assert flume.mkt.fqme == fqme
feed.flumes[fqme] = flume
# TODO: do we need this?

View File

@ -68,7 +68,10 @@ from ..data.feed import (
Feed,
Flume,
)
from ..accounting._mktinfo import Symbol
from ..accounting import (
MktPair,
Symbol,
)
from ..log import get_logger
from ._interaction import ChartView
from ._forms import FieldsForm
@ -287,7 +290,7 @@ class GodWidget(QWidget):
pp_nav.hide()
# set window titlebar info
symbol = self.rt_linked.symbol
symbol = self.rt_linked.mkt
if symbol is not None:
self.window.setWindowTitle(
f'{symbol.fqme} '
@ -452,7 +455,7 @@ class LinkedSplits(QWidget):
# update the UI for a given "chart instance".
self.display_state: DisplayState | None = None
self._symbol: Symbol = None
self._mkt: MktPair | Symbol = None
def on_splitter_adjust(
self,
@ -474,9 +477,20 @@ class LinkedSplits(QWidget):
**kwargs,
)
def set_mkt_info(
self,
mkt: MktPair,
) -> None:
self._mkt = mkt
@property
def symbol(self) -> Symbol:
return self._symbol
def mkt(self) -> MktPair:
return self._mkt
@property
def symbol(self) -> Symbol | MktPair:
log.warning(f'{type(self)}.symbol is now deprecated use .mkt!')
return self.mkt
def set_split_sizes(
self,
@ -521,7 +535,7 @@ class LinkedSplits(QWidget):
def plot_ohlc_main(
self,
symbol: Symbol,
mkt: MktPair,
shm: ShmArray,
flume: Flume,
sidepane: FieldsForm,
@ -540,7 +554,7 @@ class LinkedSplits(QWidget):
# add crosshairs
self.cursor = Cursor(
linkedsplits=self,
digits=symbol.tick_size_digits,
digits=mkt.price_tick_digits,
)
# NOTE: atm the first (and only) OHLC price chart for the symbol
@ -548,7 +562,7 @@ class LinkedSplits(QWidget):
# be no distinction since we will have multiple symbols per
# view as part of "aggregate feeds".
self.chart = self.add_plot(
name=symbol.fqme,
name=mkt.fqme,
shm=shm,
flume=flume,
style=style,
@ -1030,7 +1044,7 @@ class ChartPlotWidget(pg.PlotWidget):
'''
view = vb or self.view
viz = self.main_viz
l, r = viz.view_range()
left, right = viz.view_range()
x_shift = viz.index_step() * datums
if datums >= 300:
@ -1040,8 +1054,8 @@ class ChartPlotWidget(pg.PlotWidget):
# should trigger broadcast on all overlays right?
view.setXRange(
min=l + x_shift,
max=r + x_shift,
min=left + x_shift,
max=right + x_shift,
# TODO: holy shit, wtf dude... why tf would this not be 0 by
# default... speechless.
@ -1227,7 +1241,7 @@ class ChartPlotWidget(pg.PlotWidget):
# if the sticky is for our symbol
# use the tick size precision for display
name = name or pi.name
sym = self.linked.symbol
sym = self.linked.mkt
digits = None
if name == sym.key:
digits = sym.tick_size_digits

View File

@ -228,7 +228,7 @@ class ContentsLabel(pg.LabelItem):
'bar_wap',
]
],
name=name,
# name=name,
index=ix,
)
)
@ -363,7 +363,7 @@ class Cursor(pg.GraphicsObject):
# value used for rounding y-axis discreet tick steps
# computing once, up front, here cuz why not
mkt = self.linked._symbol
mkt = self.linked.mkt
self._y_tick_mult = 1/float(mkt.price_tick)
# line width in view coordinates

View File

@ -436,12 +436,12 @@ class Viz(Struct):
else:
if x_range is None:
(
l,
xl,
_,
lbar,
rbar,
_,
r,
xr,
) = self.datums_range()
profiler(f'{self.name} got bars range')
@ -585,12 +585,12 @@ class Viz(Struct):
Return a range tuple for the datums present in view.
'''
l, r = view_range or self.view_range()
xl, xr = view_range or self.view_range()
index_field: str = index_field or self.index_field
if index_field == 'index':
l: int = round(l)
r: int = round(r)
xl: int = round(xl)
xr: int = round(xr)
if array is None:
array = self.shm.array
@ -601,12 +601,12 @@ class Viz(Struct):
# invalid view state
if (
r < l
or l < 0
or r < 0
xr < xl
or xl < 0
or xr < 0
or (
l > last
and r > last
xl > last
and xr > last
)
):
leftmost: int = first
@ -616,12 +616,12 @@ class Viz(Struct):
# determine first and last datums in view determined by
# l -> r view range.
rightmost = max(
min(last, ceil(r)),
min(last, ceil(xr)),
first,
)
leftmost = min(
max(first, floor(l)),
max(first, floor(xl)),
last,
rightmost - 1,
)
@ -632,12 +632,12 @@ class Viz(Struct):
self.vs.xrange = leftmost, rightmost
return (
l, # left x-in-view
xl, # left x-in-view
first, # first datum
leftmost,
rightmost,
last, # last_datum
r, # right-x-in-view
xr, # right-x-in-view
)
def read(
@ -665,12 +665,12 @@ class Viz(Struct):
profiler('self.shm.array READ')
(
l,
xl,
ifirst,
lbar,
rbar,
ilast,
r,
xr,
) = self.datums_range(
index_field=index_field,
array=array,
@ -715,8 +715,8 @@ class Viz(Struct):
# a uniform time stamp step size?
else:
# get read-relative indices adjusting for master shm index.
lbar_i = max(l, ifirst) - ifirst
rbar_i = min(r, ilast) - ifirst
lbar_i = max(xl, ifirst) - ifirst
rbar_i = min(xr, ilast) - ifirst
# NOTE: the slice here does NOT include the extra ``+ 1``
# BUT the ``in_view`` slice DOES..
@ -1244,18 +1244,25 @@ class Viz(Struct):
'''
# get most recent right datum index in-view
l, start, datum_start, datum_stop, stop, r = self.datums_range()
(
xl,
start,
datum_start,
datum_stop,
stop,
xr,
) = self.datums_range()
lasts = self.shm.array[-1]
i_step = lasts['index'] # last index-specific step.
i_step_t = lasts['time'] # last time step.
# fqme = self.flume.symbol.fqme
# fqme = self.flume.mkt.fqme
# check if "last (is) in view" -> is a real-time update necessary?
if self.index_field == 'index':
liv = (r >= i_step)
liv = (xr >= i_step)
else:
liv = (r >= i_step_t)
liv = (xr >= i_step_t)
# compute the first available graphic obj's x-units-per-pixel
# TODO: make this not loop through all vizs each time!

View File

@ -37,6 +37,9 @@ import pyqtgraph as pg
from msgspec import field
# from .. import brokers
from ..accounting import (
MktPair,
)
from ..data.feed import (
open_feed,
Feed,
@ -319,8 +322,8 @@ async def graphics_update_loop(
for fqme, flume in feed.flumes.items():
ohlcv = flume.rt_shm
hist_ohlcv = flume.hist_shm
symbol = flume.mkt
fqme = symbol.fqme
mkt = flume.mkt
fqme = mkt.fqme
# update last price sticky
fast_viz = fast_chart._vizs[fqme]
@ -360,13 +363,13 @@ async def graphics_update_loop(
last, volume = ohlcv.array[-1][['close', 'volume']]
symbol = flume.mkt
mkt = flume.mkt
l1 = L1Labels(
fast_pi,
# determine precision/decimal lengths
digits=symbol.tick_size_digits,
size_digits=symbol.lot_size_digits,
digits=mkt.price_tick_digits,
size_digits=mkt.size_tick_digits,
)
# TODO:
@ -449,7 +452,7 @@ async def graphics_update_loop(
and quote_rate >= display_rate
):
pass
# log.warning(f'High quote rate {symbol.key}: {quote_rate}')
# log.warning(f'High quote rate {mkt.fqme}: {quote_rate}')
last_quote_s = time.time()
@ -1224,7 +1227,7 @@ async def display_symbol_data(
# tf_key = tf_in_1s[step_size_s]
godwidget.window.setWindowTitle(
f'{fqmes} '
# f'tick:{symbol.tick_size} '
# f'tick:{mkt.tick_size} '
# f'step:{tf_key} '
)
# generate order mode side-pane UI
@ -1234,8 +1237,8 @@ async def display_symbol_data(
godwidget.pp_pane = pp_pane
# create top history view chart above the "main rt chart".
rt_linked = godwidget.rt_linked
hist_linked = godwidget.hist_linked
rt_linked: LinkedSplits = godwidget.rt_linked
hist_linked: LinkedSplits = godwidget.hist_linked
# NOTE: here we insert the slow-history chart set into
# the fast chart's splitter -> so it's a splitter of charts
@ -1279,17 +1282,17 @@ async def display_symbol_data(
# TODO NOTE: THIS CONTROLS WHAT SYMBOL IS USED FOR ORDER MODE
# SUBMISSIONS, we need to make this switch based on selection.
rt_linked._symbol = flume.mkt
hist_linked._symbol = flume.mkt
rt_linked.set_mkt_info(flume.mkt)
hist_linked.set_mkt_info(flume.mkt)
ohlcv: ShmArray = flume.rt_shm
hist_ohlcv: ShmArray = flume.hist_shm
symbol = flume.mkt
fqme = symbol.fqme
mkt: MktPair = flume.mkt
fqme = mkt.fqme
hist_chart = hist_linked.plot_ohlc_main(
symbol,
mkt,
hist_ohlcv,
flume,
# in the case of history chart we explicitly set `False`
@ -1311,7 +1314,7 @@ async def display_symbol_data(
hist_linked.cursor.always_show_xlabel = False
rt_chart = rt_linked.plot_ohlc_main(
symbol,
mkt,
ohlcv,
flume,
# in the case of history chart we explicitly set `False`
@ -1378,8 +1381,8 @@ async def display_symbol_data(
ohlcv: ShmArray = flume.rt_shm
hist_ohlcv: ShmArray = flume.hist_shm
symbol = flume.mkt
fqme = symbol.fqme
mkt = flume.mkt
fqme = mkt.fqme
hist_pi = hist_chart.overlay_plotitem(
name=fqme,

View File

@ -29,7 +29,6 @@ from typing import (
Any,
)
import numpy as np
import msgspec
import tractor
import pyqtgraph as pg
@ -428,7 +427,7 @@ class FspAdmin:
in self._flow_registry.items()
],
) as (ctx, last_index),
) as (ctx, _),
ctx.open_stream() as stream,
):
@ -486,8 +485,10 @@ class FspAdmin:
readonly=True,
)
portal = self.cluster.get(worker_name) or self.rr_next_portal()
provider_tag = portal.channel.uid
portal: tractor.Portal = (
self.cluster.get(worker_name)
or self.rr_next_portal()
)
# TODO: this should probably be turned into a
# ``Cascade`` type which describes the routing

View File

@ -45,7 +45,10 @@ from ..calc import (
pnl,
puterize,
)
from ..accounting._allocate import Allocator
from ..accounting import (
Allocator,
MktPair,
)
from ..accounting import (
Position,
)
@ -244,7 +247,7 @@ class SettingsPane:
# a ``brokerd`) then error and switch back to the last
# selection.
if tracker is None:
sym = old_tracker.charts[0].linked.symbol.key
sym: str = old_tracker.charts[0].linked.mkt.fqme
log.error(
f'Account `{account_name}` can not be set for {sym}'
)
@ -415,9 +418,10 @@ class SettingsPane:
'''
mode = self.order_mode
sym = mode.chart.linked.symbol
mkt: MktPair = mode.chart.linked.mkt
size = tracker.live_pp.size
flume: Feed = mode.feed.flumes[sym.fqme]
fqme: str = mkt.fqme
flume: Feed = mode.feed.flumes[fqme]
pnl_value = 0
if size:
@ -430,7 +434,6 @@ class SettingsPane:
# maybe start update task
global _pnl_tasks
fqme = sym.fqme
if fqme not in _pnl_tasks:
_pnl_tasks[fqme] = True
self.order_mode.nursery.start_soon(
@ -555,7 +558,7 @@ class Nav(Struct):
'''
for key, chart in self.charts.items():
size_digits = size_digits or chart.linked.symbol.lot_size_digits
size_digits = size_digits or chart.linked.mkt.size_tick_digits
line = self.lines.get(key)
level_marker = self.level_markers[key]
pp_label = self.pp_labels[key]

View File

@ -23,7 +23,10 @@ WARNING: this code likely doesn't work at all (yet)
"""
import numpy as np
import pyqtgraph as pg
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import (
QtCore,
QtWidgets,
)
from .quantdom.charts import CenteredTextItem
from .quantdom.base import Quotes

View File

@ -36,17 +36,18 @@ import trio
from PyQt5.QtCore import Qt
from .. import config
from ..accounting import Position
from ..accounting._allocate import (
from ..accounting import (
Allocator,
Position,
mk_allocator,
MktPair,
Symbol,
)
from ..clearing._client import (
open_ems,
OrderClient,
)
from ._style import _font
from ..accounting._mktinfo import Symbol
from ..accounting import MktPair
from ..data.feed import (
Feed,
Flume,
@ -93,7 +94,7 @@ class Dialog(Struct):
order: Order
symbol: str
lines: list[LevelLine]
last_status_close: Callable = lambda: None
last_status_close: Callable | None = None
msgs: dict[str, dict] = {}
fills: dict[str, Any] = {}
@ -288,10 +289,10 @@ class OrderMode:
# since that's illogical / a no-op.
return
symbol = self.chart.linked.symbol
mkt: MktPair = self.chart.linked.mkt
# NOTE : we could also use instead,
# symbol.quantize(price, quantity_type='price')
# mkt.quantize(price, quantity_type='price')
# but it returns a Decimal and it's probably gonna
# be slower?
# TODO: should we be enforcing this precision
@ -301,7 +302,7 @@ class OrderMode:
price = round(
price,
ndigits=symbol.tick_size_digits,
ndigits=mkt.size_tick_digits,
)
order = self._staged_order = Order(
@ -309,8 +310,8 @@ class OrderMode:
price=price,
account=self.current_pp.alloc.account,
size=0,
symbol=symbol,
brokers=[symbol.broker],
symbol=mkt.fqme,
brokers=[mkt.broker],
oid='', # filled in on submit
exec_mode=trigger_type, # dark or live
)
@ -457,10 +458,10 @@ class OrderMode:
the EMS, adjust mirrored level line on secondary chart.
'''
mktinfo = self.chart.linked.symbol
mktinfo: MktPair = self.chart.linked.mkt
level = round(
line.value(),
ndigits=mktinfo.tick_size_digits,
ndigits=mktinfo.size_tick_digits,
)
# updated by level change callback set in ``.new_line_from_order()``
dialog = line.dialog
@ -497,7 +498,9 @@ class OrderMode:
# a submission is the start of a new order dialog
dialog = self.dialogs[uuid]
dialog.lines = lines
dialog.last_status_close()
cls: Callable | None = dialog.last_status_close
if cls:
cls()
for line in lines:
@ -549,7 +552,7 @@ class OrderMode:
# XXX: seems to fail on certain types of races?
# assert len(lines) == 2
if lines:
flume: Flume = self.feed.flumes[chart.linked.symbol.fqme]
flume: Flume = self.feed.flumes[chart.linked.mkt.fqme]
_, _, ratio = flume.get_ds_info()
for chart, shm in [
@ -740,15 +743,15 @@ async def open_order_mode(
lines = LineEditor(godw=godw)
arrows = ArrowEditor(godw=godw)
# symbol id
symbol = chart.linked.symbol
# market endpoint info
mkt: MktPair = chart.linked.mkt
# map of per-provider account keys to position tracker instances
trackers: dict[str, PositionTracker] = {}
# load account names from ``brokers.toml``
accounts_def = config.load_accounts(
providers=[symbol.broker],
providers=[mkt.broker],
)
# XXX: ``brokerd`` delivers a set of account names that it
@ -771,17 +774,17 @@ async def open_order_mode(
# net-zero pp
startup_pp = Position(
mkt=symbol,
mkt=mkt,
size=0,
ppu=0,
# XXX: BLEH, do we care about this on the client side?
bs_mktid=symbol.key,
bs_mktid=mkt.key,
)
# allocator config
alloc = mk_allocator(
symbol=symbol,
alloc: Allocator = mk_allocator(
mkt=mkt,
account=account_name,
# if this startup size is greater the allocator limit,