Skip to content

Log linearized curve overlays #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 96 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
223e9d9
Add first-draft `PlotItemOverlay.group_maxmin()`
goodboy Jan 15, 2023
fc73bec
Drop Qt interaction signal usage
goodboy Jan 18, 2023
f89e11f
Right, handle y-ranging multiple paths per plot
goodboy Jan 19, 2023
8a5b9f4
Rename `.maybe_downsample_graphics()` -> `.interact_graphics_cycle()`
goodboy Jan 19, 2023
dfc3525
First draft, group y-minmax transform algo
goodboy Jan 19, 2023
52ac105
2nd try: dispersion normalize y-ranges around median
goodboy Jan 20, 2023
052ce65
3rdz the charm: log-linearize minor y-ranges to a major
goodboy Jan 20, 2023
0591cb0
Clean up cross-curve intersect point indexing
goodboy Jan 21, 2023
7e421ba
Drop `.group_maxmin()`
goodboy Jan 21, 2023
c2dd255
Only remove axis from scene when in one
goodboy Jan 21, 2023
fc6ccc3
Only set the major curve's range once (per render cycle)
goodboy Jan 22, 2023
a0fb84f
Just warn log on bad intersect indexing errors (for now)
goodboy Jan 22, 2023
32f21dc
Drop `update_graphics_from_flow()`
goodboy Jan 22, 2023
89e2e7f
Adjust `.update_graphics()` to expect `in_view: bool` in `_fsp.py`
goodboy Jan 22, 2023
896259d
When only one curve is in view, skip group ranging
goodboy Jan 22, 2023
776ffd2
Factor curve-dispersion sorting into primary loop
goodboy Jan 22, 2023
481f1b3
Fix intersect detection using time indexing
goodboy Jan 23, 2023
497174c
Add full profiling to `.interact_graphics_cycle()`
goodboy Jan 24, 2023
9418f53
Speed up ranging in display loop
goodboy Jan 24, 2023
ec8679a
Add `Viz.median_from_range()`
goodboy Jan 24, 2023
cf67c79
Use new cached median method in overlay scaling
goodboy Jan 24, 2023
e06d4b4
Add linked charts guard-flag for use in display loop
goodboy Jan 24, 2023
5eaca18
Don't skip overlay scaling in disp-loop for now
goodboy Jan 24, 2023
ea84505
Don't scale overlays on linked from display loop
goodboy Jan 24, 2023
517c68f
Use `._pathops.slice_from_time()` for overlay intersects
goodboy Jan 24, 2023
5a8fd42
Lul, actually scaled main chart from linked set
goodboy Jan 24, 2023
7ebcd6d
Comment out all median usage, turns out it's uneeded..
goodboy Jan 24, 2023
246d070
Drop old loop and wait on fsp engine tasks startups
goodboy Jan 24, 2023
5dd69b2
Better handle dynamic registry sampler broadcasts
goodboy Jan 25, 2023
9930f25
Move axis hiding into `.overlay_plotitem()`
goodboy Jan 21, 2023
7a83a72
Update profile msgs to new apis
goodboy Jan 30, 2023
2ed43c0
Fix profiler f-string
goodboy Feb 2, 2023
c690e14
Don't unset `Viz.render` for unit vlm
goodboy Feb 3, 2023
91d41eb
Allow y-range input via a `yranges: dict[Viz, tuple[float, float]]`
goodboy Feb 3, 2023
25cf8df
Pass windowed y-mxmn to `.interact_graphics_cycle()` calls in display…
goodboy Feb 3, 2023
6ea64a7
Iterate all charts (widgets) when only one overlay
goodboy Feb 6, 2023
3daee0c
Disable overlay scaling on per-symbol-feed updates
goodboy Feb 6, 2023
c57567a
No-overlays, y-ranging optimizations
goodboy Feb 8, 2023
a7db6ad
Always set the `ChartView._viz` for each plot
goodboy Feb 8, 2023
0a93931
Only set the specific view's yrange per quote
goodboy Feb 8, 2023
c646b43
Incrementally set vlm chart yrange per quote
goodboy Feb 8, 2023
74c215d
Lel, always meant to no-cache the step curve..
goodboy Feb 8, 2023
972b723
Skip overlay transform calcs on common-pi curves
goodboy Feb 8, 2023
db1e0a0
Only use last `ChartView._yrange` if set
goodboy Feb 9, 2023
2d73598
Go back to no-cache on OHLC downsample line
goodboy Feb 9, 2023
cda3bcc
Expose `._set_yrange()` kwargs via `yrange_kwargs: dict`
goodboy Feb 9, 2023
091afcc
Dynamically adjust y-range margin in display loop
goodboy Feb 9, 2023
3dc1f66
Go back to caching on all curves
goodboy Feb 9, 2023
a6d1053
Facepalm, align overlay plot view exactly to parent
goodboy Feb 11, 2023
cb5e2d4
Add hack-zone UI REPL access via `ctl-u`
goodboy Feb 11, 2023
d5ba26c
Try to hide all axes even when removed
goodboy Feb 11, 2023
5f470d6
Rework overlay pin technique: "align to first"
goodboy Feb 16, 2023
54ecb09
Remove vlm chart again, drop lotsa fsp cruft
goodboy Feb 21, 2023
753e991
Adjust `.ui` modules to new set-style "optional" annots
goodboy Feb 21, 2023
35c40e8
Move overlay transform logic to new `.ui.view_mode`
goodboy Feb 21, 2023
98b7d78
Drop (now) unused major curve mx/mn variables
goodboy Feb 22, 2023
26690b0
Make slow chart a teensie bit smaller
goodboy Feb 22, 2023
62e0889
Add `Viz.view_state: ViewState`
goodboy Feb 24, 2023
8fd5c67
Drop last lingering usage of `Viz.bars_range()`
goodboy Feb 26, 2023
29418e9
Avoid index-from-time slicing including gaps
goodboy Feb 27, 2023
4d11c5c
Add cached dispersion methods to `Viz`
goodboy Feb 27, 2023
6601dea
Support "pin-to-target-curve" overlay method again
goodboy Feb 27, 2023
c1ea855
Back-rescale previous (minor) curves from latest
goodboy Feb 27, 2023
01ea706
Better doc string, use `Viz.vs: ViewState`
goodboy Feb 28, 2023
45e97dd
Solve a final minor-should-rescale edge case
goodboy Feb 28, 2023
8c392fd
Drop a bunch of commented/uneeded cruft
goodboy Feb 28, 2023
993bb47
Drop passing overlay method from viewbox to view-mode handler
goodboy Feb 28, 2023
7579863
Skip overlay handling when `N < 2` are detected
goodboy Feb 28, 2023
94f0ef1
Repair x-label datetime labels when in array-index mode
goodboy Feb 28, 2023
75807f4
Rename overlay technique var to `method`
goodboy Feb 28, 2023
77401a9
Simplify `FlowGraphics.x_last()` logics
goodboy Feb 28, 2023
eda283f
Fix focal min calc after switching to `Viz.datums_range()`..
goodboy Feb 28, 2023
7564292
Move cache-reset ctx mngr to parent type: `FlowGraphics.reset_cache()`
goodboy Feb 28, 2023
9b96059
Add per-chart `Viz`/overlay graphics iterator method
goodboy Feb 28, 2023
f7dfe57
Disable coordinate caching during interaction
goodboy Feb 28, 2023
eea8504
Handle yrange not set on view vase for vlm fsp plot
goodboy Mar 1, 2023
9c8bd9b
Expand mxmn view y-margins back to 0.06
goodboy Mar 2, 2023
6690bd4
Drop remaining non-usage of `ChartPlotWidget.maxmin()`
goodboy Mar 4, 2023
12bee71
Add `do_min_bars: bool` flag to `Viz.default_view()`
goodboy Mar 4, 2023
7e6e04b
Drop remaining usage of `ChartPlotWidget.default_view()`
goodboy Mar 6, 2023
57d56c4
Facepalm: set `Viz.ViewState.yrange` even on cache hits..
goodboy Mar 6, 2023
fc98d66
Fix curve up-sampling on `'r'` hotkey
goodboy Mar 6, 2023
05aee4a
Tweak debug printing to display y-mxmn per viz
goodboy Mar 6, 2023
4bb580a
Don't `@lru_cache` on `Viz.i_from_t()`, since view state..
goodboy Mar 6, 2023
51f3733
Handle "target-is-shorter-then-pinned" case
goodboy Mar 7, 2023
712f1a4
Require `step: float` input to `slice_from_time()`
goodboy Mar 7, 2023
3292674
Always pass `step` to `slice_from_time()` in the `Viz`
goodboy Mar 7, 2023
8d1c713
Always pass step to `slice_from_time()` in view mode
goodboy Mar 7, 2023
5958ace
Add (commented) draft 1min OHLC time index logging
goodboy Mar 7, 2023
8a87e5f
Remove leftover debug print in cache reset meth
goodboy Mar 7, 2023
12e196a
Catch `KeyError` on bcast errors which pop the sub
goodboy Mar 7, 2023
32339cb
Always show a minimum bars during startup
goodboy Mar 8, 2023
3066b15
Handle (shorter supported) minor-curve not-in-view
goodboy Mar 9, 2023
5c697de
Presume never handling not-in-view case for minor curves
goodboy Mar 9, 2023
1aab9f1
Actually yes, we need to handle empty in-view range..
goodboy Mar 10, 2023
889e920
Short-circuit rendering on no 1d-data; avoid m4 layer crash
goodboy Mar 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions piker/data/_pathops.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def slice_from_time(
arr: np.ndarray,
start_t: float,
stop_t: float,
step: int | None = None,
step: float, # sampler period step-diff

) -> slice:
'''
Expand Down Expand Up @@ -324,12 +324,6 @@ def slice_from_time(
# end of the input array.
read_i_max = arr.shape[0]

# TODO: require this is always passed in?
if step is None:
step = round(t_last - times[-2])
if step == 0:
step = 1

# compute (presumed) uniform-time-step index offsets
i_start_t = floor(start_t)
read_i_start = floor(((i_start_t - t_first) // step)) - 1
Expand Down Expand Up @@ -395,7 +389,7 @@ def slice_from_time(
# f'diff: {t_diff}\n'
# f'REMAPPED START i: {read_i_start} -> {new_read_i_start}\n'
# )
read_i_start = new_read_i_start - 1
read_i_start = new_read_i_start

t_iv_stop = times[read_i_stop - 1]
if (
Expand All @@ -412,7 +406,7 @@ def slice_from_time(
times[read_i_start:],
# times,
i_stop_t,
side='left',
side='right',
)

if (
Expand Down
81 changes: 61 additions & 20 deletions piker/data/_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ class Sampler:
# holds all the ``tractor.Context`` remote subscriptions for
# a particular sample period increment event: all subscribers are
# notified on a step.
# subscribers: dict[int, list[tractor.MsgStream]] = {}
subscribers: defaultdict[
float,
list[
Expand Down Expand Up @@ -240,8 +239,11 @@ async def broadcast(
subscribers for a given sample period.

'''
pair: list[float, set]
pair = self.subscribers[period_s]

last_ts: float
subs: set
last_ts, subs = pair

task = trio.lowlevel.current_task()
Expand All @@ -253,25 +255,35 @@ async def broadcast(
# f'consumers: {subs}'
)
borked: set[tractor.MsgStream] = set()
for stream in subs:
sent: set[tractor.MsgStream] = set()
while True:
try:
await stream.send({
'index': time_stamp or last_ts,
'period': period_s,
})
except (
trio.BrokenResourceError,
trio.ClosedResourceError
):
log.error(
f'{stream._ctx.chan.uid} dropped connection'
)
borked.add(stream)
for stream in (subs - sent):
try:
await stream.send({
'index': time_stamp or last_ts,
'period': period_s,
})
sent.add(stream)

except (
trio.BrokenResourceError,
trio.ClosedResourceError
):
log.error(
f'{stream._ctx.chan.uid} dropped connection'
)
borked.add(stream)
else:
break
except RuntimeError:
log.warning(f'Client subs {subs} changed while broadcasting')
continue

for stream in borked:
try:
subs.remove(stream)
except ValueError:
except KeyError:
log.warning(
f'{stream._ctx.chan.uid} sub already removed!?'
)
Expand Down Expand Up @@ -419,7 +431,7 @@ async def maybe_open_samplerd(
loglevel: str | None = None,
**kwargs,

) -> tractor._portal.Portal: # noqa
) -> tractor.Portal: # noqa
'''
Client-side helper to maybe startup the ``samplerd`` service
under the ``pikerd`` tree.
Expand Down Expand Up @@ -609,6 +621,14 @@ async def sample_and_broadcast(
fqsn = f'{broker_symbol}.{brokername}'
lags: int = 0

# TODO: speed up this loop in an AOT compiled lang (like
# rust or nim or zig) and/or instead of doing a fan out to
# TCP sockets here, we add a shm-style tick queue which
# readers can pull from instead of placing the burden of
# broadcast on solely on this `brokerd` actor. see issues:
# - https://github.com/pikers/piker/issues/98
# - https://github.com/pikers/piker/issues/107

for (stream, tick_throttle) in subs.copy():
try:
with trio.move_on_after(0.2) as cs:
Expand Down Expand Up @@ -738,9 +758,6 @@ def frame_ticks(
ticks_by_type[ttype].append(tick)


# TODO: a less naive throttler, here's some snippets:
# token bucket by njs:
# https://gist.github.com/njsmith/7ea44ec07e901cb78ebe1dd8dd846cb9
async def uniform_rate_send(

rate: float,
Expand All @@ -750,8 +767,22 @@ async def uniform_rate_send(
task_status: TaskStatus = trio.TASK_STATUS_IGNORED,

) -> None:
'''
Throttle a real-time (presumably tick event) stream to a uniform
transmissiom rate, normally for the purposes of throttling a data
flow being consumed by a graphics rendering actor which itself is limited
by a fixed maximum display rate.

# try not to error-out on overruns of the subscribed (chart) client
Though this function isn't documented (nor was intentially written
to be) a token-bucket style algo, it effectively operates as one (we
think?).

TODO: a less naive throttler, here's some snippets:
token bucket by njs:
https://gist.github.com/njsmith/7ea44ec07e901cb78ebe1dd8dd846cb9

'''
# try not to error-out on overruns of the subscribed client
stream._ctx._backpressure = True

# TODO: compute the approx overhead latency per cycle
Expand Down Expand Up @@ -848,6 +879,16 @@ async def uniform_rate_send(
# rate timing exactly lul
try:
await stream.send({sym: first_quote})
except tractor.RemoteActorError as rme:
if rme.type is not tractor._exceptions.StreamOverrun:
raise
ctx = stream._ctx
chan = ctx.chan
log.warning(
'Throttled quote-stream overrun!\n'
f'{sym}:{ctx.cid}@{chan.uid}'
)

except (
# NOTE: any of these can be raised by ``tractor``'s IPC
# transport-layer and we want to be highly resilient
Expand Down
3 changes: 3 additions & 0 deletions piker/data/feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,9 @@ async def open_feed(
(brokermod, bfqsns),
) in zip(ctxs, providers.items()):

# NOTE: do it asap to avoid overruns during multi-feed setup?
ctx._backpressure = backpressure

for fqsn, flume_msg in flumes_msg_dict.items():
flume = Flume.from_msg(flume_msg)
assert flume.symbol.fqsn == fqsn
Expand Down
4 changes: 2 additions & 2 deletions piker/ui/_annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
Annotations for ur faces.

"""
from typing import Callable, Optional
from typing import Callable

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QPointF, QRectF
Expand Down Expand Up @@ -105,7 +105,7 @@ def __init__(
get_level: Callable[..., float],
size: float = 20,
keep_in_view: bool = True,
on_paint: Optional[Callable] = None,
on_paint: Callable | None = None,

) -> None:

Expand Down
24 changes: 14 additions & 10 deletions piker/ui/_axes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# piker: trading gear for hackers
# Copyright (C) Tyler Goodlet (in stewardship for piker0)
# Copyright (C) Tyler Goodlet (in stewardship for pikers)

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
Expand All @@ -20,7 +20,7 @@
"""
from __future__ import annotations
from functools import lru_cache
from typing import Optional, Callable
from typing import Callable
from math import floor

import numpy as np
Expand Down Expand Up @@ -60,7 +60,8 @@ def __init__(
**kwargs
)

# XXX: pretty sure this makes things slower
# XXX: pretty sure this makes things slower!
# no idea why given we only move labels for the most part?
# self.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)

self.pi = plotitem
Expand Down Expand Up @@ -190,7 +191,7 @@ def __init__(
*args,
min_tick: int = 2,
title: str = '',
formatter: Optional[Callable[[float], str]] = None,
formatter: Callable[[float], str] | None = None,
**kwargs

) -> None:
Expand All @@ -202,8 +203,8 @@ def __init__(
def set_title(
self,
title: str,
view: Optional[ChartView] = None,
color: Optional[str] = None,
view: ChartView | None = None,
color: str | None = None,

) -> Label:
'''
Expand Down Expand Up @@ -303,8 +304,9 @@ def _indexes_to_timestrs(
viz = chart._vizs[chart.name]
shm = viz.shm
array = shm.array
times = array['time']
i_0, i_l = times[0], times[-1]
ifield = viz.index_field
index = array[ifield]
i_0, i_l = index[0], index[-1]

# edge cases
if (
Expand All @@ -316,11 +318,13 @@ def _indexes_to_timestrs(
(indexes[0] > i_0
and indexes[-1] > i_l)
):
# print(f"x-label indexes empty edge case: {indexes}")
return []

if viz.index_field == 'index':
arr_len = times.shape[0]
if ifield == 'index':
arr_len = index.shape[0]
first = shm._first.value
times = array['time']
epochs = times[
list(
map(
Expand Down
Loading