fintech-algorithms

Historical Constituent Reconstruction

Rebuild the Roster Without Looking Ahead

Install and import

bash
npm install fintech-algorithms
ts
import { calculate } from "fintech-algorithms/corporate-actions-and-security-master-data/point-in-time-universe/historical-constituent-reconstruction";

Signature

calculate(data)

Worked example

executed Captured by running this function on the input its own test provides. Real output of real code — but not asserted against a published figure.

Input

data
{
  "indexId": "IDX:SYNTH:ALPHA",
  "effectiveAt": "2024-08-15T00:00:00Z",
  "knownAt": "2024-08-15T12:00:00Z",
  "snapshots": [
    {
      "snapshotId": "BASE-2024",
      "indexId": "IDX:SYNTH:ALPHA",
      "revision": 1,
      "effectiveAt": "2024-01-01T00:00:00Z",
      "availableAt": "2024-01-02T09:00:00Z",
      "lastSequence": 0,
      "status": "active",
      "securityIds": ["SEC:A", "SEC:B", "SEC:C", "SEC:D"],
      "sourceId": "SYNTH-BASE"
    }
  ],
  "events": [
    {
      "eventId": "EV-001",
      "indexId": "IDX:SYNTH:ALPHA",
      "revision": 1,
      "sequence": 1,
      "eventType": "add",
      "effectiveOrder": 0,
      "effectiveAt": "2024-02-01T00:00:00Z",
      "announcedAt": "2024-01-15T12:00:00Z",
      "availableAt": "2024-01-15T12:05:00Z",
      "status": "active",
      "changes": [
        {
          "action": "add",
          "securityId": "SEC:E"
        }
      ],
      "sourceId": "SYNTH-NOTICE-001"
    },
    {
      "eventId": "EV-002",
      "indexId": "IDX:SYNTH:ALPHA",
      "revision": 1,
      "sequence": 2,
      "eventType": "replace",
      "effectiveOrder": 0,
      "effectiveAt": "2024-03-01T00:00:00Z",
      "announcedAt": "2024-02-20T16:00:00Z",
      "availableAt": "2024-02-20T16:03:00Z",
      "status": "active",
      "changes": [
        {
          "action": "delete",
          "securityId": "SEC:B"
        },
        {
          "action": "add",
          "securityId": "SEC:F"
        }
      ],
      "sourceId": "SYNTH-NOTICE-002"
    },
    {
      "eventId": "EV-003",
      "indexId": "IDX:SYNTH:ALPHA",
      "revision": 1,
      "sequence": 3,
      "eventType": "rebalance",
      "effectiveOrder": 0,
      "effectiveAt": "2024-04-01T00:00:00Z",
      "announcedAt": "2024-03-20T15:00:00Z",
      "availableAt": "2024-03-20T15:04:00Z",
      "status": "active",
      "changes": [],
      "sourceId": "SYNTH-NOTICE-003"
    }
  ]
}

Call

calculate(data)

Returns

object with 11 fields: status, indexId, effectiveAt, knownAt, baseSnapshotId, baseRevision, securityIds, membershipIntervals, …

{
  "status": "resolved",
  "indexId": "IDX:SYNTH:ALPHA",
  "effectiveAt": "2024-08-15T00:00:00Z",
  "knownAt": "2024-08-15T12:00:00Z",
  "baseSnapshotId": "BASE-2024",
  "baseRevision": 1,
  "securityIds": ["SEC:E", "SEC:F", "SEC:G", "SEC:H", "SEC:I", "SEC:J"],
  "membershipIntervals": [
    {
      "securityId": "SEC:A",
      "validFrom": "2024-01-01T00:00:00Z",
      "validTo": "2024-07-01T00:00:00Z"
    },
    {
      "securityId": "SEC:B",
      "validFrom": "2024-01-01T00:00:00Z",
      "validTo": "2024-03-01T00:00:00Z"
    },
    {
      "securityId": "SEC:C",
      "validFrom": "2024-01-01T00:00:00Z",
      "validTo": "2024-04-15T00:00:00Z"
    }
  ],
  "appliedEventVersions": ["EV-001@1", "EV-002@1", "EV-003@1", "EV-004@1", "EV-005@1", "EV-006@1"],
  "cancelledEventIds": [],
  "diagnostics": []
}

Diagrams

Historical Constituent Reconstruction — article hero
Historical Constituent Reconstruction — failure guard
Historical Constituent Reconstruction — worked example

Calculation flow

Bitemporal reconstruction flow
flowchart LR
    Q["Index ID, effectiveAt, knownAt"] --> B["Latest eligible base revision"]
    Q --> E["Latest eligible revision per event"]
    B --> G{"Complete ledger?"}
    E --> G
    G -->|"No base or gap"| I["incomplete"]
    G -->|"Overlap or conflict"| A["ambiguous"]
    G -->|"Unknown event semantics"| U["unsupported"]
    G -->|"Yes"| P["Replay ordered changes"]
    P --> R["resolved roster and audit trace"]
Resolution state lifecycle
stateDiagram-v2
    [*] --> Validating
    Validating --> Selecting: contract valid
    Selecting --> Incomplete: no base or sequence gap
    Selecting --> Ambiguous: competing evidence
    Selecting --> Unsupported: unknown event type
    Selecting --> Replaying: one complete ledger
    Replaying --> Ambiguous: impossible add or delete
    Replaying --> Resolved: ordered replay succeeds
    Incomplete --> [*]
    Ambiguous --> [*]
    Unsupported --> [*]
    Resolved --> [*]

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.

Read the article →

References

  • Tesla Set to Join S&P 500
  • S&P DJI Announces Implementation of Tesla’s Addition to S&P 500
  • Tesla Set to Join S&P 500 & 100; Apartment Income REIT to Join S&P MidCap 400
  • FTSE Russell Corporate Actions and Events Guide
  • Subscribe to FTSE Russell index data
  • Evidence boundary