When to Use
Use this skill when building or reviewing market-abuse surveillance for EU/EEA trading, where MAR Article 16(1)-(2) obliges market operators and investment firms operating a trading venue, and persons professionally arranging or executing transactions (PPAETs), to maintain effective arrangements to prevent and detect market abuse and to notify the National Competent Authority (NCA) without delay once reasonable suspicion is formed.
It fits post-trade / near-real-time batch screening of an order and trade log for three of the indicators in Commission Delegated Regulation (EU) 2016/522, Annex II and MAR Annex I: transactions with no change in beneficial ownership, layering/spoofing, and quote stuffing.
When NOT to Use
- Not for the UK. Since 1 January 2021 the UK applies its own assimilated MAR and assimilated Delegated Regulation (EU) 2016/957, supervised by the FCA. The FCA is not an NCA under EU MAR; a UK STOR goes to the FCA under UK MAR, through FCA channels.
- Not for crypto-assets. MAR covers financial instruments admitted to trading (or for which admission has been requested) on an EU regulated market, MTF or OTF. The analogous crypto obligation is Regulation (EU) 2023/1114 (MiCA) Article 92.
- Not as a filing client. There is no EU-wide STOR endpoint. Each NCA prescribes its own channel with prior enrolment — BaFin via the MVP portal's STOR procedure, the AMF via the ROSA extranet. This skill produces a draft record only.
- Not as a suspicion determination. Delegated Regulation (EU) 2016/957 requires an appropriate level of human analysis. An alert is an input to that analysis, never a filing decision.
- Not a real-time gate. It scores finite batches after the fact. For pre-trade self-match prevention see
wash-trade-and-spoofing-self-detection.
Prerequisites
- Order and trade event log with
event_id,cl_ord_id,isin,symbol,side,order_qty,price,event_type(NEW/MODIFY/CANCEL/FILL/REJECT),timestamp_ns,buyer_account_id,seller_account_id, and — where known —account_idfor the order's owning account. timestamp_nsin nanoseconds since the Unix epoch, UTC. Sub-100ms lifespan logic is only meaningful on synchronised business clocks (Commission Delegated Regulation (EU) 2017/574, RTS 25 for MiFID II venues and their members).- Batches that contain the whole order lifecycle: a
CANCELwhoseNEWfell outside the batch cannot be timed and is excluded from the ratio (reported asunmatched_cancels). - An account → beneficial-owner mapping, if sub-accounts are used. Without it, self-execution across two account ids of one owner is invisible.
- Calibrated detection parameters. The defaults (
0.90cancel ratio,100mslifespan,500 msgs/sec) are library heuristics, not regulatory thresholds — MAR prescribes no numbers.
Workflow
- Validate the batch before scoring it.
audit_events_for_mar_patternsrejects unknown event types or sides, non-positive quantities, non-finite or negative prices, negative timestamps, events identifying no instrument, and duplicateevent_ids. A duplicated event would inflate both the cancel ratio and the message rate, so it is an error, not a warning. Events may arrive unsorted — they are ordered internally, so the result never depends on input ordering. - Resolve beneficial ownership first. Every detector groups by beneficial owner, not by raw account id. Pass
beneficial_owner_mapso sub-accounts collapse onto the owning entity. - Wash trading — test ownership, not string equality. A
FILLwhose buyer and seller resolve to the same beneficial owner is flaggedWASH_TRADE_ALERT(CRITICAL), mapping to the MAR Annex I Section A indicator "no change in beneficial ownership". - Spoofing/layering — ratio and lifespan and side. Per (owner, instrument): join
NEWtoCANCELoncl_ord_id, keep cancels whose lifespan is at or belowspoof_max_lifespan_ms, and comparefast_cancels / new_ordersagainst the threshold once at leastmin_orders_for_cancel_ratioorders exist. If an opposite-sideFILLlands inside the pattern window the alert isHIGHandopposite_side_fill_observed=True— that is the Annex II layering/spoofing shape. Without it the alert isMEDIUM: a non-bona-fide-order indicator, not confirmed spoofing. Setrequire_opposite_side_fill=Trueto suppress the weaker case entirely. - Quote stuffing — sliding window, not calendar buckets. Peak rate is the largest count of
NEW/MODIFY/CANCELmessages inside any one-second window per (owner, instrument). Fills are excluded — an execution is not a quote. Fixed second-buckets would split a burst that straddles a boundary and miss it. - Assemble the STOR draft, then hand it to a human.
stor_filing_payloadcarries the legal basis, the template source (Delegated Regulation (EU) 2016/957, Annex), the instruments, the beneficial owners, the timestamp window, the exact detection parameters used,status = DRAFT_PENDING_HUMAN_REVIEW, and the retention obligation. Transposing it into the NCA's template and forming reasonable suspicion is the compliance officer's job. - Retain the analysis for five years. Delegated Regulation (EU) 2016/957 requires the analysis of examined orders and transactions to be kept for five years — including cases where no STOR was submitted, and the reasons why. Persist
detection_parametersalongside the alerts so a decision can be reconstructed years later.
Full procedure: see
references/workflows.md. Standards and calibration guidance: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
- Treating an alert as a STOR. Delegated Regulation (EU) 2016/957 requires human analysis. Auto-filing every alert both floods the NCA and skips the judgement the RTS asks for; the engine therefore never emits a "ready to submit" state.
- Batching STORs to file them together. MAR Article 16 requires notification without delay once reasonable suspicion is formed. Holding a report back to accumulate cases is expressly contrary to that.
- Computing a cancel ratio across the whole batch. One spoofer inside honest flow is diluted to nothing (9 fast cancels out of 50 batch-wide orders looks like 18%, while that participant's own ratio is 90%). Group by beneficial owner and instrument, always.
- Using the cancel ratio without the order lifespan. A liquidity provider that quotes continuously and re-prices every few hundred milliseconds can exceed a 90% cancel ratio legitimately. What distinguishes layering is how briefly the orders rested, and whether the other side traded.
- String-matching account ids for wash trades. MAR Annex I Section A asks whether there was a change in beneficial ownership. Two sub-accounts of one entity self-crossing is a wash trade whose account ids differ; conversely, a broker's internal give-up representation can put one account on both sides of a non-abusive booking — check the mapping before escalating.
- Setting the quote-stuffing threshold below normal market-making rates. A designated market maker meeting quoting obligations in a liquid name will breach a naive rate limit continuously. Calibrate per venue and instrument liquidity tier and record the rationale.
- Assuming timestamps are comparable. Fast-cancel logic at 100ms resolution is meaningless across desynchronised clocks; on MiFID II venues the applicable standard is RTS 25 (Delegated Regulation (EU) 2017/574).
- Citing the wrong RTS. The STOR template lives in the Annex to Delegated Regulation (EU) 2016/957. RTS 25 is MiFID II business-clock accuracy — relevant to timestamps, not to STOR content.
Verification
- Instantiate
EuMarSurveillanceEngine. Submit aFILLwhose buyer and seller resolve to the same beneficial owner — expect oneWASH_TRADE_ALERTatCRITICALand astor_filing_payloadwhosestatusisDRAFT_PENDING_HUMAN_REVIEW. - Submit 10
NEWorders with 9 cancelled after 5ms — expect oneSPOOFING_ALERTatMEDIUM, attributed to the owning entity and the traded instrument. Re-run with the same cancels at 500ms — expect no alert. Add an opposite-sideFILLinside the window — expectHIGHwithopposite_side_fill_observed=True. - Submit 150 order-book messages 1ms apart against
quote_rate_threshold_per_sec=100— expect oneQUOTE_STUFFING_ALERT; the same 150 messages 100ms apart must not alert. - Run
python -m unittest discover -s skills/eu-market-abuse-regulation-mar-surveillance/scripts.