Skip to content

Deterministic Replay

The quantpylib replay system is a deterministic replay kernel for institutional-grade backtest simulation. It is designed so live trading and simulation use a unified code interface, reducing the margin for error created by implementation gaps between research and production.

See the market-making tutorial for a tutorial on how to write market making code using the replay's runtime.

Live and replay runtime domains

In replay mode, ReplayRuntime is injected into the OMS. It schedules asynchronous work on the replay kernel's deterministic event heap. The replay clock therefore synchronizes historical market-data delivery and strategy events. This allows the replay and the live strategy to share code.

Live and replay architecture. Shared strategy, Feed, OMS, and portfolio mirrors are centered; replay-domain objects are shaded orange.

This architecture preserves several properties required for high-fidelity simulation:

  • Avoid last-mile regressions. The same strategy methods, Feed callbacks, OMS wiring, portfolio mirrors, and cancellation paths run in replay and production. A strategy change exercised in replay therefore reaches live execution through the same business-logic path, avoiding implementation drift.
  • Determinism and receive-time fidelity. Market events retain and deliver at the timing observed by the actual user's tick-data capture. Bursts, backpressure and jitter are as-was.
  • Exchange-specific RX and TX latency simulation. Public data, outbound orders and cancels, responses, and private updates can travel through separately configured latency paths for each exchange.
  • Configurable queue and impact policies. Queue placement, cancellations and the relationship between simulated private orders and historical public depth can be modelled.
  • Production-grade event journals and artefacts. Replay uses the same performant logging and journaling path to generate trace artefacts for simulation-fidelity review and strategy analysis.

See the component references for the individual replay domains:

  • replay_exchange owns authoritative venue, matching, order, fill, and position state.
  • replay_wrapper exposes the simulated venue client through the Gateway-compatible methods and applies RX/TX latency on actions.
  • replay_orderbook projects public MBP (L2) book into MBO (L3) book with impact and queue policies.
  • replay_stream restores bounded timestamp disorder into deterministic key order.

Before the API reference - we want to show a simple diagram of how the replay kernel performs scheduling through an order submission and two market data events.

sequenceDiagram
    participant QBN as QBNReplay
    participant Heap as ReplayRuntime heap
    participant Strategy
    participant Exchange as ReplayExchange
    participant Wrapper as ReplayWrapper
    participant OMS as OMS order mirror

    QBN->>Heap: Directly schedule QBN data 1 for ts = 1
    QBN->>Heap: Directly schedule QBN data 2 for ts = 2
    Heap->>Strategy: At ts = 1 deliver QBN data 1 through Feed
    Strategy->>Wrapper: limit_order()
    Wrapper->>Heap: runtime.sleep_ns(submit_order.TX)
    Heap->>Heap: Create Future and schedule wake at ts = 1 + submit_order.TX
    Note over Wrapper: submit_order awaits Future<br/>ReplayRuntime continues draining due events
    Heap->>Exchange: At ts = 2 deliver QBN data 2
    Heap-->>Wrapper: At ts = 1 + submit_order.TX resolve Future
    Wrapper->>Exchange: submit_order()
    Exchange-->>Wrapper: Emit private order update
    Wrapper->>Heap: create_task(deliver_acknowledgement)
    Wrapper->>Heap: runtime.sleep_ns(order_ack.RX)
    Heap->>Heap: Create Future and schedule wake at ts = submit_ts + order_ack.RX
    Note over Wrapper: Acknowledgement task awaits Future<br/>ReplayRuntime continues
    Heap-->>Wrapper: At ts = submit_ts + order_ack.RX resolve Future
    Wrapper->>OMS: Deliver private order acknowledgement

API reference

Deterministic public market-data replay over QBN archives.

QBNReplay

Stream deterministic exchange-time and receive-time QBN routes.

Each decoded source record may produce an exchange route ordered by ts_exch_ns and a client route ordered by ts_recv_ns. The global merge key is (delivery_ns, route, source_id, source_ordinal).

Parameters:

Name Type Description Default
sources iterable[ReplaySource]

Non-empty set of uniquely identified QBN sources. Paths and source IDs must both be unique.

required

Attributes:

Name Type Description
out_of_order_counts dict[int, int]

Late client-route counts by source ID from the latest events() iteration.

exchange_out_of_order_counts dict[int, int]

Late exchange-route counts by source ID from the latest iteration.

__init__(sources)

Validate sources and open the matching QBN readers.

events(*, data_iter_buffer=1000, include_client=True, start_ns=None, end_ns=None, symbols=None)

Yield merged exchange-time and receive-time routes.

Exchange routes use bounded lookahead to repair nearly ordered exchange timestamps. Client routes use zero lookahead and therefore preserve receive-order causality while dropping backward timestamps.

Parameters:

Name Type Description Default
data_iter_buffer int

Records buffer protection against out-of-order.

1000
include_client bool

Emit client RX routes in addition to exchange.

True
start_ns int

Inclusive route-time lower bound.

None
end_ns int

Exclusive route-time upper bound.

None
symbols str or iterable[str]

Tickers to retain.

None

Yields:

Name Type Description
tuple

``(delivery_ns, route, source_id, source_ordinal,

ReplayEvent)`` in deterministic order.

Raises:

Type Description
ValueError

If start_ns is greater than end_ns.

from_paths(paths) classmethod

Build a replay whose source precedence follows path order.

Parameters:

Name Type Description Default
paths path - like or iterable[path - like]

QBN archive paths.

required

Returns:

Name Type Description
QBNReplay

Replay with sequential source IDs beginning at zero.

source_headers()

Return copied QBN headers keyed by configured source ID.

Returns:

Type Description

dict[int, dict]: Source metadata safe for caller mutation.

ReplayClock

Nanosecond clock domain advanced by the replay kernel.

Strategy code receives this object through ReplayRuntime.

__init__()

Create an unstarted replay clock.

now_ns()

Return current replay time in nanoseconds.

Returns:

Name Type Description
int

Current deterministic timestamp.

Raises:

Type Description
RuntimeError

If the replay clock has not been primed or started.

ReplayError

Bases: Exception

Base error for deterministic QBN replay.

ReplayEvent dataclass

A native QBN event with deterministic replay context.

source_ordinal is the zero-based physical record position within one source, including records excluded by replay filters.

Parameters:

Name Type Description Default
delivery_ns int

Timestamp at which this route is delivered.

required
source_id int

Stable cross-source priority.

required
source_ordinal int

Physical record position in the source.

required
feed_id FeedId

Venue, market class, feed type, and ticker identity.

required
event object

Decoded native QBN market-data event.

required

ReplayNode

Compose deterministic QBN tick data backtest through Gateway and Feed.

One ReplayExchange owns venue truth for each exchange and one ReplayWrapper exposes that venue through the standard gateway boundary. runtime is injected into OMS so strategy, transport, and portfolio states share the replay clock domain.

Parameters:

Name Type Description Default
replay QBNReplay or iterable[ReplaySource]

Configured replay input.

required
data_iter_buffer int

Records buffer protection against out-of-order.

1000
exchange_kwargs dict

Per-venue keyword arguments for ReplayExchange.

None
wrapper_kwargs dict

Per-venue keyword arguments for ReplayWrapper.

None

Attributes:

Name Type Description
clock ReplayClock

Deterministic clock.

runtime ReplayRuntime

Strategy and OMS scheduler.

exchanges dict[str, ReplayExchange]

Venue truth by alias.

clients dict[str, ReplayWrapper]

Gateway transports by alias.

gateway Gateway

Normalized gateway over replay clients.

feed Feed

Normal production feed over the replay gateway.

__init__(replay, *, data_iter_buffer=1000, exchange_kwargs=None, wrapper_kwargs=None)

Validate QBN compatibility and build the replay component graph.

cleanup() async

Close feed sinks and replay gateway clients.

from_qbn(paths, **kwargs) classmethod

Create a node from QBN paths in precedence order.

Parameters:

Name Type Description Default
paths path - like or iterable[path - like]

QBN inputs.

required
**kwargs

Additional ReplayNode constructor arguments.

{}

Returns:

Name Type Description
ReplayNode

Fully composed, uninitialized node.

init() async

Initialize clients and prime state timestamps to QBN time.

Raises:

Type Description
ReplayError

If the configured replay has no events.

run(*, start_ns=None, end_ns=None, symbols=None, include_client=True) async

Run the deterministic replay kernel.

Parameters:

Name Type Description Default
start_ns int

Inclusive route-time lower bound.

None
end_ns int

Exclusive route-time upper bound.

None
symbols str or iterable[str]

Tickers to retain.

None
include_client bool

Deliver client routes to subscribed feeds.

True

Raises:

Type Description
RuntimeError

If the node is already running.

ReplayError

If replay scheduling or source ordering fails.

ReplayOrderError

Bases: ReplayError

Replay input or scheduled work cannot satisfy time ordering.

ReplayRuntime

Schedule asynchronous work on the replay kernel.

Implements the clock and scheduling interface used by OMS and strategy code. Client code should use the runtime appropriate to its execution mode. In production, AsyncioRuntime schedules gather, create_task, sleeps, and periodic work on the asyncio event loop. In replay, ReplayRuntime schedules the same work on the deterministic event heap and advances it using replay time.

Parameters:

Name Type Description Default
clock ReplayClock

Clock advanced as scheduled callbacks are drained.

required

__init__(clock)

Initialize an empty scheduler over clock.

call_at_ns(when_ns, callback)

Schedule a callback at an absolute replay timestamp.

Equal timestamps execute by insertion order. A callback may return an awaitable, a tracked future, or a non-awaitable result.

Parameters:

Name Type Description Default
when_ns int

Absolute deterministic timestamp.

required
callback callable

Zero-argument callback.

required

Returns:

Name Type Description
int

Event identifier used internally for cancellation tracking.

Raises:

Type Description
TypeError

If callback is not callable.

ReplayOrderError

If active replay work is scheduled in the past.

call_later_ns(delay_ns, callback)

Schedule a callback relative to current replay time.

Parameters:

Name Type Description Default
delay_ns int

Non-negative delay in nanoseconds.

required
callback callable

Zero-argument callback.

required

Returns:

Name Type Description
int

Scheduled event identifier.

Raises:

Type Description
ValueError

If delay_ns is negative.

create_task(coro, *, name=None, daemon=False)

Create and track an asyncio task as replay work.

Parameters:

Name Type Description Default
coro coroutine

Coroutine to execute.

required
name str

Asyncio task name used in diagnostics.

None
daemon bool

Whether the task may be abandoned when all non-daemon replay work is complete.

False

Returns:

Type Description

asyncio.Task: Registered task.

every(interval_sec, callback, *, name=None)

Schedule a daemon callback at a fixed replay-time interval.

Parameters:

Name Type Description Default
interval_sec float

Non-negative interval in seconds.

required
callback callable

Awaitable callback invoked after each interval.

required
name str

Diagnostic task name.

None

Returns:

Type Description

asyncio.Task: Daemon loop task.

gather(*aws, return_exceptions=False) async

Join awaitables while preserving replay task accounting.

Outside an active run this delegates directly to asyncio.gather. During replay, coroutine children are converted to tracked tasks and the tracked parent is marked waiting until they finish.

Parameters:

Name Type Description Default
*aws

Awaitables to execute concurrently.

()
return_exceptions bool

Match asyncio.gather exception collection semantics.

False

Returns:

Name Type Description
list

Results in input order.

Raises:

Type Description
ReplayError

If called by an untracked active task or passed an untracked future during replay.

now_ns()

Return the current deterministic timestamp in nanoseconds.

Returns:

Name Type Description
int

Current replay time.

reset()

Clear completed scheduler state and unstart the clock.

Raises:

Type Description
RuntimeError

If the runtime is active or still owns tasks.

run() async

Advance time and drain deterministic non-daemon work.

The clock advances to each heap timestamp, callbacks run in insertion order, and tracked tasks settle between scheduling boundaries. On any failure, outstanding tasks are cancelled before the error is reraised.

Raises:

Type Description
RuntimeError

If this runtime is already running.

ReplayError

If work escapes runtime scheduling or waits without a scheduled wake-up.

BaseException

Any unjoined callback or task failure.

sleep(seconds) async

Suspend the current replay task for deterministic seconds.

Parameters:

Name Type Description Default
seconds float

Non-negative duration in seconds.

required

sleep_ns(delay_ns) async

Suspend the current replay task for deterministic nanoseconds.

During an active run, only tasks registered by create_task() may sleep. Cancellation removes the pending wake-up from effective work.

Parameters:

Name Type Description Default
delay_ns int

Non-negative delay in nanoseconds.

required

Raises:

Type Description
ValueError

If delay_ns is negative.

ReplayError

If an untracked task sleeps during an active run.

ReplaySource dataclass

One explicitly ordered QBN input source.

source_id is the stable cross-source tie-break priority; lower values replay first.

Parameters:

Name Type Description Default
path path - like

QBN archive path.

required
source_id int

Unique deterministic priority for this source.

required

__post_init__()

Validate the source priority and normalize path to Path.