How to produce an AMLA RTS effectiveness pack¶
When you need this: You are an EU-supervised obliged entity (a credit institution, payment institution, EMI, or VASP) and the EU AML Authority (AMLA) wants to see, for one engine run, that your transaction-monitoring program is effective — the alert→case→STR funnel — and that your detection rules evidence the three AMLR (Regulation (EU) 2024/1624) effectiveness obligations: CDD (Art. 28(1), the CDD-information RTS), ongoing monitoring of the business relationship (Art. 26; CDD measure Art. 20(1)(f)), and targeted-financial-sanctions screening (Art. 20(1)(d)). The offline
aml amla-effectiveness-reportcommand (#528) rolls all of that into one frozen, deterministic artifact for your Model Risk Committee (MRC) / supervisory pack.Prereqs: A spec whose EU rules carry the AMLA RTS citations in their
regulation_refs(the bundledexamples/eu_bank/aml.yamlis the demonstrator). One completedaml runso the run dir hascases/,decisions.jsonl, andmanifest.json.metrics/amla_effectiveness.py::build_amla_effectiveness_reportis the canonical, pure builder.Time: ~5 min.
The pack is offline and advisory — it is a post-run report like aml model-inventory, NEVER in the engine run path. It derives every number from the run's own artifacts: the funnel + per-rule counts come straight from metrics.outcomes.compute_outcomes (so the AMLA pack and the existing outcomes pack never diverge), and the RTS coverage comes from each rule's regulation_refs. Nothing is fabricated — the run records STR filing (escalated_to_str) but not regulator acceptance, so str_acceptance is reported as not_tracked rather than invented. It is deterministic: generated_at is the run manifest's as_of, not a wall-clock read, so the same run yields byte-identical output.
Steps¶
1 · Annotate your EU rules with AMLA RTS citations¶
Each rule whose logic reflects an RTS effectiveness standard carries the matching citation in regulation_refs (these are the real AMLR / AMLD6 references — AMLR = Regulation (EU) 2024/1624; AMLD6 = Directive (EU) 2024/1640):
rules:
- id: structuring_cash
regulation_refs:
- citation: "AMLR Art. 28(1)" # CDD RTS
description: "CDD RTS — customer-due-diligence measures the rule reflects."
- id: pep_screening
regulation_refs:
- citation: "AMLR Art. 26" # ongoing monitoring of the business relationship
description: "Ongoing monitoring of the PEP business relationship (CDD measure Art. 20(1)(f))."
- id: sanctions_screening
regulation_refs:
- citation: "AMLR Art. 20(1)(d)" # targeted-financial-sanctions screening
description: "Targeted-financial-sanctions screening obligation (CDD measure Art. 20(1)(d))."
The report maps a rule onto an AMLR article when one of the rule's citations contains the article's reference (AMLR Art. 28(1), AMLR Art. 26, AMLR Art. 20(1)(d)). A rule with no AMLA citation simply isn't counted toward coverage — it still appears in the per-rule funnel.
2 · Validate + run¶
3 · Produce the pack¶
aml amla-effectiveness-report examples/eu_bank/aml.yaml .artifacts/run-<TS> \
--markdown amla_effectiveness.md
The command writes amla_effectiveness_report.json into the run dir (atomic write — a temp file in the same directory then os.replace, so an I/O error never leaves a partial artifact), an optional --markdown MRC table, and prints a summary:
AMLA RTS effectiveness eu_bank_aml
alerts: 89 cases: 89 str_filed: 83
alert→str: 93.26% str_acceptance: not_tracked
RTS coverage: 3/3 articles
Use --out <path> to write the JSON somewhere other than the run dir.
4 · Read the report¶
{
"spec_program": "eu_bank_aml",
"generated_at": "2026-06-09T18:31:51.015515",
"total_alerts": 89,
"total_str_filed": 83,
"alert_to_str_pct": 93.26,
"str_acceptance_rate_pct": null,
"str_acceptance_status": "not_tracked",
"rts_coverage": [
{
"key": "cdd",
"title": "Customer due diligence",
"citation": "AMLR Art. 28(1)",
"instrument": "AMLR",
"covering_rule_ids": ["high_risk_jurisdiction", "structuring_cash"],
"status": "covered"
}
],
"n_rts_covered": 3
}
| Field | Meaning |
|---|---|
total_alerts / total_cases / total_str_filed |
The AMLA effectiveness funnel, derived from cases/ + decisions.jsonl. |
alert_to_str_pct |
Overall conversion — the canonical AMLA effectiveness ratio. |
str_acceptance_status |
not_tracked — the run records STR filing, not regulator acceptance. Wire your FIU-feedback loop separately if you need this. |
rts_coverage[].status |
covered (≥1 rule cites the article) or gap (none). |
rules[].precision |
Per-rule precision — null unless you supply case labels (precision needs ground-truth labels the run doesn't carry). recall is always null; it needs the ground-truth positive population, which the alert set alone can't tell you. |
Note (2026-07-10): AMLA's statutory deadline to submit the three final RTS to the European Commission passed on 2026-07-10. This edition treats the submission as pending confirmation, not asserted — see the "Deadline passed" entry in
docs/research/2026-07-regulator-pulse.mdfor the Commission's 3-month endorsement clock (2026-07-10 → 2026-10-10). Once endorsed, arts_coverage[].status: gapstops meaning "consultation input still open" and starts meaning an implementation backlog against a locked-in binding standard — worth re-runningaml amla-effectiveness-reportagainst a fresh spec once the endorsement lands.
5 · Read it on the dashboard instead¶
- Framework Alignment (page 8) — for an EU program, an "AMLA RTS coverage" tab maps the spec's rule citations onto the three RTS articles with a ✓ (mapped) / ∼ (partial — cited but no evidence trail) / ✗ (gap) indicator and the covering rule ids. The mapping logic (
dashboard/frameworks.py::build_amla_rts_alignment) is streamlit-free and unit-testable.
Verify it worked¶
Three checks:
- The JSON exists in the run dir and
n_rts_coveredmatches the number of RTS articles your rules cite (3/3for the bundledeu_bankspec). - STR acceptance is honest —
str_acceptance_statusisnot_tracked, not a fabricated rate. - Determinism — re-run the command against the same run dir; the JSON is byte-identical (
generated_atis the manifestas_of, not a clock read).
Common problems¶
| Symptom | Cause | Fix |
|---|---|---|
RTS coverage: 0/3 articles |
No rule carries an AMLR Art. 28(1) / AMLR Art. 26 / AMLR Art. 20(1)(d) citation |
Add the citation to the relevant rule's regulation_refs; re-validate |
No manifest.json in <run-dir> |
You pointed at a directory that isn't an engine run dir | Pass a real .artifacts/run-<TS> dir (or run aml run first) |
precision is null for every rule |
No labels supplied — precision needs ground-truth true/false-positive labels | The run doesn't carry labels; supply them from your QA system if you need precision (the funnel + coverage are label-free) |
| A citation won't resolve to a URL | The AMLA citation isn't in CITATION_URL_MAP |
The three RTS citations ship resolvable; for a novel one add a url: to the regulation_refs entry |
Next steps¶
- How to monitor model risk + per-rule drift — the sibling post-run report (
aml model-inventory/model_risk_report.json); the AMLA pack reuses the same offline, deterministic, manifest-pinned shape. jurisdictions.md— the EU section, where the AMLA RTS effectiveness pack is the supervisory deliverable.