The event clock isn't the panel clock
what you'll learn · Why the panel clock and the event clock need the same point-in-time discipline but different feature shapes, and the minimum surface that makes both work in one platform.
A research platform calibrated for day bars and minute bars handles event-driven trades awkwardly. Adding event-clock research doesn't need a new runtime — it needs one new column on the feature DAG and one new rule in the risk gate.
A research platform usually picks one clock and runs everything on it. Day bars, minute bars, 5-second bars — whatever the panel cadence is, every feature gets computed at every tick, every backtest walks the panel, every risk check fires per rebalance. It’s a clean shape and most of what you’d ever want to trade fits inside it.
The cases that don’t fit are the ones that move money fastest:
- A tweet from a high-reach account crosses the wire. The stock moves in 90 seconds. By the time the next minute bar closes, the trade is over.
- A FOMC statement releases at 2 PM ET. The interesting trade is before it — the 24-hour window during which positions reduce in anticipation. Or the 15 minutes after, when the curve repositions.
- A prediction-market contract quotes a different implied probability for the same real-world event across two venues. The arbitrage closes inside a second.
The shape these share: the signal isn’t “the value of some panel
column at time t.” It’s “when the event landed relative to other
events, plus what the event implies.” Different surface. Same
platform, if you’re careful.
What you don’t need
The internet’s advice on event-driven trading converges on a stack: Kafka, partitioned by symbol, sub-second event loop, an online feature store keeping the rolling sentiment fresh, a separate inference path that bypasses the panel-clock runtime. Production bots that execute inside 500ms of a sentiment shift do exist; the literature documents them, and the firms operating them publish slide decks about their Kafka topology.
For one operator running one strategy live, that stack is the wrong shape. Three pieces of advice not to take:
- Don’t introduce a separate runtime. A second runtime is two runtimes’ worth of operational surface and one runtime’s worth of strategies. The panel-clock orchestrator already pays for reconciliation, OMS, risk, monitoring. A sub-second event loop pays for those all over again, in async code, on a hotter cache.
- Don’t introduce a separate feature store. Walk-forward without leakage is hard enough with one store. Two stores with two clocks is two PIT contracts that have to stay consistent — and they won’t.
- Don’t reach for Kafka. Kafka is the right shape for the
event-log discipline, not for a person reading one strategy’s
fills in the morning. The discipline is “every event is a row in
an append-only table with
(exchange_ts, knowledge_time).” The log gives you that for free; the broker doesn’t.
The cheaper move is to teach the existing platform to understand the event clock as one more input class — not a new runtime.
What you do need
The minimum surface is small. Three pieces, all reusing existing patterns:
1. Event silver tables
Three new tables in the parquet store, all wearing the same bitemporal floor every other silver table wears:
event_post # tweets, news items, forum posts
event_release # scheduled releases (FOMC, earnings)
event_quote # prediction-market quotes
Each row carries (exchange_ts, knowledge_time, source, …).
knowledge_time is the key. For a tweet, it’s the publish time —
when the platform could have known. For a forward-looking FOMC
release, it’s the scheduled time until the meeting actually
fires; before that the row exists but is filtered out of any
point-in-time query (the calendar was published, the content
wasn’t). ADR-0027 codifies the schemas; ADR-0028 names the
vendor adapters and their cost tiers.
2. Event-keyed feature nodes
A feature whose input is an event stream, not the panel. Its
materialised value at panel time t is a reduction over the
event log filtered by knowledge_time <= t:
time_to_next_fomc = event_feature(
kind="event_release",
filter=lambda e: e.release_kind == "fomc",
reduction="time_to_next",
lag="0s",
)
Same content-addressing as a regular feature: the hash composes the kind, the filter source, the reduction identifier, the lag literal, and (when the reduction operates on an LLM score) the registered model artifact. A re-trained scorer rotates the hash; a comment edit on the Python wrapper does not. ADR-0029 codifies the surface.
The PIT linter gets one rule added: event-keyed features must
filter the stream by knowledge_time. Filtering by exchange_ts
silently leaks future-knowledge rows (the calendar case). The
linter rejects the leakage shape at registration; the materialiser
applies the gate at runtime as a defensive raise.
3. Event-aware cooldown in the risk gate
A strategy that fires on event:event_release/FOMC-2026-06 cites
that event in its provenance row. The risk gate reads the
strategy’s recent provenance and refuses to fire on the same event
twice inside its cooldown window — 30 seconds for posts, an hour
for releases, 5 seconds for prediction-market quotes. Same hook
the OMS already uses; one new rejection code.
ADR-0031 ratifies the cooldown rule. Two more rules — a reach floor and an adversarial kill switch — are named in the same ADR but deferred until there’s data to gate against and a scorer to defend against. The cooldown alone closes the loop on the single-tweet-cycles-the-broker case.
What the PIT discipline buys
The panel-clock platform already enforces a bitemporal floor: every
row carries knowledge_time, and a query filtered to t excludes
rows whose knowledge_time is in t’s future. Adding the event clock
doesn’t add new discipline. It only adds one rule to the existing
discipline: when reducing an event stream, use knowledge_time,
not exchange_ts or ingested_at. A backtest that uses
exchange_ts for the calendar case will see every future FOMC
meeting from the moment the calendar was published — a leak that
looks like prescience.
The same checklist from walk-forward without leakage carries over, with one addition specific to LLM-scored event features: pin the model artifact ID in the feature hash. A scorer’s behaviour is its weights + its prompt — not its Python wrapper. Hashing the wrapper gives you false cache invalidation: a comment edit rotates the hash, a re-train silently doesn’t.
The first artifact
The simplest event-keyed feature you can write with no scorer is
time_to_next_fomc. Seconds until the next FOMC meeting whose
calendar release is known at the panel clock. No LLM, no
ingestion adapter beyond the public Federal Reserve calendar page,
no risk model decisions on the critical path.
It’s also the most boring useful event feature. Once it’s in the
DAG, every strategy can read it. A pre-FOMC blackout becomes a
filter (time_to_next_fomc > 24h). A 15-minute post-FOMC drift
window becomes a different filter. Neither needs an LLM; neither
needs sub-second latency; both close research questions the
panel-clock platform couldn’t answer cleanly.
The pattern is the same as the citation graph as substrate: once the column exists, every consumer can read it. The hard part isn’t the feature; it’s deciding the column is part of the platform.
The next slice
After the calendar, the surface that actually moves money is textual events — tweets, news, forum posts. Three things have to land before that surface is honest:
- An
event_postingestion adapter. Pulled from a free filtered stream first — targeted accounts, not the full firehose. The firehose is a four-figure-monthly commitment that should follow a result, not precede one. - An LLM scorer with the model artifact registered. The scorer’s identity is the artifact, not the wrapper. Without that pin, every backtest is built on whichever model happened to be loaded at the time, and replay determinism is gone.
- The adversarial-robustness story written down. A tweet can attempt prompt injection against any scorer that reads it. The scorer treats post text as untrusted user content, and the risk gate’s kill switch fires if the score distribution shifts past a calibration threshold. This is not a future concern — it’s a day-one design constraint, restated so a future review can’t say it was forgotten.
None of those depend on a new runtime. They depend on one new ingestion path and one new model artifact. Both fit the existing platform’s shape; both pay back into the same feature DAG, the same risk gate, the same citation graph.
The event clock isn’t the panel clock. It’s also not a different platform.