Move all pre-path formatting routines to `._pathops`, proto formatter type
parent
61218f30f5
commit
0db5451e47
|
@ -31,7 +31,6 @@ from typing import (
|
||||||
|
|
||||||
import msgspec
|
import msgspec
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.lib import recfunctions as rfn
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from PyQt5.QtGui import QPainterPath
|
from PyQt5.QtGui import QPainterPath
|
||||||
from PyQt5.QtCore import QLineF
|
from PyQt5.QtCore import QLineF
|
||||||
|
@ -44,9 +43,21 @@ from .._profile import (
|
||||||
# ms_slower_then,
|
# ms_slower_then,
|
||||||
)
|
)
|
||||||
from ._pathops import (
|
from ._pathops import (
|
||||||
|
by_index_and_key,
|
||||||
|
|
||||||
|
# Plain OHLC renderer
|
||||||
gen_ohlc_qpath,
|
gen_ohlc_qpath,
|
||||||
|
|
||||||
|
# OHLC -> line renderer
|
||||||
ohlc_to_line,
|
ohlc_to_line,
|
||||||
|
update_ohlc_to_line,
|
||||||
|
ohlc_flat_to_xy,
|
||||||
|
|
||||||
|
# step curve renderer
|
||||||
to_step_format,
|
to_step_format,
|
||||||
|
update_step_xy,
|
||||||
|
step_to_xy,
|
||||||
|
|
||||||
xy_downsample,
|
xy_downsample,
|
||||||
)
|
)
|
||||||
from ._ohlc import (
|
from ._ohlc import (
|
||||||
|
@ -75,55 +86,6 @@ log = get_logger(__name__)
|
||||||
# flows: dict[str, np.ndarray] = {}
|
# flows: dict[str, np.ndarray] = {}
|
||||||
|
|
||||||
|
|
||||||
def update_ohlc_to_line(
|
|
||||||
src_shm: ShmArray,
|
|
||||||
array_key: str,
|
|
||||||
src_update: np.ndarray,
|
|
||||||
slc: slice,
|
|
||||||
ln: int,
|
|
||||||
first: int,
|
|
||||||
last: int,
|
|
||||||
is_append: bool,
|
|
||||||
|
|
||||||
) -> np.ndarray:
|
|
||||||
|
|
||||||
fields = ['open', 'high', 'low', 'close']
|
|
||||||
return (
|
|
||||||
rfn.structured_to_unstructured(src_update[fields]),
|
|
||||||
slc,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def ohlc_flat_to_xy(
|
|
||||||
r: Renderer,
|
|
||||||
array: np.ndarray,
|
|
||||||
array_key: str,
|
|
||||||
vr: tuple[int, int],
|
|
||||||
|
|
||||||
) -> tuple[
|
|
||||||
np.ndarray,
|
|
||||||
np.nd.array,
|
|
||||||
str,
|
|
||||||
]:
|
|
||||||
# TODO: in the case of an existing ``.update_xy()``
|
|
||||||
# should we be passing in array as an xy arrays tuple?
|
|
||||||
|
|
||||||
# 2 more datum-indexes to capture zero at end
|
|
||||||
x_flat = r.x_data[r._xy_first:r._xy_last]
|
|
||||||
y_flat = r.y_data[r._xy_first:r._xy_last]
|
|
||||||
|
|
||||||
# slice to view
|
|
||||||
ivl, ivr = vr
|
|
||||||
x_iv_flat = x_flat[ivl:ivr]
|
|
||||||
y_iv_flat = y_flat[ivl:ivr]
|
|
||||||
|
|
||||||
# reshape to 1d for graphics rendering
|
|
||||||
y_iv = y_iv_flat.reshape(-1)
|
|
||||||
x_iv = x_iv_flat.reshape(-1)
|
|
||||||
|
|
||||||
return x_iv, y_iv, 'all'
|
|
||||||
|
|
||||||
|
|
||||||
def render_baritems(
|
def render_baritems(
|
||||||
flow: Flow,
|
flow: Flow,
|
||||||
graphics: BarItems,
|
graphics: BarItems,
|
||||||
|
@ -253,77 +215,6 @@ def render_baritems(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_step_xy(
|
|
||||||
src_shm: ShmArray,
|
|
||||||
array_key: str,
|
|
||||||
y_update: np.ndarray,
|
|
||||||
slc: slice,
|
|
||||||
ln: int,
|
|
||||||
first: int,
|
|
||||||
last: int,
|
|
||||||
is_append: bool,
|
|
||||||
|
|
||||||
) -> np.ndarray:
|
|
||||||
|
|
||||||
# for a step curve we slice from one datum prior
|
|
||||||
# to the current "update slice" to get the previous
|
|
||||||
# "level".
|
|
||||||
if is_append:
|
|
||||||
start = max(last - 1, 0)
|
|
||||||
end = src_shm._last.value
|
|
||||||
new_y = src_shm._array[start:end][array_key]
|
|
||||||
slc = slice(start, end)
|
|
||||||
|
|
||||||
else:
|
|
||||||
new_y = y_update
|
|
||||||
|
|
||||||
return (
|
|
||||||
np.broadcast_to(
|
|
||||||
new_y[:, None], (new_y.size, 2),
|
|
||||||
),
|
|
||||||
slc,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def step_to_xy(
|
|
||||||
r: Renderer,
|
|
||||||
array: np.ndarray,
|
|
||||||
array_key: str,
|
|
||||||
vr: tuple[int, int],
|
|
||||||
|
|
||||||
) -> tuple[
|
|
||||||
np.ndarray,
|
|
||||||
np.nd.array,
|
|
||||||
str,
|
|
||||||
]:
|
|
||||||
|
|
||||||
# 2 more datum-indexes to capture zero at end
|
|
||||||
x_step = r.x_data[r._xy_first:r._xy_last+2]
|
|
||||||
y_step = r.y_data[r._xy_first:r._xy_last+2]
|
|
||||||
|
|
||||||
lasts = array[['index', array_key]]
|
|
||||||
last = lasts[array_key][-1]
|
|
||||||
y_step[-1] = last
|
|
||||||
|
|
||||||
# slice out in-view data
|
|
||||||
ivl, ivr = vr
|
|
||||||
ys_iv = y_step[ivl:ivr+1]
|
|
||||||
xs_iv = x_step[ivl:ivr+1]
|
|
||||||
|
|
||||||
# flatten to 1d
|
|
||||||
y_iv = ys_iv.reshape(ys_iv.size)
|
|
||||||
x_iv = xs_iv.reshape(xs_iv.size)
|
|
||||||
|
|
||||||
# print(
|
|
||||||
# f'ys_iv : {ys_iv[-s:]}\n'
|
|
||||||
# f'y_iv: {y_iv[-s:]}\n'
|
|
||||||
# f'xs_iv: {xs_iv[-s:]}\n'
|
|
||||||
# f'x_iv: {x_iv[-s:]}\n'
|
|
||||||
# )
|
|
||||||
|
|
||||||
return x_iv, y_iv, 'all'
|
|
||||||
|
|
||||||
|
|
||||||
class Flow(msgspec.Struct): # , frozen=True):
|
class Flow(msgspec.Struct): # , frozen=True):
|
||||||
'''
|
'''
|
||||||
(Financial Signal-)Flow compound type which wraps a real-time
|
(Financial Signal-)Flow compound type which wraps a real-time
|
||||||
|
@ -786,20 +677,6 @@ class Flow(msgspec.Struct): # , frozen=True):
|
||||||
g.update()
|
g.update()
|
||||||
|
|
||||||
|
|
||||||
def by_index_and_key(
|
|
||||||
renderer: Renderer,
|
|
||||||
array: np.ndarray,
|
|
||||||
array_key: str,
|
|
||||||
vr: tuple[int, int],
|
|
||||||
|
|
||||||
) -> tuple[
|
|
||||||
np.ndarray,
|
|
||||||
np.ndarray,
|
|
||||||
np.ndarray,
|
|
||||||
]:
|
|
||||||
return array['index'], array[array_key], 'all'
|
|
||||||
|
|
||||||
|
|
||||||
class Renderer(msgspec.Struct):
|
class Renderer(msgspec.Struct):
|
||||||
|
|
||||||
flow: Flow
|
flow: Flow
|
||||||
|
@ -1210,7 +1087,7 @@ class Renderer(msgspec.Struct):
|
||||||
and do_append
|
and do_append
|
||||||
and not should_redraw
|
and not should_redraw
|
||||||
):
|
):
|
||||||
# print(f'{array_key} append len: {append_length}')
|
print(f'{array_key} append len: {append_length}')
|
||||||
new_x = x_out[-append_length - 2:] # slice_to_head]
|
new_x = x_out[-append_length - 2:] # slice_to_head]
|
||||||
new_y = y_out[-append_length - 2:] # slice_to_head]
|
new_y = y_out[-append_length - 2:] # slice_to_head]
|
||||||
profiler('sliced append path')
|
profiler('sliced append path')
|
||||||
|
@ -1236,6 +1113,7 @@ class Renderer(msgspec.Struct):
|
||||||
profiler('generated append qpath')
|
profiler('generated append qpath')
|
||||||
|
|
||||||
if use_fpath:
|
if use_fpath:
|
||||||
|
print(f'{self.flow.name}: FAST PATH')
|
||||||
# an attempt at trying to make append-updates faster..
|
# an attempt at trying to make append-updates faster..
|
||||||
if fast_path is None:
|
if fast_path is None:
|
||||||
fast_path = append_path
|
fast_path = append_path
|
||||||
|
|
|
@ -19,10 +19,12 @@ Super fast ``QPainterPath`` generation related operator routines.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import (
|
from typing import (
|
||||||
# Optional,
|
Optional,
|
||||||
|
Callable,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import msgspec
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.lib import recfunctions as rfn
|
from numpy.lib import recfunctions as rfn
|
||||||
from numba import njit, float64, int64 # , optional
|
from numba import njit, float64, int64 # , optional
|
||||||
|
@ -42,6 +44,56 @@ if TYPE_CHECKING:
|
||||||
from ._flows import Renderer
|
from ._flows import Renderer
|
||||||
|
|
||||||
|
|
||||||
|
def by_index_and_key(
|
||||||
|
renderer: Renderer,
|
||||||
|
array: np.ndarray,
|
||||||
|
array_key: str,
|
||||||
|
vr: tuple[int, int],
|
||||||
|
|
||||||
|
) -> tuple[
|
||||||
|
np.ndarray,
|
||||||
|
np.ndarray,
|
||||||
|
np.ndarray,
|
||||||
|
]:
|
||||||
|
return array['index'], array[array_key], 'all'
|
||||||
|
|
||||||
|
|
||||||
|
class IncrementalFormatter(msgspec.Struct):
|
||||||
|
|
||||||
|
shm: ShmArray
|
||||||
|
|
||||||
|
# optional pre-graphics xy formatted data which
|
||||||
|
# is incrementally updated in sync with the source data.
|
||||||
|
allocate_xy_nd: Optional[Callable[
|
||||||
|
[int, slice],
|
||||||
|
tuple[np.ndarray, np.nd.array]
|
||||||
|
]] = None
|
||||||
|
|
||||||
|
incr_update_xy_nd: Optional[Callable[
|
||||||
|
[int, slice], None]
|
||||||
|
] = None
|
||||||
|
|
||||||
|
# default just returns index, and named array from data
|
||||||
|
format_xy_nd_to_1d: Callable[
|
||||||
|
[np.ndarray, str],
|
||||||
|
tuple[np.ndarray]
|
||||||
|
] = by_index_and_key
|
||||||
|
|
||||||
|
x_nd: Optional[np.ndarray] = None
|
||||||
|
y_nd: Optional[np.ndarray] = None
|
||||||
|
|
||||||
|
x_1d: Optional[np.ndarray] = None
|
||||||
|
y_1d: Optional[np.ndarray] = None
|
||||||
|
|
||||||
|
# indexes which slice into the above arrays (which are allocated
|
||||||
|
# based on source data shm input size) and allow retrieving
|
||||||
|
# incrementally updated data.
|
||||||
|
# _xy_first: int = 0
|
||||||
|
# _xy_last: int = 0
|
||||||
|
xy_nd_start: int = 0
|
||||||
|
xy_nd_end: int = 0
|
||||||
|
|
||||||
|
|
||||||
def xy_downsample(
|
def xy_downsample(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
@ -214,6 +266,55 @@ def ohlc_to_line(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_ohlc_to_line(
|
||||||
|
src_shm: ShmArray,
|
||||||
|
array_key: str,
|
||||||
|
src_update: np.ndarray,
|
||||||
|
slc: slice,
|
||||||
|
ln: int,
|
||||||
|
first: int,
|
||||||
|
last: int,
|
||||||
|
is_append: bool,
|
||||||
|
|
||||||
|
) -> np.ndarray:
|
||||||
|
|
||||||
|
fields = ['open', 'high', 'low', 'close']
|
||||||
|
return (
|
||||||
|
rfn.structured_to_unstructured(src_update[fields]),
|
||||||
|
slc,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ohlc_flat_to_xy(
|
||||||
|
r: Renderer,
|
||||||
|
array: np.ndarray,
|
||||||
|
array_key: str,
|
||||||
|
vr: tuple[int, int],
|
||||||
|
|
||||||
|
) -> tuple[
|
||||||
|
np.ndarray,
|
||||||
|
np.nd.array,
|
||||||
|
str,
|
||||||
|
]:
|
||||||
|
# TODO: in the case of an existing ``.update_xy()``
|
||||||
|
# should we be passing in array as an xy arrays tuple?
|
||||||
|
|
||||||
|
# 2 more datum-indexes to capture zero at end
|
||||||
|
x_flat = r.x_data[r._xy_first:r._xy_last]
|
||||||
|
y_flat = r.y_data[r._xy_first:r._xy_last]
|
||||||
|
|
||||||
|
# slice to view
|
||||||
|
ivl, ivr = vr
|
||||||
|
x_iv_flat = x_flat[ivl:ivr]
|
||||||
|
y_iv_flat = y_flat[ivl:ivr]
|
||||||
|
|
||||||
|
# reshape to 1d for graphics rendering
|
||||||
|
y_iv = y_iv_flat.reshape(-1)
|
||||||
|
x_iv = x_iv_flat.reshape(-1)
|
||||||
|
|
||||||
|
return x_iv, y_iv, 'all'
|
||||||
|
|
||||||
|
|
||||||
def to_step_format(
|
def to_step_format(
|
||||||
shm: ShmArray,
|
shm: ShmArray,
|
||||||
data_field: str,
|
data_field: str,
|
||||||
|
@ -239,3 +340,74 @@ def to_step_format(
|
||||||
# start y at origin level
|
# start y at origin level
|
||||||
y_out[0, 0] = 0
|
y_out[0, 0] = 0
|
||||||
return x_out, y_out
|
return x_out, y_out
|
||||||
|
|
||||||
|
|
||||||
|
def update_step_xy(
|
||||||
|
src_shm: ShmArray,
|
||||||
|
array_key: str,
|
||||||
|
y_update: np.ndarray,
|
||||||
|
slc: slice,
|
||||||
|
ln: int,
|
||||||
|
first: int,
|
||||||
|
last: int,
|
||||||
|
is_append: bool,
|
||||||
|
|
||||||
|
) -> np.ndarray:
|
||||||
|
|
||||||
|
# for a step curve we slice from one datum prior
|
||||||
|
# to the current "update slice" to get the previous
|
||||||
|
# "level".
|
||||||
|
if is_append:
|
||||||
|
start = max(last - 1, 0)
|
||||||
|
end = src_shm._last.value
|
||||||
|
new_y = src_shm._array[start:end][array_key]
|
||||||
|
slc = slice(start, end)
|
||||||
|
|
||||||
|
else:
|
||||||
|
new_y = y_update
|
||||||
|
|
||||||
|
return (
|
||||||
|
np.broadcast_to(
|
||||||
|
new_y[:, None], (new_y.size, 2),
|
||||||
|
),
|
||||||
|
slc,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def step_to_xy(
|
||||||
|
r: Renderer,
|
||||||
|
array: np.ndarray,
|
||||||
|
array_key: str,
|
||||||
|
vr: tuple[int, int],
|
||||||
|
|
||||||
|
) -> tuple[
|
||||||
|
np.ndarray,
|
||||||
|
np.nd.array,
|
||||||
|
str,
|
||||||
|
]:
|
||||||
|
|
||||||
|
# 2 more datum-indexes to capture zero at end
|
||||||
|
x_step = r.x_data[r._xy_first:r._xy_last+2]
|
||||||
|
y_step = r.y_data[r._xy_first:r._xy_last+2]
|
||||||
|
|
||||||
|
lasts = array[['index', array_key]]
|
||||||
|
last = lasts[array_key][-1]
|
||||||
|
y_step[-1] = last
|
||||||
|
|
||||||
|
# slice out in-view data
|
||||||
|
ivl, ivr = vr
|
||||||
|
ys_iv = y_step[ivl:ivr+1]
|
||||||
|
xs_iv = x_step[ivl:ivr+1]
|
||||||
|
|
||||||
|
# flatten to 1d
|
||||||
|
y_iv = ys_iv.reshape(ys_iv.size)
|
||||||
|
x_iv = xs_iv.reshape(xs_iv.size)
|
||||||
|
|
||||||
|
# print(
|
||||||
|
# f'ys_iv : {ys_iv[-s:]}\n'
|
||||||
|
# f'y_iv: {y_iv[-s:]}\n'
|
||||||
|
# f'xs_iv: {xs_iv[-s:]}\n'
|
||||||
|
# f'x_iv: {x_iv[-s:]}\n'
|
||||||
|
# )
|
||||||
|
|
||||||
|
return x_iv, y_iv, 'all'
|
||||||
|
|
Loading…
Reference in New Issue