Fix L1 updates to be like TWS

I think this gets us to the same output as TWS both on booktrader and
the quote details pane. In theory there might be logic needed to
decreases an L1 queue size on trades but can't seem to get it without
getting -ves displayed occasionally - thus leaving it for now.

Also, fix the max-min streaming logic to actually do its job, lel.
bar_select
Tyler Goodlet 2020-11-05 12:08:29 -05:00
parent db075b81ac
commit cc88300ac5
1 changed files with 67 additions and 37 deletions

View File

@ -34,10 +34,7 @@ from ._style import (
from ..data._source import Symbol from ..data._source import Symbol
from .. import brokers from .. import brokers
from .. import data from .. import data
from ..data import ( from ..data import maybe_open_shm_array
iterticks,
maybe_open_shm_array,
)
from ..log import get_logger from ..log import get_logger
from ._exec import run_qtractor, current_screen from ._exec import run_qtractor, current_screen
from ._interaction import ChartView from ._interaction import ChartView
@ -583,7 +580,12 @@ class ChartPlotWidget(pg.PlotWidget):
if self._static_yrange is not None: if self._static_yrange is not None:
ylow, yhigh = self._static_yrange ylow, yhigh = self._static_yrange
else: # determine max, min y values in viewable x-range elif yrange is not None:
ylow, yhigh = yrange
else:
# Determine max, min y values in viewable x-range from data.
# Make sure min bars/datums on screen is adhered.
l, lbar, rbar, r = self.bars_range() l, lbar, rbar, r = self.bars_range()
@ -794,6 +796,10 @@ async def chart_from_quotes(
last_price_sticky = chart._ysticks[chart.name] last_price_sticky = chart._ysticks[chart.name]
def maxmin(): def maxmin():
# TODO: implement this
# https://arxiv.org/abs/cs/0610046
# https://github.com/lemire/pythonmaxmin
array = chart._array array = chart._array
last_bars_range = chart.bars_range() last_bars_range = chart.bars_range()
l, lbar, rbar, r = last_bars_range l, lbar, rbar, r = last_bars_range
@ -804,24 +810,27 @@ async def chart_from_quotes(
last_bars_range, last_mx, last_mn = maxmin() last_bars_range, last_mx, last_mn = maxmin()
chart.default_view() chart.default_view()
l1 = L1Labels(chart)
last_bid = last_ask = ohlcv.array[-1]['close'] # last_bid = last_ask = ohlcv.array[-1]['close']
l1 = L1Labels(chart)
async for quotes in stream: async for quotes in stream:
for sym, quote in quotes.items(): for sym, quote in quotes.items():
for tick in quote.get('ticks', ()): for tick in quote.get('ticks', ()):
# for tick in iterticks(quote, type='trade'):
# print(f"CHART: {quote['symbol']}: {tick}")
ticktype = tick.get('type') ticktype = tick.get('type')
price = tick.get('price') price = tick.get('price')
size = tick.get('size')
if ticktype in ('trade', 'utrade'): if ticktype in ('trade', 'utrade'):
array = ohlcv.array array = ohlcv.array
# update price sticky(s) # update price sticky(s)
last = array[-1] last = array[-1]
last_price_sticky.update_from_data(*last[['index', 'close']]) last_price_sticky.update_from_data(
*last[['index', 'close']]
)
# update price bar # update price bar
chart.update_ohlc_from_array( chart.update_ohlc_from_array(
@ -829,44 +838,65 @@ async def chart_from_quotes(
array, array,
) )
# TODO: we need a streaming minmax algorithm here to
brange, mx, mn = maxmin()
if brange != last_bars_range:
if mx > last_mx or mn < last_mn:
# avoid running this every cycle.
chart._set_yrange()
# save for next cycle
last_mx, last_mn = mx, mn
if vwap_in_history: if vwap_in_history:
# update vwap overlay line # update vwap overlay line
chart.update_curve_from_array('vwap', ohlcv.array) chart.update_curve_from_array('vwap', ohlcv.array)
# TODO: # TODO:
# - eventually we'll want to update bid/ask labels and # - eventually we'll want to update bid/ask labels
# other data as subscribed by underlying UI consumers. # and other data as subscribed by underlying UI
# - in theory we should be able to read buffer data # consumers.
# faster then msgs arrive.. needs some tinkering and testing # - in theory we should be able to read buffer data faster
# then msgs arrive.. needs some tinkering and testing
# if trade volume jumps above / below prior L1 price # if trade volume jumps above / below prior L1 price
# levels adjust bid / ask lines to match # levels adjust bid / ask lines to match
if price > last_ask:
# compute max and min trade values to display in view
# TODO: we need a streaming minmax algorithm here, see
# def above.
brange, mx_in_view, mn_in_view = maxmin()
# XXX: prettty sure this is correct?
# if ticktype in ('trade', 'last'):
if ticktype in ('last',): # 'size'):
label = {
l1.ask_label.level: l1.ask_label,
l1.bid_label.level: l1.bid_label,
}.get(price)
if label is not None:
label.size = size
label.update_from_data(0, price)
# on trades should we be knocking down
# the relevant L1 queue?
# label.size -= size
elif ticktype in ('ask', 'asize'):
l1.ask_label.size = size
l1.ask_label.update_from_data(0, price) l1.ask_label.update_from_data(0, price)
last_ask = price
elif price < last_bid: # update max price in view to keep ask on screen
mx_in_view = max(price, mx_in_view)
elif ticktype in ('bid', 'bsize'):
l1.bid_label.size = size
l1.bid_label.update_from_data(0, price) l1.bid_label.update_from_data(0, price)
last_bid = price
# update min price in view to keep bid on screen
mn_in_view = max(price, mn_in_view)
elif ticktype == 'ask': if mx_in_view > last_mx or mn_in_view < last_mn:
l1.ask_label.update_from_data(0, price) chart._set_yrange(yrange=(mn_in_view, mx_in_view))
last_ask = price last_mx, last_mn = mx_in_view, mn_in_view
elif ticktype == 'bid': if brange != last_bars_range:
l1.bid_label.update_from_data(0, price) # we **must always** update the last values due to
last_bid = price # the x-range change
last_mx, last_mn = mx_in_view, mn_in_view
last_bars_range = brange
async def chart_from_fsp( async def chart_from_fsp(