D01-F04-A05 / Complete engineering topic

Point-in-Time Availability Guard

A production-minded guide to Point-in-Time Availability Guard.

D01 · MARKET DATA ENGINEERING
D01-F04-A05Canonical / Tested / Open
D01 / D01-F04
Key concepts

The governed definitions this build depends on. Read them first if a term is unfamiliar.

Practical promise. Reconstruct what a data system could actually have known at a historical cutoff without confusing the period described, the source's timestamps, and your pipeline's usable-data boundary.

Two-axis point-in-time decision showing valid time and knowledge time

A quarterly value may describe June, reach a regulator in August, appear on a public channel moments later, enter a vendor product later still, and become usable by your feature pipeline only after validation. If a historical join stores only “June,” every later revision can masquerade as June knowledge.

The guard in this article solves a narrow, important problem: given immutable revisions and defensible timestamps, it selects the latest observation valid at one cutoff and the latest version knowable at another. It does not guess missing availability times and it does not establish investment significance.

One record, several clocks

The terms are easiest to understand as separate questions:

Clock or boundaryQuestion it answers
Valid time / observation timeWhen does the fact apply?
Source acceptanceWhen did the source accept the submission?
Public disseminationWhen did a declared public channel expose it?
Vendor ingestionWhen did a vendor capture or transform it?
Internal receiptWhen did your organization receive it?
Transaction timeWhen did your audited store commit this version?
Feature-ready timeWhen could the downstream calculation actually use it?

The first is about the world described. Transaction time is about database history. The intermediate boundaries are about the information's journey. available_at must name one declared boundary; in this package it means feature-ready time.

This distinction is not pedantic. The SEC documents separate EDGAR fields for period of report, filing date, and acceptance time, and states that it does not provide a timestamp for when filing content first appears on sec.gov (SEC timestamp FAQ).

The two-cutoff rule

Let (v) be the valid-time cutoff and (k) the knowledge-time cutoff. A record is eligible only when

observation_timervandavailable_atrk.observation\_time_r \le v \quad\text{and}\quad available\_at_r \le k.

For each entity and feature, selection then has two ordered maxima:

  1. choose the greatest eligible observation_time;
  2. among rows for that observation, choose the greatest tuple (available_at, revision, record_id).

Written compactly:

o=maxobservation_time,r=argmaxobservation_time=o(available_at,revision,record_id).o^*=\max observation\_time, \qquad r^*=\arg\max_{observation\_time=o^*} (available\_at, revision, record\_id).

That ordering matters. If a correction to January arrives in March, a global maximum over availability could incorrectly displace an already-known February observation.

Rendering system map…

A numerical revision example

Suppose one metric describes 2026-02-13. Its three synthetic revisions become feature-ready on February 15, February 22, and March 13.

Knowledge cutoffValid cutoffResult
February 20February 20revision 0
February 25February 20revision 1
March 15February 20revision 2

At each step, later releases remain stored but ineligible. Equality is inclusive: a revision whose available_at exactly equals the knowledge cutoff is eligible.

Revision timeline showing eligible and blocked releases

A real filing demonstrates what remains unknown

Apple's Form 10-Q for the quarter ended 2024-06-29 is a useful historical case because its official dates are visibly different. The SEC filing-detail page reports:

  • period of report: 2024-06-29;
  • accepted: 2024-08-01 18:03:34; and
  • filing date: 2024-08-02.

These are directly verifiable on the SEC filing-detail page, and the filed 10-Q confirms the reporting period.

What do these facts prove? They prove the filing's period, EDGAR acceptance timestamp, and assigned filing date. They do not prove when a data vendor updated a field, when your downloader received the document, when a warehouse committed it, or when a feature became ready.

The SEC says filings are often available on sec.gov within one to three minutes of the EDGAR timestamp, but it also says the lag can increase and is neither guaranteed nor predictable. Adding one or three minutes to this acceptance time would therefore create a fictional event timestamp, not a verified one. The EDGAR evidence record keeps every unknown explicit.

Build the data contract before the query

The executable record contains entity, feature, observation time, available_at, revision, finite value, and immutable record ID. Production lineage should additionally retain every timestamp it truly observes: acceptance, dissemination, vendor ingestion, internal receipt, transaction, and feature-ready time.

Do not overwrite one timestamp with another. Store availability_basis with the record so a reviewer knows whether available_at means public source, warehouse commit, or feature-ready. If the timestamp is unknown, keep it unknown and exclude the record from analyses requiring that boundary.

Implementation walkthrough

Both reference implementations follow the same sequence:

Plain text
validate records and UTC offsets
filter observation_time <= valid cutoff
filter available_at <= knowledge cutoff
group by entity and feature
choose latest observation in each group
choose latest eligible revision for that observation
emit the original record with provenance

The Python and TypeScript implementations accept separate knowledge and valid cutoffs. Omitting the valid cutoff makes it equal to the knowledge cutoff, which is convenient for a “latest value knowable then” query but should remain an explicit design choice.

Use the lab to inspect the boundary

The guided as-of playground starts with a complete canonical snapshot. It provides three scenarios:

  • a normal revision chain;
  • delayed internal ingestion after public dissemination; and
  • separate valid-time and knowledge-time cutoffs.

Use Back and Step to cross one boundary at a time. The audit table shows why each record is selected, blocked by knowledge time, or excluded by valid time. Reset is deterministic, Play uses the same transition function as Step, and reduced-motion mode remains fully usable.

Tests that matter

The shared fixture contains 180 synthetic rows across 3 entities, 20 weekly observations, and 3 releases. Tests verify:

  • no selected row exceeds either cutoff;
  • an availability equality is eligible;
  • valid time can remain in February while knowledge time advances to May;
  • a late old correction cannot displace a newer observation;
  • malformed timestamps, negative revisions, non-finite values, and duplicate IDs fail explicitly; and
  • Python and TypeScript select the same fixture records.

These tests establish agreement with the definition. They do not prove that an upstream timestamp is truthful or that a downstream backtest is unbiased in every other respect.

Common failures

  • Joining only on a reporting-period date.
  • Treating an official filing date as an intraday availability timestamp.
  • Equating source acceptance with consumer feature readiness.
  • Replacing a specific observation with a service's “typical” delay.
  • Keeping only the latest revision.
  • Letting revision numbers override availability order.
  • Forgetting that identifiers, units, taxonomies, and calendars also change through knowledge time.

Production checklist

  • Declare the valid-time and knowledge-time cutoffs separately.
  • State exactly what available_at means for this system.
  • Preserve raw records and every observed timestamp.
  • Keep unknown boundaries unknown.
  • Store source, schema, unit, identifier, and transformation versions.
  • Test cutoff equality, clock precision, duplicate IDs, late revisions, and empty eligibility sets.
  • Monitor the delay from receipt through transaction commit to feature-ready.
  • Reproduce snapshots from immutable history before trusting a backtest.

Summary

A point-in-time guard is a two-axis filter followed by a two-stage selection. Valid time answers what period is in scope; knowledge time answers what the declared system could use. Source acceptance, public dissemination, ingestion, transaction time, and feature readiness supply evidence for that second axis, but they are not interchangeable.

Continue with Filing-Revision Versioning to design the immutable version history that this guard queries, and with Asynchronous Return Alignment to align market observations after eligibility is established.

ReferencesPrimary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

SEC-APPLE-10Q-INDEX — Apple 2024 third-quarter Form 10-Q filing detail

  • Organization or authors: U.S. Securities and Exchange Commission; filer Apple Inc.
  • Source type: Official EDGAR filing-detail record
  • Publication or effective date: Accepted 2024-08-01; filing date 2024-08-02
  • Version: Accession 0000320193-24-000081
  • URL or DOI: https://www.sec.gov/Archives/edgar/data/320193/000032019324000081/0000320193-24-000081-index.htm
  • Accessed: 2026-07-22
  • Jurisdiction: United States
  • Supports: Issuer, form, accession, period of report, acceptance timestamp, and official filing date used in the historical case.
  • Limitations: Does not report vendor ingestion, consumer receipt, feature-ready time, or the first instant content appeared on sec.gov.

SEC-APPLE-10Q — Apple Form 10-Q for the period ended 2024-06-29

  • Organization or authors: Apple Inc.; filed with the U.S. Securities and Exchange Commission
  • Source type: Primary issuer filing in EDGAR
  • Publication or effective date: Period ended 2024-06-29; filed 2024-08-02
  • Version: Accession 0000320193-24-000081, primary document aapl-20240629.htm
  • URL or DOI: https://www.sec.gov/Archives/edgar/data/320193/000032019324000081/aapl-20240629.htm
  • Accessed: 2026-07-22
  • Jurisdiction: United States
  • Supports: The reporting-period fact used as the valid-time anchor.
  • Limitations: Filing contents do not establish downstream system availability.

SEC-TIMESTAMPS — Webmaster Frequently Asked Questions

  • Organization or authors: U.S. Securities and Exchange Commission
  • Source type: Official technical FAQ
  • Publication or effective date: Last reviewed 2024-08-23
  • Version: Accessed 2026-07-22
  • URL or DOI: https://www.sec.gov/about/webmaster-frequently-asked-questions
  • Accessed: 2026-07-22
  • Jurisdiction: United States
  • Supports: EDGAR timestamp definitions; distinction among period of report, filed-as-of date, and acceptance; typical sec.gov lag; absence of a first-public-availability timestamp.
  • Limitations: The one-to-three-minute lag is explicitly not guaranteed or predictable and cannot establish availability for an individual consumer.

SEC-APIS — EDGAR Application Programming Interfaces

  • Organization or authors: U.S. Securities and Exchange Commission
  • Source type: Official API documentation
  • Publication or effective date: Last reviewed 2025-04-08
  • Version: Accessed 2026-07-22
  • URL or DOI: https://www.sec.gov/search-filings/edgar-application-programming-interfaces
  • Accessed: 2026-07-22
  • Jurisdiction: United States
  • Supports: Submissions and XBRL APIs have different typical processing delays; bulk archives have a nightly schedule; APIs update as filings disseminate.
  • Limitations: Typical processing delays are not guarantees for a particular request, vendor, or internal pipeline.

SEC-SUBMISSION-GUIDE — Attach and Submit a Filing Through the EDGAR Filing Website

ALFRED-VINTAGES — FRED Series Vintage Dates

  • Organization or authors: Federal Reserve Bank of St. Louis
  • Source type: Official API documentation
  • Publication or effective date: Current API documentation
  • Version: Accessed 2026-07-22
  • URL or DOI: https://fred.stlouisfed.org/docs/api/fred/series/series_vintagedates.html
  • Accessed: 2026-07-22
  • Jurisdiction: United States
  • Supports: Vintage dates represent dates when series values were revised or newly released.
  • Limitations: Date-level vintage semantics can be coarser than intraday consumer availability.
pointInTimeGuard.ts
export interface BitemporalRecord {
  entity: string;
  feature: string;
  observation_time: string;
  available_at: string;
  revision: number;
  value: number;
  record_id: string;
  [key: string]: unknown;
}

function time(value: string, field = "timestamp"): number {
  if (typeof value !== "string" || !/(Z|[+-]\d{2}:\d{2})$/.test(value)) {
    throw new Error(`${field} must be an ISO-8601 timestamp with a UTC offset`);
  }
  const parsed = Date.parse(value);
  if (!Number.isFinite(parsed)) throw new Error(`invalid ${field}: ${value}`);
  return parsed;
}

function validated(records: BitemporalRecord[]): BitemporalRecord[] {
  const seen = new Set<string>();
  records.forEach((record, index) => {
    for (const field of ["entity", "feature", "record_id"] as const) {
      if (typeof record[field] !== "string" || !record[field]) throw new Error(`record ${index} has invalid ${field}`);
    }
    if (seen.has(record.record_id)) throw new Error(`duplicate record_id: ${record.record_id}`);
    seen.add(record.record_id);
    time(record.observation_time, "observation_time");
    time(record.available_at, "available_at");
    if (!Number.isInteger(record.revision) || record.revision < 0) throw new Error(`record ${index} has invalid revision`);
    if (!Number.isFinite(record.value)) throw new Error(`record ${index} has invalid value`);
  });
  return records;
}

export function asOfSnapshot(records: BitemporalRecord[], knowledgeTime: string, validTime?: string) {
  const knowledgeCutoff = time(knowledgeTime, "knowledge_time");
  const validCutoff = time(validTime ?? knowledgeTime, "valid_time");
  const eligible = validated(records).filter(
    (record) =>
      time(record.observation_time) <= validCutoff &&
      time(record.available_at) <= knowledgeCutoff,
  );
  const groups = new Map<string, BitemporalRecord[]>();
  for (const record of eligible) {
    const key = `${record.entity}\u0000${record.feature}`;
    groups.set(key, [...(groups.get(key) ?? []), record]);
  }
  return [...groups.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([, rows]) => {
    const latestObservation = Math.max(...rows.map((row) => time(row.observation_time)));
    return rows
      .filter((row) => time(row.observation_time) === latestObservation)
      .sort((left, right) =>
        time(right.available_at) - time(left.available_at) ||
        right.revision - left.revision ||
        right.record_id.localeCompare(left.record_id),
      )[0];
  });
}

export function leakageAudit(records: BitemporalRecord[], knowledgeTime: string, validTime?: string) {
  const knowledgeCutoff = time(knowledgeTime, "knowledge_time");
  const validCutoff = time(validTime ?? knowledgeTime, "valid_time");
  return validated(records)
    .filter(
      (record) =>
        time(record.observation_time) <= validCutoff &&
        knowledgeCutoff < time(record.available_at),
    )
    .map((record) => ({ ...record, reason: "available_after_query_time" }));
}
Full-height labpoint in time playgroundOpen full screen