Survivorship-Bias Guard
Keep Historical Failures in the Test
Install and import#
npm install fintech-algorithmsimport { guardSurvivorship } from "fintech-algorithms/corporate-actions-and-security-master-data/point-in-time-universe/survivorship-bias-guard";Signature#
guardSurvivorship(input)Compares a universe you intend to test against the point-in-time roster and reports what is missing. It is a gate rather than a transform: the failure is the product.
Parameters#
| Name | Type | Notes |
|---|---|---|
input | { universeId: string; definitionId: string; effectiveAt: string; knownAt: string; candidateUniverse: object; universeDefinitions: object[]; membershipRecords: object[]; comparisonRoster: object } | candidateUniverse is the roster under test — typically whatever a data pull returned — and comparisonRoster is the point-in-time truth to check it against. |
Returns#
{ status, definitionVersion, candidateUniverseAsOf, missingSecurities, extraSecurities, … }
Which securities are missing from the candidate and which should not be there, with the definition version the comparison used.
Errors#
- When the universe definition is unknown at knownAt — reported as a status rather than thrown
Complexity: time O(members),
space O(members).
Worked example#
verified This is the worked example published in the article, replayed by the test suite on every run. The output cannot drift.
Input#
{
"universeId": "UNIVERSE:SYNTH:CORE",
"definitionId": "DEF:SYNTH:CORE",
"effectiveAt": "2023-12-31T23:59:59Z",
"knownAt": "2024-01-02T12:00:00Z",
"candidateUniverse": {
"construction": "historical_membership_ledger",
"asOf": "2023-12-31T23:59:59Z",
"availableAt": "2024-01-02T10:00:00Z",
"securityIds": ["SEC:A", "SEC:B", "SEC:C", "SEC:D"],
"sourceId": "SYNTH-CANDIDATE-LEDGER"
},
"universeDefinitions": [
{
"definitionId": "DEF:SYNTH:CORE",
"universeId": "UNIVERSE:SYNTH:CORE",
"revision": 1,
"effectiveFrom": "2020-01-01T00:00:00Z",
"effectiveTo": null,
"announcedAt": "2019-12-15T12:00:00Z",
"availableAt": "2019-12-15T12:05:00Z",
"status": "active",
"baseType": "named_research_universe",
"baseId": "BASE:SYNTH:001",
"ruleSummary": "Synthetic members documented by effective-dated membership evidence.",
"sourceId": "SYNTH-DEFINITION-R1"
},
{
"definitionId": "DEF:SYNTH:CORE",
"universeId": "UNIVERSE:SYNTH:CORE",
"revision": 2,
"effectiveFrom": "2020-01-01T00:00:00Z",
"effectiveTo": null,
"announcedAt": "2024-02-01T09:00:00Z",
"availableAt": "2024-02-01T09:05:00Z",
"status": "active",
"baseType": "named_research_universe",
"baseId": "BASE:SYNTH:001",
"ruleSummary": "Later documentation revision; unavailable to the canonical query.",
"sourceId": "SYNTH-DEFINITION-R2"
}
],
"membershipRecords": [
{
"membershipId": "MEM-A",
"revision": 1,
"universeId": "UNIVERSE:SYNTH:CORE",
"securityId": "SEC:A",
"validFrom": "2020-01-01T00:00:00Z",
"validTo": null,
"announcedAt": "2019-12-15T12:00:00Z",
"availableAt": "2019-12-15T12:05:00Z",
"status": "active",
"changeType": "entry",
"eventId": "EVENT-A-ENTRY",
"sourceId": "SYNTH-MEM-A"
},
{
"membershipId": "MEM-B",
"revision": 1,
"universeId": "UNIVERSE:SYNTH:CORE",
"securityId": "SEC:B",
"validFrom": "2020-01-01T00:00:00Z",
"validTo": "2024-06-01T00:00:00Z",
"announcedAt": "2023-12-20T10:00:00Z",
"availableAt": "2023-12-20T10:05:00Z",
"status": "active",
"changeType": "exit",
"eventId": "EVENT-B-EXIT",
"sourceId": "SYNTH-MEM-B-R1"
},
{
"membershipId": "MEM-B",
"revision": 2,
"universeId": "UNIVERSE:SYNTH:CORE",
"securityId": "SEC:B",
"validFrom": "2020-01-01T00:00:00Z",
"validTo": "2023-12-15T00:00:00Z",
"announcedAt": "2024-01-10T08:00:00Z",
"availableAt": "2024-01-10T08:05:00Z",
"status": "active",
"changeType": "revision",
"eventId": "EVENT-B-CORRECTION",
"sourceId": "SYNTH-MEM-B-R2"
}
],
"comparisonRoster": {
"observedAt": "2025-01-02T00:00:00Z",
"availableAt": "2025-01-02T12:00:00Z",
"securityIds": ["SEC:A", "SEC:C", "SEC:D"],
"sourceId": "SYNTH-CURRENT-ROSTER"
}
}Call#
guardSurvivorship(input)Returns#
object with 7 fields: status, definitionVersion, eligibleSecurityIds, ineligibleSecurityIds, unknownSecurityIds, ambiguousSecurityIds, hindsightDiagnostics
{
"status": "resolved",
"definitionVersion": "DEF:SYNTH:CORE@1",
"eligibleSecurityIds": ["SEC:A", "SEC:B", "SEC:C"],
"ineligibleSecurityIds": ["SEC:D"],
"unknownSecurityIds": [],
"ambiguousSecurityIds": [],
"hindsightDiagnostics": {
"historicallyEligibleMissingFromCurrent": ["SEC:B"],
"currentRosterNotHistoricallyEligible": ["SEC:D"],
"retentionRatio": 0.666667
}
}Other exports#
This module also exports
compareEqualWeightedReturns. Every module additionally exports run as an alias of its
primary function, and a meta object carrying its catalog id, domain, family,
shape and article URL.
Diagrams#
Calculation flow#
Causal membership and hindsight diagnostics
flowchart LR
Q["effectiveAt and knownAt"] --> A["Rows available by knownAt"]
A --> V["Validate revision prefixes"]
V --> D["Resolve supported definition"]
D --> C{"Historical candidate ledger complete?"}
C -->|No| I["Incomplete"]
C -->|Yes| M["Classify membership intervals"]
M --> R["Causal result"]
R -.-> H["Non-causal later-roster diagnostic"]
R -.-> P["Synthetic-only return illustration"]
Membership classification states
stateDiagram-v2
[*] --> SelectKnownVersions
SelectKnownVersions --> Eligible: exactly one interval covers effectiveAt
SelectKnownVersions --> Ineligible: active evidence excludes effectiveAt
SelectKnownVersions --> Unknown: no active evidence available
SelectKnownVersions --> Ambiguous: multiple intervals cover effectiveAt
Eligible --> Resolved
Ineligible --> Resolved
Unknown --> NeedsEvidence
Ambiguous --> NeedsReconciliation
How it works#
This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.
References#
- R1 - S&P Dow Jones Indices Equity Indices Policies & Practices — S&P Dow Jones Indices
- R2 - Lehman Brothers Form 8-K: Chapter 11 filing — Lehman Brothers Holdings Inc., filed with the U.S. Securities and Exchange Commission
- R3 - Lehman Brothers Form 8-K: NYSE suspension — Lehman Brothers Holdings Inc., filed with the U.S. Securities and Exchange Commission
- R4 - S&P 150 comparison — S&P Dow Jones Indices
- R5 - CRSP Research Data Products — Center for Research in Security Prices
- R6 - CRSP PERMNO and PERMCO — Center for Research in Security Prices
- R7 - CRSP10 US Stock Database Guide — Center for Research in Security Prices
- R8 - SEC EDGAR access guidance — U.S. Securities and Exchange Commission