Crossed/Locked Market Detector
Install and import
npm install fintech-algorithmsimport { classifyMarkets } from "fintech-algorithms/market-data-engineering/cleaning-and-validation/crossed-locked-market-detector";Signature
classifyMarkets(quotes, options)Classifies a quote as normal, locked (bid equals ask) or crossed (bid above ask). Crossed markets are usually a stale or misordered feed rather than a real arbitrage, which is exactly why they must be caught before anything downstream trusts the spread.
Parameters
| Name | Type | Notes |
|---|---|---|
quotes | Quote[] | Quotes carrying instrument, market_scope, feed, event_time, receive_time, sequence, bid and ask. |
options | { tolerance_ticks?: number; relative_tolerance_ppm?: number } | Absolute tolerance in ticks and relative tolerance in parts per million, so a one-tick rounding artefact is not reported as a crossed book. optional |
Returns
Verdict[] · length same-as-input
One classification per quote with the margin by which it locked or crossed. 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 a tolerance 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
[
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.000Z",
"receive_time": "2026-07-20T13:30:00.003Z",
"sequence": 1001,
"bid_source": "VENUE-A",
"ask_source": "VENUE-B",
"bid": 100,
"ask": 100.02,
"tick_size": 0.01
},
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.100Z",
"receive_time": "2026-07-20T13:30:00.104Z",
"sequence": 1002,
"bid_source": "VENUE-A",
"ask_source": "VENUE-B",
"bid": 100.01,
"ask": 100.01,
"tick_size": 0.01
},
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.200Z",
"receive_time": "2026-07-20T13:30:00.207Z",
"sequence": 1003,
"bid_source": "VENUE-A",
"ask_source": "VENUE-C",
"bid": 100.03,
"ask": 100.02,
"tick_size": 0.01
}
]Showing 3 of 8 elements.
{
"tolerance_ticks": 0,
"relative_tolerance_ppm": 0
}Call
classifyMarkets(quotes, options)Returns
array of 8 objects
[
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.000Z",
"receive_time": "2026-07-20T13:30:00.003Z",
"sequence": 1001,
"bid_source": "VENUE-A",
"ask_source": "VENUE-B",
"bid": 100,
"ask": 100.02,
"tick_size": 0.01,
"index": 0,
"spread": 0.01999999999999602,
"spread_ticks": 1.999999999999602
},
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.100Z",
"receive_time": "2026-07-20T13:30:00.104Z",
"sequence": 1002,
"bid_source": "VENUE-A",
"ask_source": "VENUE-B",
"bid": 100.01,
"ask": 100.01,
"tick_size": 0.01,
"index": 1,
"spread": 0,
"spread_ticks": 0
},
{
"instrument": "SYNTH",
"market_scope": "SYNTHETIC_CONSOLIDATED_TOP",
"feed": "SYNTH-SIP",
"event_time": "2026-07-20T13:30:00.200Z",
"receive_time": "2026-07-20T13:30:00.207Z",
"sequence": 1003,
"bid_source": "VENUE-A",
"ask_source": "VENUE-C",
"bid": 100.03,
"ask": 100.02,
"tick_size": 0.01,
"index": 2,
"spread": -0.010000000000005116,
"spread_ticks": -1.0000000000005116
}
]Showing 3 of 8 elements.
Diagrams
Calculation flow
Geometry-to-review flow
flowchart LR
A["Snapshot plus scope, feed, and clocks"] --> B{"Context and values valid?"}
B -->|No| C["Retain INVALID plus reason"]
B -->|Yes| D["Compute spread, ticks, and explicit boundary"]
D --> E{"Compare spread with boundary"}
E -->|Above| F["NORMAL observation"]
E -->|Within| G["LOCKED observation"]
E -->|Below| H["CROSSED observation"]
F --> I["Attach lineage and warnings"]
G --> I
H --> I
I --> J["Separate review or policy layer"]
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.
References
- SEC-605-FAQ - Frequently Asked Questions: Rule 605 of Regulation NMS — U.S. Securities and Exchange Commission, Division of Trading and Markets staff
- SEC-610-611-FAQ - Responses Concerning Rules 610 and 611 — U.S. Securities and Exchange Commission, Division of Trading and Markets staff
- SEC-2026-PROPOSAL - Trade-Through Rule and Locked and Crossed Markets Provisions — U.S. Securities and Exchange Commission
- SEC-TICK-SIZE-RELIEF - 2025 exemptive order and compliance timing — U.S. Securities and Exchange Commission
- PY-FLOAT - Python floating-point information — Python Software Foundation
- ECMASCRIPT-NUMBER - Number.EPSILON — ECMA International
- Evidence classification and historical-example decision