Timing output is enabled by default for public runners that use
@timing_harness. Set BACKTEST_ENABLE_TIMING=0 only when you explicitly want
quiet output.
PMXT
PMXT book runners fetch historical L2 order-book data one UTC hour at a time. The hour lookup order is:
- Local filtered cache.
- Each explicit raw source in
MarketDataConfig.sources, left to right. - Confirmed miss.
The public PMXT runners usually use:
sources=(
"local:/Volumes/storage/pmxt_data",
"archive:r2v2.pmxt.dev",
"archive:r2.pmxt.dev",
)
After a successful raw-source fetch, the market/token/hour slice is written to
the filtered cache under ~/.cache/nautilus_trader/pmxt. Warm filtered-cache
reads should be sub-millisecond to low-millisecond per hour because the cache
stores a compact filtered parquet slice rather than the full raw archive hour.
When multiple PMXT replays load together, the adapter stages all metadata first, then all book data, then all execution trade ticks. Filtered-cache misses are grouped by raw archive hour: one local or remote raw parquet hour can serve many market/token requests before the per-replay filtered caches are written.
Example Output
A representative PMXT run prints:
PMXT source: explicit priority (cache -> local /Volumes/storage/pmxt_data -> archive https://r2v2.pmxt.dev -> archive https://r2.pmxt.dev)
Loading PMXT Polymarket market will-ludvig-aberg-win-the-2026-masters-tournament (token_index=0, window_start=2026-04-05T00:00:00+00:00, window_end=2026-04-07T23:59:59+00:00)...
2026-04-05T00:00:00+00:00 ... ... rows cache polymarket_orderbook_2026-04-05T00.parquet
2026-04-06T12:00:00+00:00 ... ... rows local raw
2026-04-07T23:00:00+00:00 ... 0 rows none
PMXT book progress [#######################-] 69.0/72 hours (95.8%; started=72, done=69, active=3)
Important fields:
PMXT source:shows exact source priority.cachemeans the filtered market/token/hour cache satisfied the request.local rawmeans a local raw archive hour was scanned and filtered.r2 rawmeans a remote raw archive hour was downloaded and filtered.nonemeans the hour was not found in any configured source.- Active progress shows currently running scans or transfers.
Telonex
Telonex book runners read full-depth daily book snapshots from
book_snapshot_full.
Typical source config:
MarketDataConfig(
platform=Polymarket,
data_type=Book,
vendor=Telonex,
sources=(
"api:",
"local:/Volumes/storage/telonex_data",
),
)
The effective lookup order for converted replay records is:
- Telonex materialized replay caches under
book-deltas-v1andtrade-ticks-v1. - Explicit entries in
MarketDataConfig.sources, left to right. - For execution trade ticks only, Polymarket's public trades cache/API remains the final fallback after Telonex local/API misses.
The Telonex source: line shows that implicit cache layer:
Telonex book source: explicit priority (cache -> api https://api.telonex.io (key set) -> local /Volumes/storage/telonex_data)
Telonex trade source: explicit priority (cache -> api https://api.telonex.io (key set) -> local /Volumes/storage/telonex_data -> polymarket cache -> api https://data-api.polymarket.com/trades)
Local reads use the DuckDB manifest when present. The manifest maps requested market/outcome/channel/day ranges to concrete parquet part paths, so the loader does not need to glob or scan unrelated partitions. If a candidate local part is empty or unreadable, it is ignored and the loader can fall through to the next source.
API reads are daily. A first API run writes both the raw nested daily parquet
and a .fast.parquet sidecar. Warm cache reads prefer the sidecar, which
stores bid_prices, bid_sizes, ask_prices, and ask_sizes as
list<string> columns. That keeps price/size precision and avoids slow nested
list-of-struct pandas decoding.
After any raw/cache/local/API day is converted to OrderBookDeltas, the loader
writes a materialized replay parquet. Non-empty Telonex onchain_fills or
trades days are also materialized as TradeTicks. Empty Telonex onchain-fill
results are not terminal for execution matching; the loader continues to
Telonex trades and then Polymarket's public trade fallback before deciding a
day has no trade ticks. Repeated runs for the same market, token, instrument id,
day, and clipped window report telonex deltas cache ... or
telonex onchain_fills cache ... / telonex trades cache ... and skip
local/API decoding. Local/API trade-tick labels also include the exact Telonex
channel, such as telonex local onchain_fills or telonex local trades.
Multi-replay Telonex loading uses staged source preparation with bounded
materialization: all Polymarket Gamma/CLOB metadata is prepared first, source
fetch/cache work fans out, then book materialization, trade loading, and replay
building run through a smaller memory cap. BACKTEST_REPLAY_LOAD_WORKERS
controls source-stage concurrency, defaults to 32, and can be raised to 128;
BACKTEST_REPLAY_MATERIALIZE_WORKERS controls the memory-heavy replay object
stage and defaults to 4. Telonex API requests are separately capped by
TELONEX_API_WORKERS and default to 32; local file, DuckDB, and parquet
operations are capped by TELONEX_FILE_WORKERS and default to 28 to avoid
file-descriptor pressure on large 100-market loads.
Timing Expectations By Source
| Source | Expected behavior | When it happens |
|---|---|---|
| PMXT filtered cache | Fastest PMXT path; compact filtered parquet per market/token/hour | Second run onward for the same market, token, and hour |
| Local PMXT raw archive | Local disk bound; grouped by raw hour during batch loads | Hour is missing from filtered cache but exists in local:/... |
| Remote PMXT raw archive | Network and full-hour parquet bound; grouped by raw hour during batch loads | Hour is missing locally and archive fallback is configured |
| Telonex deltas cache | Fastest Telonex path; materialized Nautilus OrderBookDeltas |
Same market/token/day/window was already converted once |
| Telonex materialized trade-tick cache | Fastest Telonex execution path; materialized Nautilus TradeTicks |
Same market/token/day/window Telonex fills or trades were already converted once |
| Telonex fast API cache | Local disk bound; avoids nested payload materialization | API day was previously downloaded and sidecar exists or was lazily migrated |
| Local Telonex mirror | Local disk bound; manifest-pruned parquet parts | /Volumes/storage/telonex_data has the requested full-book day |
| Telonex API | Network and daily parquet bound | Cache/local mirror misses and TELONEX_API_KEY is available |
| None | Fast miss | Hour/day does not exist in any source |
How To See This Output
Run any public PMXT or Telonex runner directly:
uv run python backtests/polymarket_book_ema_crossover.py
uv run python backtests/polymarket_book_joint_portfolio_runner.py
uv run python backtests/polymarket_telonex_book_joint_portfolio_runner.py
Run all public Python backtests:
uv run python scripts/run_all_backtests.py
Use the timing harness helper when you want only source/timing diagnostics for a runner:
uv run python prediction_market_extensions/backtesting/_timing_test.py backtests/polymarket_book_ema_crossover.py
Timing output is additive to Nautilus logs. It should remain enabled by default so local/cache/archive/API source behavior is visible in normal runs.