Commit Graph

3188 Commits (e2e3e30d7a7f50075f1e7c5a994e8245e4c98120)

Author SHA1 Message Date
Tyler Goodlet bd2abcb91f Simplify formatter update methodology
Don't expect values (array + slice) to be returned and applied by
`.incr_update_xy_nd()` and instead presume this will implemented
internally in each (sub)formatter.

Attempt to simplify some incr-update routines, (particularly in the step
curve formatter, though most of it was reverted to just a simpler form
of the original implementation XD) including:
- dropping the need for the `slice_to_head: int` control.
- using the `xy_nd_start/stop` index counters over custom lookups.
2023-01-10 12:42:25 -05:00
Tyler Goodlet 97feb195e6 TOSQUASH: f3d757c2 (flow->viz) 2023-01-10 12:42:25 -05:00
Tyler Goodlet c084a1122a First attempt, field-index agnostic formatting
Remove harcoded `'index'` field refs from all formatters in a first
attempt at moving towards epoch-time alignment (though don't actually
use it it yet).

Adjustments to the formatter interface:
- property for `.xy_nd` the x/y nd arrays.
- property for and `.xy_slice` the nd format array(s) start->stop index
  slice.

Internal routine tweaks:
- drop `read_src_from_key` and always pass full source array on updates
  and adjust handlers to expect to have to index the data field of
  interest.
- set `.last_read` right after update calls instead of after 1d
  conversion.
- drop `slice_to_head` array read slicing.
- add some debug points for testing 'time' indexing (though not used
  here yet).
- add `.x_nd` array update logic for when the `.index_field` is not
  'index' - i.e. when we begin to try and support epoch time.
- simplify some new y_nd updates to not require use of `np.broadcast()`
  where possible.
2023-01-10 12:42:25 -05:00
Tyler Goodlet 1b9f6a7152 Pepper render routines with time-slice calls 2023-01-10 12:42:25 -05:00
Tyler Goodlet 3574548fe2 Add `Viz.bars_range()` (moved from chart API)
Call it from view kb loop.
2023-01-10 12:42:25 -05:00
Tyler Goodlet 598b1e2787 Make `Viz.slice_from_time()` take input array
Probably means it doesn't need to be a `Flume` method but it's
convenient to expect the caller to pass in the `np.ndarray` with
a `'time'` field instead of a `timeframe: str` arg; also, return the
slice mask instead of the sliced array as output (again allowing the
caller to do any slicing). Also, handle the slice-outside-time-range
case by just returning the entire index range with a `None` mask.

Adjust `Viz.view_data()` to instead do timeframe (for rt vs. hist shm
array) lookup and equiv array slicing with the returned mask.
2023-01-10 12:42:25 -05:00
Tyler Goodlet cb85079cf1 Add breakpoint on -ve range for now 2023-01-10 12:42:25 -05:00
Tyler Goodlet 670ba169e9 Copy timestamps from source to FSP dest buffer 2023-01-10 12:06:03 -05:00
Tyler Goodlet 3cf590eedf `Order.symbol` is a `str`.. 2023-01-10 12:06:03 -05:00
Tyler Goodlet d839fcb8e7 Avoid key error on already popped cancel 2023-01-10 12:06:03 -05:00
Tyler Goodlet 42faaa9870 Go back to hard-coded index field
Turns out https://github.com/numba/numba/issues/8622 is real
and the suggested `numba.literally` hack doesn't seem to work..
2023-01-10 12:05:57 -05:00
Tyler Goodlet b078235414 Move `ui._compression`/`._pathops` to `.data` subpkg
Since these modules no longer contain Qt specific code we might
as well include them in the data sub-package.

Also, add `IncrementalFormatter.index_field` as single point to def the
indexing field that should be used for all x-domain graphics-data
rendering.
2023-01-10 12:05:57 -05:00
Tyler Goodlet 03e6a00efd Add some data-flows jargon notes (re: #270) 2023-01-10 12:05:57 -05:00
Tyler Goodlet 1bfcda70ae Rename `.ui._flows.py` -> `.ui._render.py` 2023-01-10 12:05:57 -05:00
Tyler Goodlet 498ed8757c Rename `._flumes.py` -> `.flows.py` 2023-01-10 12:05:57 -05:00
Tyler Goodlet 4f4b5e0280 Rename `Flow` -> `Viz`
The type is better described as a "data visualization":
https://en.wikipedia.org/wiki/Data_and_information_visualization

Add `ChartPlotWidget.get_viz()` to start working towards not accessing
the private table directly XD

We'll probably end up using the name `Flow` for a type that tracks
a collection of composed/cascaded `Flume`s:
https://en.wikipedia.org/wiki/Two-port_network#Cascade_connection
2023-01-10 12:05:57 -05:00
Tyler Goodlet 6cca1eb941 Expand sampler loop shm write lines 2023-01-10 12:05:57 -05:00
Tyler Goodlet a016a28032 Adjust order mode to use `Flume.get_index()` 2023-01-10 12:05:57 -05:00
Tyler Goodlet 75f21470a9 Pass `Flume`s throughout FSP-ui and charting APIs
Since higher level charting and fsp management need access to the
new `Flume` indexing apis this adjusts some func sigs to pass through
(and/or create) flume instances:
- `LinkedSplits.add_plot()` and dependents.
- `ChartPlotWidget.draw_curve()` and deps, and it now returns a `Flow`.
- `.ui._fsp.open_fsp_admin()` and `FspAdmin.open_fsp_ui()` related
  methods => now we wrap the destination fsp shm in a flume on the admin
  side and is returned from `.start_engine_method()`.

Drop a bunch of (unused) chart widget methods including some already
moved to flume methods: `.get_index()`, `.in_view()`,
`.last_bar_in_view()`, `.is_valid_index()`.
2023-01-10 12:05:57 -05:00
Tyler Goodlet d3be4caa6a Make hist shm token optional to allow for FSPs 2023-01-10 12:05:57 -05:00
Tyler Goodlet 13e86fbe30 Move `Flume` to a new `.data._flumes` module 2023-01-10 12:05:57 -05:00
Tyler Goodlet 8793b76ee2 Extend `Flume` methods
Add some (untested) data slicing util methods for mapping time ranges to
source data indices:
- `.get_index()` which maps a single input epoch time to an equiv array
  (int) index.
- add `slice_from_time()` which returns a view of the shm data from an
  input epoch range presuming the underlying struct array contains
  a `'time'` field with epoch stamps.
- `.view_data()` which slices out the "in view" data according to the
  current state of the passed in `pg.PlotItem`'s view box.
2023-01-10 12:05:57 -05:00
Tyler Goodlet d115f43885 Add epoch time index to fsp buffers 2023-01-10 12:05:57 -05:00
Tyler Goodlet 0442945ce5 Drop px-cache-resets, failed try at path appends
Comments out the pixel-cache resetting since it doesn't seem we need it
any more to avoid draw oddities?

For `.fast_path` appends, this nearly got it working except the new path
segments are either not being connected correctly (step curve) or not
being drawn in full since the history path (plain line).

Leaving the attempted code commented in for a retry in the future; my
best guesses are that maybe,
- `.connectPath()` call is being done with incorrect segment length
  and/or start point.
- the "appended" data: `appended = array[-append_len-1:slice_to_head]`
  (done inside the formatter) isn't correct (i.e. endpoint handling
  considering a path append) and needs special handling for different
  curve types?
2023-01-10 12:05:57 -05:00
Tyler Goodlet 07714c5cbd Mask profile points and drop rect `.united()` attempts 2023-01-10 12:05:57 -05:00
Tyler Goodlet f139e4f273 Make curve graphics timeframe agnostic
Ensure `.boundingRect()` calcs and `.draw_last_datum()` do geo-sizing
based on source data instead of presuming some `1.0` unit steps in some
spots; we need this to support an epoch index as is needed for overlays.

Further, clean out a bunch of old bounding rect calc code and add some
commented code for trying out `QRectF.united()` on the path + last datum
curve segment. Turns out that approach is slower as per eyeballing the
added profiler points.
2023-01-10 12:05:57 -05:00
Tyler Goodlet 366df3307f Add graphics incr-updated "formatter" subsys
After trying to hack epoch indexed time series and failing miserably,
decided to properly factor out all formatting routines into a common
subsystem API: ``IncrementalFormatter`` which provides the interface for
incrementally updating and tracking pre-path-graphics formatted data.

Previously this functionality was mangled into our `Renderer` (which
also does the work of `QPath` generation and update) but splitting it
out also preps for being able to do graphics-buffer downsampling and
caching on a remote host B)

The ``IncrementalFormatter`` (parent type) has the default behaviour of
tracking a single field-array on some source `ShmArray`, updating
a flattened `numpy.ndarray` in-mem allocation, and providing a default
1d conversion for pre-downsampling and path generation.

Changed out of `Renderer`,
- `.allocate_xy()`, `update_xy()` and `format_xy()` all are moved to
  more explicitly named formatter methods.
- all `.x/y_data` nd array management and update
- "last view range" tracking
- `.last_read`, `.diff()`
- now calls `IncrementalFormatter.format_to_1d()` inside `.render()`

The new API gets,
- `.diff()`, `.last_read`
- all view range diff tracking through `.track_inview_range()`.
- better nd format array names: `.x/y_nd`, `xy_nd_start/stop`.
- `.format_to_1d()` which renders pre-path formatted arrays ready for
  both m4 sampling and path gen.
- better explicit overloadable formatting method names:
  * `.allocate_xy()` -> `.allocate_xy_nd()`
  * `.update_xy()` -> `.incr_update_xy_nd()`
  * `.format_xy()` -> `.format_xy_nd_to_1d()`

Finally this implements per-graphics-type formatters which define
each set up related formatting routines:
- `OHLCBarsFmtr`: std multi-line style bars
- `OHLCBarsAsCurveFmtr`: draws an interpolated line for ohlc sampled data
- `StepCurveFmtr`: handles vlm style curves
2023-01-10 12:05:57 -05:00
Tyler Goodlet cbd4119101 Max out per symbol throttle @ 22Hz 2023-01-10 12:05:53 -05:00
Tyler Goodlet 01b470faf4 Move all pre-path formatting routines to `._pathops`, proto formatter type 2023-01-10 11:09:49 -05:00
Tyler Goodlet 226e84d15f Ensure a rt shm buffer without backfill has correct epoch timestamping 2023-01-10 11:09:49 -05:00
Tyler Goodlet de1c0b1399 Use throttle period for wait-on-clearing-event timeout 2023-01-10 11:09:49 -05:00
Tyler Goodlet e7daf09a83 Expect and update from by-type tick frames
Move to expect and process new by-tick-event frames where the display
loop can now just iterate the most recent tick events by type instead of
the entire tick history sequence - thus we reduce iterations inside the
update loop.

Also, go back to use using the detected display's refresh rate (minus 6)
as the default feed requested throttle rate since we can now handle
much more bursty-ness in display updates thanks to the new framing
format B)
2023-01-10 11:09:49 -05:00
Tyler Goodlet 5fcc34a9e6 Implement by-type tick-framing in throttler loop
This has been an outstanding idea for a while and changes the framing
format of tick events into a `dict[str, list[dict]]` wherein for each
tick "type" (eg. 'bid', 'ask', 'trade', 'asize'..etc) we create an FIFO
ordered `list` of events (data) and then pack this table into each
(throttled) send. This gives an additional implied downsample reduction
(in terms of iteration on the consumer side) from `N` tick-events to
a (max) `T` tick-types presuming the rx side only needs the latest tick
event.

Drop the `types: set` and adjust clearing event test to use the new
`ticks_by_type` map's keys.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 6f0b1ea283 Factor info print into func 2023-01-10 11:09:49 -05:00
Tyler Goodlet e618d13fc9 Update/improve qt screen script 2023-01-10 11:09:49 -05:00
Tyler Goodlet 947f29aefb Improved clearing-tick-burst-oriented throttling
Instead of uniformly distributing the msg send rate for a given
aggregate subscription, choose to be more bursty around clearing ticks
so as to avoid saturating the consumer with L1 book updates and vs.
delivering real trade data as-fast-as-possible.

Presuming the consumer is in the "UI land of slow" (eg. modern display
frame rates) such an approach serves more useful for seeing "material
changes" in the market as-bursty-as-possible (i.e. more short lived fast
changes in last clearing price vs. many slower changes in the bid-ask
spread queues). Such an approach also lends better to multi-feed
overlays which in aggregate tend to scale linearly with the number of
feeds/overlays; centralization of bursty arrival rates allows for
a higher overall throttle rate if used cleverly with framing.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 363c7a2df2 Brighter last OHLC graphics datum by default 2023-01-10 11:09:49 -05:00
Tyler Goodlet 701eb7c2c5 Factor setup loop, 1 FSP chain, colors, throttling
Factor out the chart widget creation since it's only executed once
during rendering of the first feed/flow whilst keeping plotitem overlay
creation inside the (flume oriented) init loop. Only create one vlm and
FSP chart/chain for now until we figure out if we want FSPs overlayed by
default or selected based on the "front" symbol in use. Add a default
color-palette set using shades of gray when plotting overlays. Presume
that the display loop's quote throttle rate should be uniformly
distributed over all input symbol-feeds for now. Restore feed pausing on
mouse interaction.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 4020a198c4 Type annot-declare fsp-engine data `Feed` 2023-01-10 11:09:49 -05:00
Tyler Goodlet d834dfac74 Define a single `ChartPlotWidget.feed: Feed` for pause/resume 2023-01-10 11:09:49 -05:00
Tyler Goodlet bdbc8de8c1 Assign pnl calc output for use when debugging 2023-01-10 11:09:49 -05:00
Tyler Goodlet 5e6ebca1e0 Rework `_FeedsBus` subscriptions mgmt using `set`
Allows using `set` ops for subscription management and guarantees no
duplicates per `brokerd` actor. New API is simpler for dynamic
pause/resume changes per `Feed`:
- `_FeedsBus.add_subs()`, `.get_subs()`, `.remove_subs()` all accept multi-sub
  `set` inputs.
- `Feed.pause()` / `.resume()` encapsulates management of *only* sending
  a msg on each unique underlying IPC msg stream.

Use new api in sampler task.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 42e934b912 Init msg keys are always lower case 2023-01-10 11:09:49 -05:00
Tyler Goodlet da285d6275 Make `PlotItemOverlay` add items inwards->out
Before this axes were being stacked from the outside in (for `'right'`
and 'bottom'` axes) which is somewhat non-intuitive for an `.append()`
operation. As such this change makes a symbol list stack a set of
`'right'` axes from left-to-right.

Details:
- rename `ComposeGridLayout.items` -> `.pitems`
- return `(int, list[AxisItem])` pairs from `.insert/append_plotitem()`
  and the down stream `PlotItemOverlay.add_plotitem()`.
- drop `PlotItemOverlay.overlays` and add it back as `@property` around
  the underlying `.layout.pitems`.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 396fb742bd Fix for empty tsdb query result case 2023-01-10 11:09:49 -05:00
Tyler Goodlet 8c6a18fdb7 Drop tick frame builder loop for now 2023-01-10 11:09:49 -05:00
Tyler Goodlet a6241a5a16 Adjust FSP UI/mgmt apis to be `Flume` oriented 2023-01-10 11:09:49 -05:00
Tyler Goodlet eb1650197b Make graphics-update-loop multi-sym aware B)
Initial support for real-time multi-symbol overlay charts using an
aggregate feed delivered by `Feed.open_multi_stream()`.

The setup steps for constructing the overlayed plot items is still very
very rough and will likely provide incentive for better refactoring high
level "charting APIs". For each fqsn passed into `display_symbol_data()`
we now synchronously,
- create a single call to `LinkedSplits.plot_ohlc_main() -> `ChartPlotWidget`
  where we cache the chart in scope and for all other "sibling" fqsns
  we,
- make a call to `ChartPlotWidget.overlay_plotitem()` -> `PlotItem`, hide its axes,
  make another call with this plotitem input to
  `ChartPlotWidget.draw_curve()`, set a sym-specific view box auto-yrange maxmin callback,
  register the plotitem in a global `pis: dict[str, list[pgo.PlotItem, pgo.PlotItem]] = {}`

Once all plots have been created we then asynchronously for each symbol,
- maybe create a volume chart and register it in a similar task-global
  table: `vlms: dict[str, ChartPlotWidget] = {}`
- start fsp displays for each symbol

Then common entrypoints are entered once for all symbols:
- a single `graphics_update_loop()` loop-task is started wherein
  real-time graphics update components for each symbol are created,
      * `L1Labels`
      * y-axis last clearing price stickies
      * `maxmin()` auto-ranger
      * `DisplayState` (stored in a table `dss: dict[str, DisplayState] = {}`)
      * an `increment_history_view()` task
  and a single call to `Feed.open_multi_stream()` is used to create
  a symbol-multiplexed quote stream which drives a single loop over all
  symbols wherein for each quote the appropriate components are looked
  up and passed to `graphics_update_cycle()`.
- a single call to `open_order_mode()` is made with the first symbol
  provided as input, though eventually we want to support passing in the
  entire list.

Further internal implementation details:
- special tweaks to the `pg.LinearRegionItem` setup wherein the region
  is added with a zero opacity and *after* all plotitem overlays to
  avoid and issue where overlays weren't being shown within the region
  area in the history chart.
- all symbol-specific graphics oriented update calls are adjusted to
  pass in the fqsn:
  * `update_fsp_chart()`
  * `ChartView._set_yrange()`
  * ChartPlotWidget.update_graphics_from_flow()`
- avoid a double increment on sample step updates by not calling the
  increment on any vlm chart since it seems the vlm-ohlc chart linking
  already takes care of this now?
- use global counters for the last epoch time step to avoid incrementing
  all views more then once per new time step given underlying shm array
  buffers may be on different array-index values from one another.
2023-01-10 11:09:49 -05:00
Tyler Goodlet 6b4614f735 Only add plot to cursor set if not an overlay 2023-01-10 11:09:49 -05:00
Tyler Goodlet fc067eb7a8 Adjust search to handle multi-sym results 2023-01-10 11:09:49 -05:00