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
parent
db075b81ac
commit
cc88300ac5
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue