diff --git a/piker/data/ticktools.py b/piker/data/ticktools.py
index bc3543f8..1ce7fa45 100644
--- a/piker/data/ticktools.py
+++ b/piker/data/ticktools.py
@@ -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
@@ -15,7 +15,7 @@
# along with this program. If not, see .
'''
-Stream format enforcement.
+Tick event stream processing, filter-by-types, format-normalization.
'''
from itertools import chain
@@ -40,6 +40,69 @@ _tick_groups: dict[str, set[str]] = {
_auction_ticks: set[str] = set.union(*_tick_groups.values())
+def frame_ticks(
+ quote: dict[str, Any],
+
+ ticks_by_type: dict | None = None,
+ ticks_in_order: list[dict[str, Any]] | None = None
+
+) -> dict[
+ str,
+ list[dict[str, Any]]
+]:
+ '''
+ XXX: build a tick-by-type table of lists
+ of tick messages. This allows for less
+ iteration on the receiver side by allowing for
+ a single "latest tick event" look up by
+ indexing the last entry in each sub-list.
+
+ tbt = {
+ 'types': ['bid', 'asize', 'last', .. ''],
+
+ 'bid': [tick0, tick1, tick2, .., tickn],
+ 'asize': [tick0, tick1, tick2, .., tickn],
+ 'last': [tick0, tick1, tick2, .., tickn],
+ ...
+ '': [tick0, tick1, tick2, .., tickn],
+ }
+
+ If `ticks_in_order` is provided, append any retrieved ticks
+ since last iteration into this array/buffer/list.
+
+ '''
+ # TODO: once we decide to get fancy really we should
+ # have a shared mem tick buffer that is just
+ # continually filled and the UI just ready from it
+ # at it's display rate.
+
+ tbt = ticks_by_type if ticks_by_type is not None else {}
+ if not (ticks := quote.get('ticks')):
+ return tbt
+
+ # append in reverse FIFO order for in-order iteration on
+ # receiver side.
+ tick: dict[str, Any]
+ for tick in ticks:
+ tbt.setdefault(
+ tick['type'],
+ [],
+ ).append(tick)
+
+ # TODO: do we need this any more or can we just
+ # expect the receiver to unwind the below
+ # `ticks_by_type: dict`?
+ # => undwinding would potentially require a
+ # `dict[str, set | list]` instead with an
+ # included `'types' field which is an (ordered)
+ # set of tick type fields in the order which
+ # types arrived?
+ if ticks_in_order:
+ ticks_in_order.extend(ticks)
+
+ return tbt
+
+
def iterticks(
quote: dict,
types: tuple[str] = (
@@ -47,10 +110,16 @@ def iterticks(
'dark_trade',
),
deduplicate_darks: bool = False,
+ reverse: bool = False,
+
+ # TODO: should we offer delegating to `frame_ticks()` above
+ # with this?
+ frame_by_type: bool = False,
) -> AsyncIterator:
'''
- Iterate through ticks delivered per quote cycle.
+ Iterate through ticks delivered per quote cycle, filter and
+ yield any declared in `types`.
'''
if deduplicate_darks:
@@ -93,63 +162,12 @@ def iterticks(
# re-insert ticks
ticks.extend(list(chain(trades.values(), darks.values())))
+ # most-recent-first
+ if reverse:
+ ticks = reversed(ticks)
+
for tick in ticks:
# print(f"{quote['symbol']}: {tick}")
ttype = tick.get('type')
if ttype in types:
yield tick
-
-
-def frame_ticks(
- quote: dict[str, Any],
-
- ticks_by_type: dict[str, list[dict[str, Any]]] = {},
- ticks_in_order: list[dict[str, Any]] | None = None
-
-) -> dict:
-
- # append quotes since last iteration into the last quote's
- # tick array/buffer.
- # TODO: once we decide to get fancy really we should
- # have a shared mem tick buffer that is just
- # continually filled and the UI just ready from it
- # at it's display rate.
-
- if ticks := quote.get('ticks'):
-
- # XXX: build a tick-by-type table of lists
- # of tick messages. This allows for less
- # iteration on the receiver side by allowing for
- # a single "latest tick event" look up by
- # indexing the last entry in each sub-list.
- # tbt = {
- # 'types': ['bid', 'asize', 'last', .. ''],
-
- # 'bid': [tick0, tick1, tick2, .., tickn],
- # 'asize': [tick0, tick1, tick2, .., tickn],
- # 'last': [tick0, tick1, tick2, .., tickn],
- # ...
- # '': [tick0, tick1, tick2, .., tickn],
- # }
-
- # append in reverse FIFO order for in-order iteration on
- # receiver side.
- tick: dict[str, Any]
- for tick in ticks:
- ticks_by_type.setdefault(
- tick['type'],
- [],
- ).append(tick)
-
- # TODO: do we need this any more or can we just
- # expect the receiver to unwind the below
- # `ticks_by_type: dict`?
- # => undwinding would potentially require a
- # `dict[str, set | list]` instead with an
- # included `'types' field which is an (ordered)
- # set of tick type fields in the order which
- # types arrived?
- if ticks_in_order:
- ticks_in_order.extend(ticks)
-
- return ticks_by_type