fintech-algorithms

Delisting Return Reconstruction

Separate Observed Proceeds from Estimates

Install and import

bash
npm install fintech-algorithms
ts
import { calculate } from "fintech-algorithms/corporate-actions-and-security-master-data/identity-continuity/delisting-return-reconstruction";

Signature

calculate(input)

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

input
{
  "asOf": "2026-04-17T12:00:00Z",
  "baseCurrency": "USD",
  "previousRegularObservation": {
    "securityId": "SYNTH-OLD-A",
    "price": "48",
    "currency": "USD",
    "tradingAt": "2026-04-10T20:00:00Z",
    "observedAt": "2026-04-10T20:00:00Z",
    "sourceAt": "2026-04-10T20:01:00Z",
    "availableAt": "2026-04-10T20:05:00Z",
    "sourceId": "SYNTH-PRICES"
  },
  "lastRegularObservation": {
    "securityId": "SYNTH-OLD-A",
    "price": "50",
    "currency": "USD",
    "tradingAt": "2026-04-14T20:00:00Z",
    "observedAt": "2026-04-14T20:00:00Z",
    "sourceAt": "2026-04-14T20:01:00Z",
    "availableAt": "2026-04-14T20:05:00Z",
    "sourceId": "SYNTH-PRICES"
  },
  "event": {
    "eventId": "SYNTH-EVENT-001",
    "reason": "merger",
    "effectiveAt": "2026-04-15T13:30:00Z",
    "observedAt": "2026-04-01T14:00:00Z",
    "sourceAt": "2026-04-01T14:01:00Z",
    "availableAt": "2026-04-01T14:05:00Z",
    "sourceId": "SYNTH-EVENTS"
  },
  "proceedsStatus": "complete",
  "proceeds": [
    {
      "kind": "cash",
      "amountPerOldShare": "10",
      "currency": "EUR",
      "effectiveAt": "2026-04-15T13:30:00Z",
      "observedAt": "2026-04-15T15:00:00Z",
      "sourceAt": "2026-04-15T15:01:00Z",
      "availableAt": "2026-04-16T00:00:00Z",
      "sourceId": "SYNTH-PAYMENTS",
      "fxToBase": {
        "fromCurrency": "EUR",
        "toCurrency": "USD",
        "rate": "1.1",
        "rateAt": "2026-04-15T20:00:00Z",
        "observedAt": "2026-04-15T20:00:00Z",
        "sourceAt": "2026-04-15T20:01:00Z",
        "availableAt": "2026-04-16T00:00:00Z",
        "sourceId": "SYNTH-FX"
      }
    },
    {
      "kind": "stock",
      "quantityPerOldShare": "0.8",
      "successorSecurityId": "SYNTH-NEW-B",
      "valuationPrice": "52.5",
      "priceCurrency": "USD",
      "valuationAt": "2026-04-15T20:00:00Z",
      "observedAt": "2026-04-15T20:00:00Z",
      "sourceAt": "2026-04-15T20:01:00Z",
      "availableAt": "2026-04-16T00:00:00Z",
      "sourceId": "SYNTH-SUCCESSOR-PRICE",
      "fxToBase": {
        "fromCurrency": "USD",
        "toCurrency": "USD",
        "rate": "1",
        "rateAt": "2026-04-15T20:00:00Z",
        "observedAt": "2026-04-15T20:00:00Z",
        "sourceAt": "2026-04-15T20:01:00Z",
        "availableAt": "2026-04-16T00:00:00Z",
        "sourceId": "SYNTH-FX-IDENTITY"
      }
    }
  ],
  "deductions": [
    {
      "kind": "fee",
      "amountPerOldShare": "0.2",
      "currency": "USD",
      "effectiveAt": "2026-04-15T13:30:00Z",
      "observedAt": "2026-04-15T21:00:00Z",
      "sourceAt": "2026-04-15T21:01:00Z",
      "availableAt": "2026-04-16T00:00:00Z",
      "sourceId": "SYNTH-FEES",
      "fxToBase": {
        "fromCurrency": "USD",
        "toCurrency": "USD",
        "rate": "1",
        "rateAt": "2026-04-15T20:00:00Z",
        "observedAt": "2026-04-15T20:00:00Z",
        "sourceAt": "2026-04-15T20:01:00Z",
        "availableAt": "2026-04-16T00:00:00Z",
        "sourceId": "SYNTH-FX-IDENTITY"
      }
    },
    {
      "kind": "withholding",
      "amountPerOldShare": "0.3",
      "currency": "USD",
      "effectiveAt": "2026-04-15T13:30:00Z",
      "observedAt": "2026-04-15T21:00:00Z",
      "sourceAt": "2026-04-15T21:01:00Z",
      "availableAt": "2026-04-16T00:00:00Z",
      "sourceId": "SYNTH-WITHHOLDING",
      "fxToBase": {
        "fromCurrency": "USD",
        "toCurrency": "USD",
        "rate": "1",
        "rateAt": "2026-04-15T20:00:00Z",
        "observedAt": "2026-04-15T20:00:00Z",
        "sourceAt": "2026-04-15T20:01:00Z",
        "availableAt": "2026-04-16T00:00:00Z",
        "sourceId": "SYNTH-FX-IDENTITY"
      }
    }
  ],
  "researchImputation": null
}

Call

calculate(input)

Returns

object with 3 fields: observed, researchEstimate, diagnostics

{
  "observed": {
    "status": "complete",
    "lastRegularReturn": "0.041666666667",
    "terminalGrossValuePerOldShare": "53.000000000000",
    "deductionsPerOldShare": "0.500000000000",
    "terminalNetValuePerOldShare": "52.500000000000",
    "terminalReturn": "0.050000000000",
    "linkedReturn": "0.093750000000",
    "proceedsBasis": "complete_observed",
    "knownObservedGrossValuePerOldShare": null,
    "knownObservedDeductionsPerOldShare": null,
    "knownObservedNetValuePerOldShare": null
  },
  "researchEstimate": null,
  "diagnostics": {
    "eventId": "SYNTH-EVENT-001",
    "eventReason": "merger",
    "asOf": "2026-04-17T12:00:00Z",
    "effectiveAt": "2026-04-15T13:30:00Z",
    "lastRegularTradingAt": "2026-04-14T20:00:00Z",
    "observedEvidenceAvailable": true,
    "missingEvidence": [],
    "withheldEvidence": [],
    "knownProceedsEvidence": [
      "deductions[0]",
      "deductions[0].fxToBase",
      "deductions[1]",
      "deductions[1].fxToBase",
      "proceeds[0]",
      "proceeds[0].fxToBase"
    ],
    "baseCurrency": "USD"
  }
}

Diagrams

Delisting Return Reconstruction — article hero
Delisting Return Reconstruction — failure guard
Delisting Return Reconstruction — worked example

Calculation flow

Evidence-gated calculation flow
flowchart TD
    A["Price and event records"] --> B["Validate decimals, identity, currency, and clocks"]
    B --> C{"Available at as-of time?"}
    C -->|No| D["Not yet available; returns null"]
    C -->|Yes| E{"Event effective?"}
    E -->|No| F["Event not effective; terminal return null"]
    E -->|Yes| G["Value known cash, stock, FX, fees, and withholding"]
    G --> H{"Proceeds evidence state"}
    H -->|Complete| I["Observed terminal and linked returns"]
    H -->|Explicit zero| I
    H -->|Partial| J["Known subtotal; returns null"]
    H -->|Unknown| K["Missing proceeds; returns null"]
    L["Documented research policy"] --> M["Research estimate branch"]
    M -. "Never fills observed fields" .-> I
Point-in-time evidence lifecycle
stateDiagram-v2
    [*] --> EventUnavailable
    EventUnavailable --> EventKnown: event available
    EventKnown --> EventEffective: effective time reached
    EventEffective --> ProceedsUnknown: no terminal evidence
    EventEffective --> ProceedsPartial: some observed legs
    EventEffective --> ProceedsComplete: complete observed legs
    EventEffective --> ZeroConfirmed: explicit zero evidence
    ProceedsUnknown --> ProceedsPartial: first observed leg
    ProceedsPartial --> ProceedsComplete: completeness established
    ProceedsUnknown --> ResearchEstimate: documented policy
    ProceedsPartial --> ResearchEstimate: documented policy
    ResearchEstimate --> ResearchEstimate: later estimates remain labeled
    ResearchEstimate --> ProceedsComplete: observed branch completes separately
    ProceedsComplete --> [*]
    ZeroConfirmed --> [*]

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

  • R1 - CRSPAccess US Stock and Index Databases Data Descriptions Guide
  • R2 - CRSP US Stock and Indexes Databases Data Descriptions Guide
  • R3 - Nasdaq Daily List Product Description
  • R4 - SEC Exchange Delistings
  • R5 - SEC Exchange Act Forms, Section 111.01
  • Evidence boundary for historical examples