view_mode: handle duplicate overlay dispersions

Discovered due to originally having a history loading bug between
btcusdt futes display where the same time series was being loaded into
the graphics system, this avoids the issue where 2 (or more) curves are
measured to have the same dispersion and thus do not get added as unique
entries to the `overlay_table: dict[float, tuple]` during the scaling
phase..

Practically speaking this should never really be a problem if the curves
(and their backing timeseries) are indeed unique but keying the
overlay table by the dispersion and the `Viz` is a minimal performance
hit when looping the sorted table and is a lot nicer then you **do want
to show** duplicate curves then having one overlay just not be ranged
correctly at all XD
basic_buy_bot
Tyler Goodlet 2023-06-14 13:53:55 -04:00
parent 0f8c685735
commit 6a1c49be4e
1 changed files with 30 additions and 13 deletions

View File

@ -19,6 +19,7 @@ Overlay (aka multi-chart) UX machinery.
''' '''
from __future__ import annotations from __future__ import annotations
from operator import itemgetter
from typing import ( from typing import (
Any, Any,
Literal, Literal,
@ -197,15 +198,17 @@ def overlay_viewlists(
) -> None: ) -> None:
''' '''
Calculate and apply y-domain (axis y-range) multi-curve overlay adjustments Calculate and apply y-domain (axis y-range) multi-curve overlay
a set of ``plots`` based on the requested ``method``. adjustments a set of ``plots`` based on the requested
``method``.
''' '''
chart_name: str chart_name: str
chart: ChartPlotWidget chart: ChartPlotWidget
for chart_name, chart in plots.items(): for chart_name, chart in plots.items():
overlay_viz_items = chart._vizs.items() overlay_viz_items: dict = chart._vizs
# Common `PlotItem` maxmin table; presumes that some path # Common `PlotItem` maxmin table; presumes that some path
# graphics (and thus their backing data sets) are in the # graphics (and thus their backing data sets) are in the
@ -271,6 +274,7 @@ def overlay_viewlists(
# determine auto-ranging input for `._set_yrange()`. # determine auto-ranging input for `._set_yrange()`.
# this is primarly used for our so called "log-linearized # this is primarly used for our so called "log-linearized
# multi-plot" overlay technique. # multi-plot" overlay technique.
# vizs_by_disp: list[tuple[float, Viz]] = []
overlay_table: dict[ overlay_table: dict[
float, float,
tuple[ tuple[
@ -288,7 +292,7 @@ def overlay_viewlists(
] = {} ] = {}
# multi-curve overlay processing stage # multi-curve overlay processing stage
for name, viz in overlay_viz_items: for name, viz in overlay_viz_items.items():
out = _maybe_calc_yrange( out = _maybe_calc_yrange(
viz, viz,
@ -356,7 +360,7 @@ def overlay_viewlists(
# returns scalars # returns scalars
r_up = (ymx - y_ref) / y_ref r_up = (ymx - y_ref) / y_ref
r_down = (ymn - y_ref) / y_ref r_down = (ymn - y_ref) / y_ref
disp = r_up - r_down disp = round(r_up - r_down, ndigits=16)
msg = ( msg = (
f'Viz[{viz.name}][{key}]: @{chart_name}\n' f'Viz[{viz.name}][{key}]: @{chart_name}\n'
@ -489,7 +493,15 @@ def overlay_viewlists(
# register curves by a "full" dispersion metric for # register curves by a "full" dispersion metric for
# later sort order in the overlay (technique # later sort order in the overlay (technique
# ) application loop below. # ) application loop below.
overlay_table[disp] = ( pair: tuple[float, Viz] = (disp, viz)
# time series are so similar they have same
# dispersion with `float` precision..
if entry := overlay_table.get(pair):
raise RuntimeError('Duplicate entry!? -> {entry}')
# vizs_by_disp.append(pair)
overlay_table[pair] = (
viz.plot.vb, viz.plot.vb,
viz, viz,
y_ref, y_ref,
@ -540,6 +552,7 @@ def overlay_viewlists(
mxmns_by_common_pi mxmns_by_common_pi
and not overlay_table and not overlay_table
): ):
print("WAATT THE FUCK")
# move to next chart in linked set since # move to next chart in linked set since
# no overlay transforming is needed. # no overlay transforming is needed.
continue continue
@ -548,7 +561,7 @@ def overlay_viewlists(
r_up_mx: float r_up_mx: float
r_dn_mn: float r_dn_mn: float
mx_disp = max(overlay_table) mx_pair: tuple = max(overlay_table, key=itemgetter(0))
if debug_print: if debug_print:
# print overlay table in descending dispersion order # print overlay table in descending dispersion order
@ -564,11 +577,11 @@ def overlay_viewlists(
) )
if method == 'loglin_ref_to_curve': if method == 'loglin_ref_to_curve':
mx_entry = overlay_table.pop(mx_disp) mx_entry = overlay_table.pop(mx_pair)
else: else:
# TODO: for pin to first-in-view we need to no pop this from the # TODO: for pin to first-in-view we need to NOT pop this from the
# table, but can we simplify below code even more? # table, but can we simplify below code even more?
mx_entry = overlay_table[mx_disp] mx_entry = overlay_table[mx_pair]
( (
mx_view, # viewbox mx_view, # viewbox
@ -599,7 +612,11 @@ def overlay_viewlists(
tuple[Viz, float, float, float, float] tuple[Viz, float, float, float, float]
] = {} ] = {}
for full_disp in reversed(overlay_table): for pair in sorted(
overlay_table,
key=itemgetter(0),
reverse=True,
):
( (
view, view,
viz, viz,
@ -610,7 +627,7 @@ def overlay_viewlists(
minor_in_view, minor_in_view,
r_up, r_up,
r_dn, r_dn,
) = overlay_table[full_disp] ) = overlay_table[pair]
key = 'open' if viz.is_ohlc else viz.name key = 'open' if viz.is_ohlc else viz.name
xref = minor_in_view[0]['time'] xref = minor_in_view[0]['time']
@ -839,7 +856,7 @@ def overlay_viewlists(
print( print(
'SCALING PHASE' + '-'*100 + '\n\n' 'SCALING PHASE' + '-'*100 + '\n\n'
'_________MAJOR INFO___________\n' '_________MAJOR INFO___________\n'
f'SIGMA MAJOR C: {mx_viz.name} -> {mx_disp}\n' f'SIGMA MAJOR C: {mx_viz.name} -> {mx_pair[0]}\n'
f'UP MAJOR C: {upt.viz.name} with disp: {upt.rng}\n' f'UP MAJOR C: {upt.viz.name} with disp: {upt.rng}\n'
f'DOWN MAJOR C: {dnt.viz.name} with disp: {dnt.rng}\n' f'DOWN MAJOR C: {dnt.viz.name} with disp: {dnt.rng}\n'
f'xref: {mx_xref}\n' f'xref: {mx_xref}\n'