The governed definitions this build depends on. Read them first if a term is unfamiliar.
- PrimaryEvent TimeEvent time is the timestamp assigned to when a market or business event occurred at its source.
- PrimaryObservation TimeObservation time is the timestamp used to place a measured value on a time axis.
- ImportantAvailability TimeAvailability time is the earliest evidenced instant at which information was usable by a specified system or decision process.
- ImportantLate-Arriving DataLate-arriving data is a record that reaches a processing boundary after the expected time for its event, partition, or decision window.
- ImportantPoint-in-Time DatasetA point-in-time dataset can reconstruct what information was valid and available to a specified user or system at a historical decision time.
- ImportantProcessing TimeProcessing time is when a computing system received, transformed, stored, or otherwise handled a record.
- ImportantTime ZoneA time zone is a governed set of civil-time offset rules used to interpret local dates and times.
- MentionedInterpolationInterpolation estimates a value between known observations according to a declared mathematical rule.
Practical promise. Determine exactly how two return intervals overlap and when the pair became jointly knowable—without pretending that geometry alone estimates covariance or produces an aligned return.
Two securities rarely update at identical instants. A tempting response is to pair returns whose end timestamps look close. That shortcut hides the time covered by each return. The reliable first step is narrower: represent every return as an event interval, compare the intervals, and preserve availability and partition evidence.
This article teaches that first step. The result is a diagnostic record, not a statistical estimator and not an investment signal.
The boundary that prevents a false claim
An overlap rule answers which intervals share event time. It does not say how price innovation was distributed within either interval. Therefore this package never prorates a return by overlap, multiplies return pairs, estimates covariance, or claims to remove nonsynchronous-sampling bias.
The Hayashi–Yoshida method is a complete covariance estimator that uses overlap as one part of its definition under a stochastic model. This package is not a partial implementation of that estimator. It supplies an auditable geometry diagnostic that a properly specified downstream method may consume.
Define event time and availability separately
For return , let the event interval be
The start is exclusive and the end is inclusive. Positive duration still uses a strict boundary test: two intervals that only meet at one endpoint have zero shared duration.
available_at is the first time the resolved return record was knowable. A
pair becomes jointly knowable at
At historical evaluation time , the pair is available only when . This prevents an ex-post overlap from silently becoming a point-in-time feature before both source returns existed.
In an append-only view, a record's knowledge interval is . If a correction later supersedes it, the version interval ends at the correction time. The reference implementation expects that version resolution to happen upstream and rejects duplicate IDs or overlapping return intervals rather than choosing a winner silently.
Calculate overlap without inventing weights
For left interval and right interval , define
The package reports how much of each interval is covered:
These fractions are descriptive. They are not return weights. Using them to split a return would assume something about the path inside an interval that the endpoints do not reveal.
Six valid geometry states
The diagnostic classifies valid pairs as:
exact: both boundaries match;left_within_rightorright_within_left: one interval is nested;partial: the intersection has positive duration without containment;touching: one interval ends exactly where the other begins;disjoint: a positive gap separates them.
Zero-length and reversed intervals are invalid inputs, not geometry classes. Intervals in different session or analysis partitions are never paired.
A worked partial-overlap example
Suppose the left return owns seconds and the right return owns seconds. Their intersection is , so:
- overlap duration is 1 second;
- left coverage is ;
- right coverage is .
If the left record was available at 4.2 seconds and the right record at 5.25 seconds, the pair became jointly knowable at 5.25 seconds. A query at 5.0 seconds must report it as unavailable even though the event intervals overlap.
No return value appears in this calculation. Tests deliberately change the stored returns while holding the timestamps fixed and verify that the geometry does not change.
The diagnostic flow
The implementations expose diagnostic_only: true, estimator: null, and
weighting_or_return_allocation: none. These are not decorative labels; they
make the scope machine-readable.
Explore the guided lab
Open the interval-overlap playground. Its initial screen already shows a complete partial-overlap case. Choose exact, nested, touching, disjoint, or unavailable scenarios, then use Back and Step to separate geometry from point-in-time availability. Play and Pause traverse the same deterministic stages; Reset restores the canonical partial case.
The lab's six cases are synthetic. They teach decision boundaries and do not stand in for market evidence.
Implementation contract
Python and TypeScript perform the same sequence:
- validate required fields and RFC 3339 UTC timestamps at second or millisecond precision;
- reject zero-length, reversed, overlapping, or unresolved duplicate records;
- partition before comparison;
- compute event overlap and geometry;
- compute both descriptive coverage fractions;
- use the later source availability timestamp for the pair;
- emit exhaustive diagnostics without multiplying return values.
The shared fixture contains three partitions and every important boundary state. Tests cover exact arithmetic, availability cutoffs, invalid inputs, correction ambiguity, partition isolation, and Python/TypeScript parity.
What not to infer
Positive overlap does not prove contemporaneous price discovery, causality, correlation, covariance, predictability, or profitability. High coverage does not show that the intervals contain evenly distributed information. The diagnostic does not correct bid-ask bounce, latency, endogenous sampling, discrete prices, jumps, or measurement error.
If the downstream question is covariance estimation, specify a complete estimator, stochastic assumptions, noise treatment, sampling regime, normalization, inference, and matrix-level checks. Do not rename this overlap report as an estimator after the fact.
Why there is no named historical security example yet
A defensible example needs more than two public price points. It requires traceable event timestamps, first-availability timestamps, correction state, security identity, session-calendar rules, and redistribution permission. The current package has no source satisfying that complete evidence contract.
The honest choice is to defer the example. A later enhancement can add one when the evidence is archived and reproducible; until then, a named company and date would look concrete while leaving the decisive time semantics unverified.
Summary and next topic
You can now distinguish exact, nested, partial, touching, and disjoint return intervals; calculate shared duration and coverage; and determine when a pair was jointly knowable. You can also state the crucial limit: overlap is a diagnostic input, not a return estimator.
Continue with Missing-Bar Gap Classifier, which uses session and source evidence to explain why an expected bar is absent.
Primary references and visual asset map
Source roles and limitations are recorded in REFERENCES.md. The canonical definition is maintained in the topic README.
| Asset | Teaching role | Source | Status |
|---|---|---|---|
| Article overview | Diagnostic boundary and workflow | ../visuals/static/article-hero.svg | Ready |
| Geometry comparison | Exact, nested, partial, touching, disjoint | ../visuals/static/return-overlaps.svg | Ready |
| Guided playground | Geometry stages and availability cutoff | ../visuals/animated/async-return-lab.html | Ready |
Rendered from the canonical Mermaid sources linked by this article.
Diagnostic interval-overlap flow
This diagram keeps correction resolution, event geometry, and point-in-time availability visible as separate gates.
Takeaway: interval overlap can support a downstream method, but it does not define that method's allocation, weighting, assumptions, or inference.
References2 primary sources and evidence notesExpand the source trail, evidence role, and limitations behind the engineering choices.
Expand the source trail, evidence role, and limitations behind the engineering choices.
Accessed 2026-07-22.
R01 — On covariance estimation of non-synchronously observed diffusion processes
- Organization or authors: Takaki Hayashi and Nakahiro Yoshida
- Source type: Original peer-reviewed paper, Bernoulli 11(2), 359–379
- Publication date: 2005
- Version: Published article
- URL or DOI: https://doi.org/10.3150/bj/1116340299
- Author-hosted copy: https://www.ms.u-tokyo.ac.jp/~nakahiro/mypapers_for_personal_use/hayyos03.pdf
- Accessed: 2026-07-22
- Jurisdiction: Statistical methodology
- Supports: A complete covariance estimator can use products of increments selected by overlapping observation intervals; nonsynchronous estimation requires assumptions beyond interval geometry
- Limitations: Continuous diffusion and asymptotic setting; it does not define the package's engineering schema, correction policy, availability fields, or coverage fractions
R02 — RFC 3339: Date and Time on the Internet: Timestamps
- Organization or authors: Internet Engineering Task Force; Graham Klyne and Chris Newman
- Source type: Standards-track timestamp specification
- Publication date: July 2002
- Version: RFC 3339, updated by RFC 9557
- URL or DOI: https://www.rfc-editor.org/info/rfc3339/
- Accessed: 2026-07-22
- Jurisdiction: Global technical standard
- Supports: Unambiguous UTC timestamp serialization used by the package
- Limitations: Does not define market event time, availability time, return ownership, correction policy, or interval arithmetic
Evidence classification
- Sourced fact: The Hayashi–Yoshida estimator is a complete covariance method whose selection rule uses overlapping observation intervals (R01).
- Implementation choice: Half-open interval ownership, explicit partitions, correction-resolved inputs, availability cutoff logic, and diagnostic coverage fractions are this package's engineering contract.
- Pedagogical simplification: Synthetic whole-second examples make boundary cases hand-checkable.
- Empirical claim: None. No market dataset or historical security example is used.
- Author inference: A named historical example without event, availability, correction, partition, identity, and licensing evidence would be misleading.
Full dependency-light reference implementations in both supported languages.
/** Event-time interval-overlap diagnostics for asynchronous returns.
*
* This module does not estimate covariance, allocate returns, or claim to
* remove nonsynchronous-sampling bias.
*/
export type ReturnInterval = {
return_id: string;
instrument: string;
partition: string;
event_start: string;
event_end: string;
available_at: string;
return_value?: number;
};
type ValidatedInterval = ReturnInterval & {
_start_ms: number;
_end_ms: number;
_available_ms: number;
};
const milliseconds = (value: string, field: string): number => {
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(value)) {
throw new Error(`${field} must be an RFC 3339 UTC timestamp at second or millisecond precision`);
}
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) throw new Error(`${field} is not a valid timestamp`);
return parsed;
};
const timestamp = (value: number): string => new Date(value).toISOString();
function validateStream(rows: ReturnInterval[], side: string): ValidatedInterval[] {
if (!Array.isArray(rows) || rows.length === 0) {
throw new Error(`${side} stream must contain at least one return interval`);
}
const required: (keyof ReturnInterval)[] = [
"return_id", "instrument", "partition", "event_start", "event_end", "available_at",
];
const seen = new Set<string>();
const validated = rows.map((row) => {
const missing = required.filter((key) => !(key in row));
if (missing.length) throw new Error(`${side} interval is missing ${missing.join(",")}`);
for (const key of ["return_id", "instrument", "partition"] as const) {
if (typeof row[key] !== "string" || !row[key]) throw new Error(`${key} must be a non-empty string`);
}
if (seen.has(row.return_id)) throw new Error("return_id values must be unique after correction resolution");
seen.add(row.return_id);
const start = milliseconds(row.event_start, "event_start");
const end = milliseconds(row.event_end, "event_end");
const available = milliseconds(row.available_at, "available_at");
if (start >= end) throw new Error("event_start must be strictly earlier than event_end");
if (available < end) throw new Error("available_at cannot precede event_end");
if (row.return_value !== undefined && !Number.isFinite(row.return_value)) {
throw new Error("return_value must be finite when supplied");
}
return { ...row, _start_ms: start, _end_ms: end, _available_ms: available };
}).sort((a, b) => a.partition.localeCompare(b.partition) || a._start_ms - b._start_ms || a._end_ms - b._end_ms);
if (new Set(validated.map((row) => row.instrument)).size !== 1) {
throw new Error(`${side} stream must contain exactly one instrument`);
}
const previous = new Map<string, ValidatedInterval>();
for (const row of validated) {
const prior = previous.get(row.partition);
if (prior && prior._end_ms > row._start_ms) {
throw new Error("return intervals within a stream cannot overlap");
}
previous.set(row.partition, row);
}
return validated;
}
const publicInterval = (row: ValidatedInterval): ReturnInterval => ({
return_id: row.return_id,
instrument: row.instrument,
partition: row.partition,
event_start: row.event_start,
event_end: row.event_end,
available_at: row.available_at,
...(row.return_value === undefined ? {} : { return_value: row.return_value }),
});
function classifyValidatedPair(left: ValidatedInterval, right: ValidatedInterval, evaluationMs: number | null) {
const availableMs = Math.max(left._available_ms, right._available_ms);
let geometry: string;
let overlapStart: string | null = null;
let overlapEnd: string | null = null;
let overlapMs = 0;
if (left.partition !== right.partition) {
geometry = "partition_mismatch";
} else {
const low = Math.max(left._start_ms, right._start_ms);
const high = Math.min(left._end_ms, right._end_ms);
overlapMs = Math.max(0, high - low);
if (overlapMs > 0) {
overlapStart = timestamp(low);
overlapEnd = timestamp(high);
}
if (high < low) geometry = "disjoint";
else if (high === low) geometry = "touching";
else if (left._start_ms === right._start_ms && left._end_ms === right._end_ms) geometry = "exact";
else if (left._start_ms >= right._start_ms && left._end_ms <= right._end_ms) geometry = "left_within_right";
else if (right._start_ms >= left._start_ms && right._end_ms <= left._end_ms) geometry = "right_within_left";
else geometry = "partial";
}
return {
left_return_id: left.return_id,
right_return_id: right.return_id,
partition: left.partition === right.partition ? left.partition : null,
geometry,
event_overlap_start: overlapStart,
event_overlap_end: overlapEnd,
event_overlap_ms: overlapMs,
left_overlap_fraction: overlapMs / (left._end_ms - left._start_ms),
right_overlap_fraction: overlapMs / (right._end_ms - right._start_ms),
pair_available_at: timestamp(availableMs),
jointly_available_at_evaluation: evaluationMs === null ? null : availableMs <= evaluationMs,
};
}
export function classifyIntervalPair(left: ReturnInterval, right: ReturnInterval, evaluatedAt?: string) {
const a = validateStream([left], "left")[0];
const b = validateStream([right], "right")[0];
return classifyValidatedPair(a, b, evaluatedAt === undefined ? null : milliseconds(evaluatedAt, "evaluated_at"));
}
export function diagnoseAsynchronousReturnAlignment(left: ReturnInterval[], right: ReturnInterval[], evaluatedAt?: string) {
const a = validateStream(left, "left");
const b = validateStream(right, "right");
const evaluationMs = evaluatedAt === undefined ? null : milliseconds(evaluatedAt, "evaluated_at");
const pairs = a.flatMap((leftRow) => b
.filter((rightRow) => leftRow.partition === rightRow.partition)
.map((rightRow) => classifyValidatedPair(leftRow, rightRow, evaluationMs)));
const positive = pairs.filter((pair) => pair.event_overlap_ms > 0);
const counts: Record<string, number> = {};
for (const pair of pairs) counts[pair.geometry] = (counts[pair.geometry] ?? 0) + 1;
const geometryCounts = Object.fromEntries(Object.entries(counts).sort(([x], [y]) => x.localeCompare(y)));
const totalOverlapMs = positive.reduce((sum, pair) => sum + pair.event_overlap_ms, 0);
const leftTotalMs = a.reduce((sum, row) => sum + row._end_ms - row._start_ms, 0);
const rightTotalMs = b.reduce((sum, row) => sum + row._end_ms - row._start_ms, 0);
return {
diagnostic_only: true,
estimator: null,
weighting_or_return_allocation: "none",
left_intervals: a.map(publicInterval),
right_intervals: b.map(publicInterval),
pairs,
positive_overlap_pairs: positive,
geometry_counts: geometryCounts,
total_event_overlap_ms: totalOverlapMs,
left_event_time_coverage: totalOverlapMs / leftTotalMs,
right_event_time_coverage: totalOverlapMs / rightTotalMs,
all_pairs_available_at_evaluation: evaluationMs === null || pairs.length === 0
? null
: pairs.every((pair) => pair.jointly_available_at_evaluation),
};
}
export const alignAsynchronousReturns = diagnoseAsynchronousReturnAlignment;
The embedded lab now expands to its full document height, keeping the article as the only scroll surface.