fintech-algorithms

Duplicate-Trade Resolver

Install and import

bash
npm install fintech-algorithms
ts
import { resolveTrades } from "fintech-algorithms/market-data-engineering/cleaning-and-validation/duplicate-trade-resolver";

Signature

resolveTrades(input)

Reconciles a stream of new, corrected and cancelled trade messages into one authoritative set. Replays of the same message must be idempotent, and a cancellation must survive a later redelivery of the trade it cancelled.

Parameters

NameTypeNotes
inputTradeMessage[]Messages carrying source_id, session_id, instrument_id, event_id, trade_id and an action of new, correct or cancel.

Returns

{ policy_version, active, states, tombstones, pending, versions, raw_history, audit }

The resolved set (active), cancelled trades kept as tombstones so a redelivery cannot resurrect them, messages awaiting a predecessor (pending), and a full audit of how each decision was reached.

Errors

  • When a message is missing an identifier required to place it — recorded in the audit rather than thrown

Complexity: time O(n), space O(n).

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
[
  {
    "source_id": "SIP-X",
    "session_id": "2026-07-20",
    "instrument_id": "XYZ",
    "event_id": "E2",
    "trade_id": "T1",
    "action": "CORRECT",
    "ref_event_id": "E1",
    "receive_ts": "2026-07-20T13:30:00.003Z",
    "sequence": 2,
    "price": 101,
    "size": 12
  },
  {
    "source_id": "SIP-X",
    "session_id": "2026-07-20",
    "instrument_id": "XYZ",
    "event_id": "E1",
    "trade_id": "T1",
    "action": "NEW",
    "receive_ts": "2026-07-20T13:30:00.004Z",
    "sequence": 1,
    "price": 100,
    "size": 10
  },
  {
    "source_id": "SIP-X",
    "session_id": "2026-07-20",
    "instrument_id": "XYZ",
    "event_id": "E1",
    "trade_id": "T1",
    "action": "NEW",
    "receive_ts": "2026-07-20T13:30:00.004Z",
    "sequence": 1,
    "price": 100,
    "size": 10
  }
]

Showing 3 of 24 elements.

Call

resolveTrades(input)

Returns

object with 8 fields: policy_version, active, states, tombstones, pending, versions, raw_history, audit

{
  "policy_version": "synthetic-lifecycle-v2",
  "active": [
    {
      "trade_key": "SIP-X|2026-07-20|XYZ|T5",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T5",
      "status": "ACTIVE",
      "head_event_id": "E13",
      "head_event_key": "SIP-X|2026-07-20|E13",
      "price": 80.5,
      "size": 32,
      "chain": ["E12", "E13"]
    }
  ],
  "states": {
    "SIP-X|2026-07-20|XYZ|T1": {
      "trade_key": "SIP-X|2026-07-20|XYZ|T1",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T1",
      "status": "CANCELED",
      "head_event_id": "E3",
      "head_event_key": "SIP-X|2026-07-20|E3",
      "price": 101,
      "size": 12,
      "chain": ["E1", "E2", "E3"],
      "tombstone": {
        "action": "CANCEL",
        "event_id": "E3",
        "ref_event_id": "E2"
      }
    },
    "SIP-X|2026-07-20|XYZ|T3": {
      "trade_key": "SIP-X|2026-07-20|XYZ|T3",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T3",
      "status": "ERRORED",
      "head_event_id": "E8",
      "head_event_key": "SIP-X|2026-07-20|E8",
      "price": 75,
      "size": 20,
      "chain": ["E7", "E8"],
      "tombstone": {
        "action": "ERROR",
        "event_id": "E8",
        "ref_event_id": "E7"
      }
    },
    "SIP-X|2026-07-20|XYZ|T4": {
      "trade_key": "SIP-X|2026-07-20|XYZ|T4",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T4",
      "status": "CANCELED",
      "head_event_id": "E18",
      "head_event_key": "SIP-X|2026-07-20|E18",
      "price": 60,
      "size": 8,
      "chain": ["E9", "E18"],
      "tombstone": {
        "action": "CANCEL",
        "event_id": "E18",
        "ref_event_id": "E9"
      }
    },
    "SIP-X|2026-07-20|XYZ|T5": {
      "trade_key": "SIP-X|2026-07-20|XYZ|T5",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T5",
      "status": "ACTIVE",
      "head_event_id": "E13",
      "head_event_key": "SIP-X|2026-07-20|E13",
      "price": 80.5,
      "size": 32,
      "chain": ["E12", "E13"]
    },
    "SIP-X|2026-07-21|XYZ|T1": {
      "trade_key": "SIP-X|2026-07-21|XYZ|T1",
      "source_id": "SIP-X",
      "session_id": "2026-07-21",
      "instrument_id": "XYZ",
      "trade_id": "T1",
      "status": "ERRORED",
      "head_event_id": "E2",
      "head_event_key": "SIP-X|2026-07-21|E2",
      "price": 103,
      "size": 11,
      "chain": ["E1", "E2"],
      "tombstone": {
        "action": "ERROR",
        "event_id": "E2",
        "ref_event_id": "E1"
      }
    }
  },
  "tombstones": [
    {
      "trade_key": "SIP-X|2026-07-20|XYZ|T1",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T1",
      "status": "CANCELED",
      "head_event_id": "E3",
      "head_event_key": "SIP-X|2026-07-20|E3",
      "price": 101,
      "size": 12,
      "chain": ["E1", "E2", "E3"],
      "tombstone": {
        "action": "CANCEL",
        "event_id": "E3",
        "ref_event_id": "E2"
      }
    },
    {
      "trade_key": "SIP-X|2026-07-20|XYZ|T3",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T3",
      "status": "ERRORED",
      "head_event_id": "E8",
      "head_event_key": "SIP-X|2026-07-20|E8",
      "price": 75,
      "size": 20,
      "chain": ["E7", "E8"],
      "tombstone": {
        "action": "ERROR",
        "event_id": "E8",
        "ref_event_id": "E7"
      }
    },
    {
      "trade_key": "SIP-X|2026-07-20|XYZ|T4",
      "source_id": "SIP-X",
      "session_id": "2026-07-20",
      "instrument_id": "XYZ",
      "trade_id": "T4",
      "status": "CANCELED",
      "head_event_id": "E18",
      "head_event_key": "SIP-X|2026-07-20|E18",
      "price": 60,
      "size": 8,
      "chain": ["E9", "E18"],
      "tombstone": {
        "action": "CANCEL",
        "event_id": "E18",
        "ref_event_id": "E9"
      }
    }
  ],
  "pending": [
    {
      "event_key": "SIP-X|2026-07-20|E10",
      "trade_key": "SIP-X|2026-07-20|XYZ|T4",
      "ref_event_id": "MISSING",
      "action": "CORRECT"
    }
  ],
  "versions": [
    {
      "event_key": "SIP-X|2026-07-20|E1",
      "trade_key": "SIP-X|2026-07-20|XYZ|T1",
      "event_id": "E1",
      "action": "NEW",
      "sequence": 1,
      "price": 100,
      "size": 10,
      "decision": "APPLY_NEW",
      "head_before": null,
      "head_after": "E1"
    },
    {
      "event_key": "SIP-X|2026-07-20|E2",
      "trade_key": "SIP-X|2026-07-20|XYZ|T1",
      "event_id": "E2",
      "action": "CORRECT",
      "ref_event_id": "E1",
      "sequence": 2,
      "price": 101,
      "size": 12,
      "decision": "APPLY_CORRECTION",
      "head_before": "E1",
      "head_after": "E2"
    },
    {
      "event_key": "SIP-X|2026-07-20|E3",
      "trade_key": "SIP-X|2026-07-20|XYZ|T1",
      "event_id": "E3",
      "action": "CANCEL",
      "ref_event_id": "E2",
      "sequence": 3,
      "decision": "APPLY_CANCEL",
      "head_before": "E2",
      "head_after": "E3"
    }
  ],
  "raw_history": [
    {
      "arrival_index": 0,
      "event": {
        "source_id": "SIP-X",
        "session_id": "2026-07-20",
        "instrument_id": "XYZ",
        "event_id": "E2",
        "trade_id": "T1",
        "action": "CORRECT",
        "ref_event_id": "E1",
        "receive_ts": "2026-07-20T13:30:00.003Z",
        "sequence": 2,
        "price": 101,
        "size": 12
      },
      "validation_errors": []
    },
    {
      "arrival_index": 1,
      "event": {
        "source_id": "SIP-X",
        "session_id": "2026-07-20",
        "instrument_id": "XYZ",
        "event_id": "E1",
        "trade_id": "T1",
        "action": "NEW",
        "receive_ts": "2026-07-20T13:30:00.004Z",
        "sequence": 1,
        "price": 100,
        "size": 10
      },
      "validation_errors": []
    },
    {
      "arrival_index": 2,
      "event": {
        "source_id": "SIP-X",
        "session_id": "2026-07-20",
        "instrument_id": "XYZ",
        "event_id": "E1",
        "trade_id": "T1",
        "action": "NEW",
        "receive_ts": "2026-07-20T13:30:00.004Z",
        "sequence": 1,
        "price": 100,
        "size": 10
      },
      "validation_errors": []
    }
  ],
  "audit": [
    {
      "arrival_index": 0,
      "event_id": "E2",
      "trade_id": "T1",
      "decision": "APPLY_CORRECTION",
      "reason": "correction references the current active version"
    },
    {
      "arrival_index": 1,
      "event_id": "E1",
      "trade_id": "T1",
      "decision": "APPLY_NEW",
      "reason": "first valid NEW for this scoped trade key"
    },
    {
      "arrival_index": 2,
      "event_id": "E1",
      "trade_id": "T1",
      "decision": "DROP_REPLAY",
      "reason": "byte-equivalent normalized event_id already retained"
    }
  ]
}

Diagrams

Duplicate-Trade Resolver — article hero
Duplicate-Trade Resolver — audit lineage
Duplicate-Trade Resolver — duplicate precedence

Calculation flow

Resolution state and evidence paths
stateDiagram-v2
  [*] --> Absent
  Absent --> Active: valid NEW
  Active --> Active: linked current-head CORRECT
  Active --> Canceled: linked CANCEL
  Active --> Errored: linked ERROR
  Absent --> Pending: missing parent
  Active --> Quarantine: stale or cross-key reference
  Absent --> Quarantine: ID or sequence conflict
  Active --> Active: exact replay or duplicate NEW
  Canceled --> Quarantine: later lifecycle action
  Errored --> Quarantine: later lifecycle action

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