fintech-algorithms

Stale-Quote Detector

Install and import

bash
npm install fintech-algorithms
ts
import { detectStaleQuotes } from "fintech-algorithms/market-data-engineering/cleaning-and-validation/stale-quote-detector";

Signature

detectStaleQuotes(events, config)

Separates the several distinct ways a quote can be stale: old at the source, delayed in transport, unchanged for too long, or arriving after a missed heartbeat. They have different causes and different remedies, so they are reported separately.

Parameters

NameTypeNotes
eventsQuoteEvent[]Quote and heartbeat events with both source_event_ts and observed_ts, which is what makes source age and transport age separable.
config{ max_source_event_age_ms: number; max_transport_age_ms: number; unchanged_threshold_ms: number; heartbeat_timeout_ms: number }One budget per failure mode: how old the venue's own timestamp may be, how long transport may take, how long a quote may sit unchanged, and how long a heartbeat may be missing.

Returns

Verdict[] · length same-as-input

One verdict per event naming which budget was breached. Returns one verdict per input row rather than throwing, so a single bad record cannot abort the batch — and cannot pass unnoticed either.

Errors

  • When any threshold is negative — throws

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

events
[
  {
    "event_id": "E01",
    "kind": "quote",
    "source_event_ts": "2026-07-22T09:30:00.000Z",
    "observed_ts": "2026-07-22T09:30:00.100Z",
    "bid": 100,
    "ask": 100.02,
    "clock_sync_ok": true,
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true
  },
  {
    "event_id": "E02",
    "kind": "heartbeat",
    "observed_ts": "2026-07-22T09:30:00.900Z",
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true
  },
  {
    "event_id": "E03",
    "kind": "check",
    "observed_ts": "2026-07-22T09:30:01.200Z",
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true
  }
]

Showing 3 of 12 elements.

config
{
  "max_source_event_age_ms": 1200,
  "max_transport_age_ms": 250,
  "unchanged_threshold_ms": 3000,
  "heartbeat_timeout_ms": 1200
}

Call

detectStaleQuotes(events, config)

Returns

array of 12 objects

[
  {
    "event_id": "E01",
    "kind": "quote",
    "source_event_ts": "2026-07-22T09:30:00.000Z",
    "observed_ts": "2026-07-22T09:30:00.100Z",
    "bid": 100,
    "ask": 100.02,
    "clock_sync_ok": true,
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true,
    "source_event_age_ms": 100,
    "last_transport_age_ms": 100,
    "unchanged_duration_ms": 0,
    "heartbeat_age_ms": 0
  },
  {
    "event_id": "E02",
    "kind": "heartbeat",
    "observed_ts": "2026-07-22T09:30:00.900Z",
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true,
    "source_event_age_ms": 900,
    "last_transport_age_ms": 100,
    "unchanged_duration_ms": 800,
    "heartbeat_age_ms": 0,
    "source_event_age_exceeded": false,
    "transport_late": false,
    "unchanged_threshold_reached": false,
    "heartbeat_lost": false
  },
  {
    "event_id": "E03",
    "kind": "check",
    "observed_ts": "2026-07-22T09:30:01.200Z",
    "session_id": "S1",
    "session_state": "ACTIVE",
    "activity_expected": true,
    "source_event_age_ms": 1200,
    "last_transport_age_ms": 100,
    "unchanged_duration_ms": 1100,
    "heartbeat_age_ms": 300,
    "source_event_age_exceeded": false,
    "transport_late": false,
    "unchanged_threshold_reached": false,
    "heartbeat_lost": false
  }
]

Showing 3 of 12 elements.

Diagrams

Stale-Quote Detector — article hero
Stale-Quote Detector — stale timeline

Calculation flow

Validation-before-mutation flow
flowchart TD
    A["Quote, heartbeat, or check"] --> B{"Schema and receiver order valid?"}
    B -->|No| R["Reject and preserve state"]
    B -->|Yes| C{"Quote event?"}
    C -->|Yes| D{"Clock sync verified, age nonnegative, source order valid?"}
    D -->|No| R
    D -->|Yes| E["Apply session reset if ID changed"]
    C -->|No| E
    E --> F["Quote or heartbeat updates last-message receipt"]
    F --> G["Only changed quote updates last-change receipt"]
    G --> H["Compute source, transport, unchanged, and heartbeat ages"]
    H --> I["Apply active-session business policy"]

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