fintech-algorithms

Forward Split Adjustment

carry later observations onto an earlier share basis

Install and import

bash
npm install fintech-algorithms
ts
import { calculate } from "fintech-algorithms/corporate-actions-and-security-master-data/adjustment-factors/forward-split-adjustment";

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
{
  "targetBasisAt": "2020-08-30T23:59:59Z",
  "knowledgeAt": "2020-09-02T00:00:00Z",
  "roundDecimalPlaces": 6,
  "observations": [
    {
      "timestamp": "2020-08-28T20:00:00Z",
      "price": 120,
      "volume": 1000,
      "sharesOutstanding": 1000000
    },
    {
      "timestamp": "2020-08-31T00:00:00Z",
      "price": 30,
      "volume": 4000,
      "sharesOutstanding": 4000000
    },
    {
      "timestamp": "2020-09-01T00:00:00Z",
      "price": 31,
      "volume": 3600,
      "sharesOutstanding": 4000000
    }
  ],
  "eventRevisions": [
    {
      "eventId": "SYNTH-SPLIT-2020",
      "revisionId": "SYNTH-SPLIT-2020-R1",
      "supersedesRevisionId": null,
      "publishedAt": "2020-07-30T20:30:00Z",
      "effectiveAt": "2020-08-31T00:00:00Z",
      "newShares": 4,
      "oldShares": 1,
      "status": "active",
      "sourceId": "synthetic-fixture"
    }
  ]
}

Call

calculate(data)

Returns

object with 4 fields: basis, selectedRevisions, observations, warnings

{
  "basis": {
    "targetBasisAt": "2020-08-30T23:59:59Z",
    "knowledgeAt": "2020-09-02T00:00:00Z",
    "ratioConvention": "new_shares_per_old_share",
    "boundaryRule": "targetBasisAt < effectiveAt <= observation.timestamp",
    "priceDirection": "multiply_post_boundary_values_to_earlier_basis",
    "quantityDirection": "divide_post_boundary_share_quantities_to_earlier_basis",
    "roundDecimalPlaces": 6
  },
  "selectedRevisions": [
    {
      "eventId": "SYNTH-SPLIT-2020",
      "revisionId": "SYNTH-SPLIT-2020-R1",
      "supersedesRevisionId": null,
      "publishedAt": "2020-07-30T20:30:00Z",
      "effectiveAt": "2020-08-31T00:00:00Z",
      "newShares": 4,
      "oldShares": 1,
      "status": "active",
      "sourceId": "synthetic-fixture"
    }
  ],
  "observations": [
    {
      "timestamp": "2020-08-28T20:00:00Z",
      "rawPrice": 120,
      "adjustedPrice": 120,
      "rawVolume": 1000,
      "adjustedVolume": 1000,
      "rawSharesOutstanding": 1000000,
      "adjustedSharesOutstanding": 1000000,
      "cumulativePriceFactor": 1,
      "cumulativeQuantityFactor": 1,
      "appliedEventIds": []
    },
    {
      "timestamp": "2020-08-31T00:00:00Z",
      "rawPrice": 30,
      "adjustedPrice": 120,
      "rawVolume": 4000,
      "adjustedVolume": 1000,
      "rawSharesOutstanding": 4000000,
      "adjustedSharesOutstanding": 1000000,
      "cumulativePriceFactor": 4,
      "cumulativeQuantityFactor": 0.25,
      "appliedEventIds": ["SYNTH-SPLIT-2020"]
    },
    {
      "timestamp": "2020-09-01T00:00:00Z",
      "rawPrice": 31,
      "adjustedPrice": 124,
      "rawVolume": 3600,
      "adjustedVolume": 900,
      "rawSharesOutstanding": 4000000,
      "adjustedSharesOutstanding": 1000000,
      "cumulativePriceFactor": 4,
      "cumulativeQuantityFactor": 0.25,
      "appliedEventIds": ["SYNTH-SPLIT-2020"]
    }
  ],
  "warnings": ["rounded outputs may not reverse exactly"]
}

Diagrams

Forward Split Adjustment — article hero
Forward Split Adjustment — failure guard
Forward Split Adjustment — worked example

Calculation flow

Forward Split Adjustment calculation flow
flowchart LR
    A["Raw observation and event revisions"] --> B["Validate UTC, ratio, order, lineage"]
    B --> C["Latest revision published by knowledgeAt"]
    C --> D{"Selected status active?"}
    D -->|No| E["Keep cancellation lineage; factor 1"]
    D -->|Yes| F{"targetBasisAt < effectiveAt <= observation time?"}
    F -->|No| G["Factor 1"]
    F -->|Yes| H["Compound newShares / oldShares"]
    E --> I["Audited raw and adjusted output"]
    G --> I
    H --> I
Corporate-action revision lifecycle
stateDiagram-v2
    [*] --> Unavailable
    Unavailable --> Active: first revision published
    Active --> Active: corrected active revision
    Active --> Cancelled: cancellation published
    Cancelled --> Active: later reinstatement revision
    Active --> Eligible: effective boundary reached
    Eligible --> Applied: observation is after anchor
    Applied --> Audited
    Cancelled --> Audited

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

  • Apple reports third quarter results
  • Apple Form 8-K filing detail, accession 0000320193-20-000060
  • Apple Form 8-K, accession 0001193125-20-213158
  • Apple 2020 Form 10-K, accession 0000320193-20-000096
  • Apple dividend and split history
  • Evidence classification