Time discipline is the first AtlantisV2 boundary. If time is loose, every feature, pattern, score, permission decision, and outcome can drift between live and replay.
Core Rule
No core function should call wall-clock time directly. Core receives explicit time through:
ReferenceTimeClockFixedClockCycleContextEventEnvelopeFreshnessPolicySessionContext
Live and replay should differ only by clock source and event stream.
ReferenceTime
ReferenceTime is the as-of timestamp for a decision.
It answers:
- what data is visible
- which events are future and must be ignored
- how freshness is evaluated
- what timestamp downstream objects should carry
Every reference time must be timezone-aware.
reference_time = ReferenceTime(datetime(2026, 4, 24, 9, 15, tzinfo=UTC))
CycleContext
CycleContext identifies one evaluation cycle:
cycle_idreference_timemodeexchangesession_idtriggerstarted_atinput_cutoff_tsprevious_cycle_id
The cycle id becomes part of deterministic object ids. This prevents replay reruns and live cycles from silently colliding.
EventEnvelope
EventEnvelope is the canonical adapter-to-core event shape.
It carries:
event_idevent_typesourcesource_sequenceinstrument_refevent_tsingest_tsreference_timepayloadschema_versiontrace_id
Adapters can parse venue payloads, but they should stop at envelopes. The core builder owns ordering, dedupe, filtering, and capability construction.
EventLineage
EventLineage is retained on market events so a feature, pattern, or outcome can be traced back to input events.
Lineage includes:
- original event id
- source adapter
- source sequence
- ingest timestamp
- schema version
- trace id
This is essential for research and replay debugging.
FreshnessPolicy
FreshnessPolicy decides whether a capability is fresh as of the reference time.
FreshnessPolicy(max_age=timedelta(seconds=30))
Freshness is not the same as missing. A stale price may still be present, but downstream definitions can refuse it or emit stale availability.
SessionContext
SessionContext captures market session state:
- exchange
- session id
- phase
- tradability
- open and close timestamps
This prevents core logic from assuming that every timestamp is tradable. Permission and lifecycle can later use the same typed session object.
Implemented Invariants
The current tests prove:
- naive datetimes are rejected
- future events are ignored by
SnapshotBuilder - duplicate event ids are ignored
- mixed numeric and string sequences sort deterministically
- invalid price payloads create quality flags
- source lineage survives the envelope-to-snapshot boundary
Why This Preserves Atlantis
Legacy Atlantis had valuable behavior, but some paths could depend on process time, latest rows, or fire-time parsing. AtlantisV2 makes that explicit:
- fire/open/close timestamps are separate
- duplicate events are idempotent or ignored
- replay and live consume the same event shape
- future data is not available to features or patterns