Story: Stochastic tick arrival times for synthetic feeds
Table of Contents
This page documents a story in Product backlog — inbox, carried unfinished from Sprint 24 at close. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
1. Goal
Today the synthetic FX spot feed emits a tick on a FIXED clock — a
constant interval derived from ticks_per_hour (fx_spot_feed::start,
projects/ores.synthetic/service/src/fx_spot_feed.cpp:64-106), sleeping
the full period before every publish including the first. Real market
data does not arrive on a fixed cadence: trades/quotes cluster and gap,
with bursty, irregular inter-arrival times. Make the feed tick at
RANDOM times drawn from a distribution that approaches reality, while
keeping the price process (GMM geometric/arithmetic) decoupled from
the arrival process — two independent configurable models.
Raised while tuning
synthetic_fx_spot_configs_realistic_populate.sql's tick cadence (see
Task: Seed Basic and Realistic dataset bundles) — settled on a fixed
2-second cadence there as good enough for a first pass, but a
genuinely "realistic" feed should vary its arrival timing, not just
its price distribution.
Candidate models for the arrival (timing) process, to be confirmed by a literature review:
- Homogeneous Poisson process — exponential inter-arrival times; the simplest step up from a fixed clock (constant intensity λ).
- Inhomogeneous Poisson — time-varying intensity λ(t) for intraday seasonality (open/close bursts, lunch lull).
- Self-exciting Hawkes processes — capture the clustering / burstiness of real order flow (an arrival raises the short-term intensity of further arrivals).
- Two-state quiet/burst regime, formally a Markov-Modulated Poisson
Process (MMPP): a Poisson process whose intensity λ switches
between discrete states (quiet/burst, or more) via a hidden Markov
chain. This is the standard, well-established model for exactly the
clustering/regime behaviour this story wants, at much lower
calibration cost than a full Hawkes process. Note: this is not a
Gaussian Mixture Model (GMM) —
ores.synthetic's existing price process uses a weighted mixture of continuous Gaussian components (gmm_process=/=gmm_component), which doesn't transfer directly to a discrete counting/arrival-time process; MMPP is the analogous-in- spirit but technically distinct construct for this domain (a regime-mixture, not a magnitude-mixture). Worth confirming during the literature review below whether MMPP or a simpler two-state regime-switch (without the full Markov-chain machinery) is the better starting point.
Scope: a configurable arrival process on the feed (alongside the existing price engine), exposed in the config (e.g. an "arrival model" with its parameters, analogous to the price-process engine choice). Find and summarise the relevant papers first (market microstructure / point processes / Hawkes in finance), then design the model and config surface.
2. Status
| Field | Value |
|---|---|
| State | BACKLOG |
| Carried from | Sprint 24 (unfinished at close) |
| Now | Not yet started. |
| Waiting on | Nothing. |
| Next | Break the story into tasks. |
| Last touched | 2026-07-11 |
3. Acceptance
- Arrival-process parameters are NOT configured per market-data feed.
Instead, a small set of named, top-level arrival-profile configs
(e.g.
liquid_spot_rates,illiquid_spot_rates) each bundle a distribution choice (Poisson / inhomogeneous Poisson / Hawkes / two-state quiet-burst) with its parameters; individual feed configs reference one profile by name/FK rather than duplicating parameters. This keeps calibration in one place and lets many feeds share a tuned profile (all liquid G10 pairs useliquid_spot_rates, say) without re-tuning per pair. - The arrival process is decoupled from the price process: swapping arrival profile never changes the GMM price-process configuration and vice versa.
- At least one non-trivial arrival model (homogeneous Poisson at
minimum) is implemented and wired into
fx_spot_feed(or its successor abstraction) as a real alternative to the fixed clock, selectable via the arrival-profile config. - The Basic/Realistic collection distinction (see
Synthetic data collections: Basic and Realistic) can express itself
through arrival profile as well as price-process parameters — e.g.
Basickeeps the fixed clock (simple, easy to eyeball),Realisticuses a genuine arrival profile.
4. Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
Planned tasks (not yet scaffolded as task docs):
- Literature review: summarise Poisson/Hawkes point-process literature (see References) into a short recommendation of which model(s) to implement first.
- Design and implement the named arrival-profile config (e.g.
arrival_profile_config, codegen-driven likefx_spot_generation_config): distribution choice + parameters, referenced by FK from feed configs rather than duplicated per feed. - Implement at least a homogeneous Poisson arrival process, wired
into
fx_spot_feed(or its successor) as an alternative to the fixed clock, decoupled from the GMM price process. - Wire
Basic=/=Realisticcollections to reference an arrival profile (Basickeeps the fixed clock;Realisticuses a real profile), per Synthetic data collections: Basic and Realistic.
5. Decisions
- Arrival-profile configs are top-level, named, and shared across
feeds (
liquid_spot_rates,illiquid_spot_rates, …) rather than parameters on each individual market-data feed/pair config. This avoids re-tuning the same distribution N times and lets feeds that should behave similarly (e.g. all liquid G10 pairs) share one calibrated profile. - The arrival (timing) process stays decoupled from the price (GMM) process — two independent, independently-configurable models, per the original capture's framing.
6. Out of scope
- Curve/vol-surface generation timing (separate stories: IR Rates synthetic data generation).
- Redesigning the GMM price process itself — this story only touches arrival/tick timing.
7. References
Code:
projects/ores.synthetic/service/src/fx_spot_feed.cpp:64-106projects/ores.sql/populate/synthetic/synthetic_fx_spot_configs_realistic_populate.sqlprojects/ores.synthetic/service/src/processes/gmm_process.hpp— the existing weighted-mixture pattern for the price process, referenced above for the MMPP-vs-GMM distinction.
Literature (point processes / Hawkes for trade & order arrivals):
- Bacry & Muzy, "Hawkes model for price and trades high-frequency dynamics" (2014, Quantitative Finance 14(7)); preprint arXiv:1301.1135 — https://arxiv.org/abs/1301.1135 . Foundational: multivariate Hawkes for trade arrivals + price dynamics (self-/mutually-exciting kernels).
- Bacry, Mastromatteo & Muzy, "Hawkes Processes in Finance" — survey of Hawkes applications to high-frequency data: https://www.stt.msu.edu/~viens/publications/11_hawkes.pdf
- "Hawkes processes for credit indices time series analysis: How random are trades arrival times?" arXiv:1902.03714 — directly on the irregularity of inter-arrival times.
- Hewlett / others, "Modelling order arrivals … using Hawkes processes" (ScienceDirect, Finance Research Letters): https://www.sciencedirect.com/science/article/abs/pii/S1544612316301490
- "Limit Order Book Simulations: A Review" arXiv:2402.17359 — https://arxiv.org/html/2402.17359v1 . Broader survey incl. arrival-process modelling for LOB simulators (useful comparison of fixed-clock vs point processes).
Notes from the survey reading:
- Simplest upgrade = a homogeneous Poisson process (exponential inter-arrival, constant intensity λ) — drop-in replacement for the fixed clock.
- Add intraday seasonality via an inhomogeneous Poisson intensity λ(t) (volumes/arrivals follow a stable intraday U-shape: open/close bursts).
- Hawkes (self-exciting) captures the empirical CLUSTERING/burstiness of real arrivals; main cost is calibration (kernel choice, likelihood complexity). An MMPP is a cheaper way to get similar clustering behaviour without full Hawkes calibration — worth comparing during the literature review task.
8. See also
- Story: Config-driven synthetic feed generation
- Story: Synthetic generation configuration and config-driven feeds
- Task: Seed Basic and Realistic dataset bundles — the task this was deferred out of.
- Synthetic data collections: Basic and Realistic — sibling story this connects to via the Basic/Realistic arrival-profile split.
9. Where this plugs in, recorded 2026-10-07
The fixed clock this story replaces is now one class rather than two. The
duplicated per-producer clocks collapsed into tick_clock in
projects/ores.synthetic/api/include/ores.synthetic.api/feeds/tick_clock.hpp,
which owns the period, the sliced stop-responsive sleep, the publish counter and
the catch-and-continue guard. A producer supplies only what one tick does:
advance its process and publish its datum. Both producers now delegate to it,
so there is exactly one place to change and both asset classes get the change.
That shape is what makes this story cheap, and it is worth stating precisely
which seam the arrival process takes. The clock currently answers one question,
"when is the next tick", by computing a constant period from ticks_per_hour.
The change is to have it ask an arrival model for that instant instead of
computing it. Nothing else moves: the producer still supplies what one tick
does, so the price process stays decoupled from the arrival process, which is
what the goal above requires.
Where the variation lives matters, and this is the part the current code cannot
express. Intensity is not only per asset class. A G11 currency pair ticks far
more often than an emerging-market pair, so the intensity is a property of the
feed, not of the class, and the scalar ticks_per_hour is too coarse for it.
The arrival model should therefore be configured per feed, and its parameters
carried on the generation config beside the process parameters it already holds.
Regimes follow from that. Under a Markov-modulated Poisson process the intensity switches between discrete states through a hidden chain, and the state machine belongs to the arrival model. The clock stays regime-agnostic: it never learns that regimes exist, it only asks for the next instant. That keeps this story from reopening the clock, and it means a regime model and a homogeneous Poisson model are interchangeable from the clock's point of view.
Two consequences for whoever picks this up. The first tick currently sleeps a full period before publishing, which a draw-based clock must not inherit, since an arrival model says when the next tick falls and the first is not special. And the stop-responsiveness the clock already has must survive, because a sleep sliced short by a stop must not be confused with an inter-arrival draw that happened to be short.