16 min read

When Maya Osei's team at Verdant Bank implemented their first automated sanctions screening system, they quickly discovered a problem that no vendor had mentioned in the sales presentation: the name "Mohammed Al-Hassan."

Chapter 8: Sanctions Screening: Watchlists, False Positives, and Calibration


Opening: The Name That Appears Everywhere

When Maya Osei's team at Verdant Bank implemented their first automated sanctions screening system, they quickly discovered a problem that no vendor had mentioned in the sales presentation: the name "Mohammed Al-Hassan."

There are, conservatively, hundreds of thousands of people worldwide named Mohammed Al-Hassan or close phonetic variants. There are also several individuals named Mohammed Al-Hassan on the OFAC Specially Designated Nationals (SDN) list. Every day, Verdant's screening system generated alerts for customers, counterparties, and transaction parties whose names matched or near-matched the sanctioned individuals.

Most of these alerts were obvious false positives — a retail customer in Birmingham, UK, whose name was a phonetic variant of a sanctioned individual in a different country, with different date of birth, different nationality, different address. But "obvious" to a human analyst and "obvious" to a rules-based matching engine are different things. The matching engine flagged them all.

In her first six months managing the screening program, Maya's team dispositioned approximately 3,400 sanctions alerts. Of those, four warranted escalation to the compliance senior management team. None resulted in a true sanctions match. The false positive rate was 99.9%.

This is the sanctions screening paradox: the regulatory consequences of a missed true positive are catastrophic (civil and criminal penalties, potential criminal prosecution, license loss), so systems are calibrated for maximum sensitivity — which generates a volume of false positives that itself represents a compliance risk (analyst fatigue, missed real matches buried in noise).


8.1 The Sanctions Landscape: Regimes, Lists, and Obligations

Sanctions are government-imposed restrictions on economic activity — prohibiting transactions with designated individuals, entities, or countries. Financial institutions sit at the center of the sanctions enforcement system because nearly all significant financial flows pass through the banking system.

The Major Sanctions Regimes

OFAC (Office of Foreign Assets Control) — United States

OFAC administers the US sanctions program — one of the most far-reaching in the world because of the US dollar's role in global trade and finance. OFAC can impose sanctions unilaterally (based on US foreign policy objectives) or multilaterally (in coordination with UN or allied country sanctions).

Key OFAC lists: - Specially Designated Nationals (SDN) List: Individuals and entities blocked from accessing the US financial system. Assets of SDNs are frozen; US persons (including US banks globally) are generally prohibited from transacting with SDNs. - Consolidated Sanctions List: Combines SDN with other OFAC program lists into a single searchable file - Foreign Sanctions Evaders (FSE): Persons determined to have violated or evaded US sanctions - Sectoral Sanctions Identifications (SSI): Entities subject to targeted restrictions (not full blocking) — relevant particularly for Russian sectoral sanctions

The extraterritorial reach of US sanctions: OFAC sanctions apply to all US persons globally, all entities organized under US law, and transactions conducted in US dollars that clear through US correspondent banks. This means non-US banks that conduct USD-denominated transactions are subject to OFAC compliance requirements through their USD correspondent banking relationships.

HM Treasury / OFSI — United Kingdom

The Office of Financial Sanctions Implementation (OFSI) administers UK financial sanctions. Post-Brexit, the UK operates its own independent sanctions regime, often aligned with but not identical to EU sanctions. Key UK lists: the UK Consolidated List of Financial Sanctions Targets.

EU — European External Action Service and Member State Implementation

EU sanctions are established through EU Regulations, implemented directly in member states without transposition. The EU Consolidated Financial Sanctions List is maintained by the European External Action Service.

UN Security Council Sanctions

UN Security Council Resolutions establish multilateral sanctions regimes implemented through domestic legislation. Major UN sanctions programs: North Korea (DPRK), Al-Qaida, Taliban, ISIL (Da'esh). UN sanctions are the minimum — many countries impose additional unilateral measures beyond the UN baseline.

What Must Be Screened

Sanctions screening obligations apply across multiple dimensions of financial institution activity:

SCREENING SCOPE

Customers ─────────── At onboarding; at regular intervals; on trigger events
                      (new sanctions designations, customer profile changes)

Transaction parties ─ Originators, beneficiaries, counterparties, correspondent
                      banks in wire transfers and other payments

Securities ─────────── CUSIP/ISIN screening against securities-based sanctions
                      (e.g., Russian sectoral sanctions restricting acquisition
                      of debt/equity of designated entities)

Correspondent banks ── Periodic screening of correspondent bank relationships

Beneficial owners ──── Screening of beneficial owners of corporate customers
                      (33% OFAC ownership rule: any entity 50%+ owned by a
                      sanctioned person is itself subject to OFAC sanctions)

The OFAC 50% Rule deserves particular attention: an entity is subject to OFAC sanctions if it is owned 50% or more by a sanctioned person, even if the entity itself is not named on any list. This creates a significant beneficial ownership screening requirement — a bank must screen not just the listed sanctioned entities but also their potential ownership interests in other entities.


8.2 How Sanctions Screening Works: The Technical Architecture

The Screening Data Flow

                 SCREENING TRIGGER
                 (new customer / new transaction / periodic re-screen)
                          │
                          ▼
            ┌─────────────────────────────┐
            │     NAME NORMALIZATION      │
            │  Unicode normalization      │
            │  Transliteration            │
            │  Punctuation removal        │
            │  Diacritic handling         │
            └─────────────────────────────┘
                          │
                          ▼
            ┌─────────────────────────────┐
            │     MATCHING ENGINE         │
            │  Exact match                │
            │  Fuzzy match (algorithms)   │
            │  Phonetic match             │
            │  Alias expansion            │
            └─────────────────────────────┘
                          │
                     ┌────┴────┐
              Match score    No match
              > threshold       │
                  │             ▼
                  ▼         PASS (no alert)
            ALERT GENERATED
                  │
                  ▼
            ┌─────────────────────────────┐
            │     ANALYST REVIEW          │
            │  True match assessment      │
            │  Supporting data comparison │
            │  → Confirm match / Clear    │
            └─────────────────────────────┘
                  │
          ┌───────┴────────┐
     True match        False positive
          │                 │
          ▼                 ▼
    BLOCK / REJECT     CLEAR (document
    File OFAC SDN      rationale)
    report if required

Matching Algorithms

The core technical challenge in sanctions screening is name matching. Sanctioned individuals' names are often: - Transliterated from non-Latin scripts (Arabic, Cyrillic, Chinese, Persian) — creating multiple valid Latin-script representations of the same name - Listed under multiple aliases on the watchlist - Phonetically similar to common legitimate names

Exact matching: Trivially implementable but catches only literal string matches. Misses "Mohammed" vs "Muhammad" vs "Mohamed" — all valid transliterations of the same Arabic name. Used for entity identifiers (tax IDs, company registration numbers) where exact matching is appropriate.

Edit distance (Levenshtein distance): Measures the minimum number of single-character edits (insertions, deletions, substitutions) needed to transform one string into another. "James Brown" to "James Browne" = 1 edit. Good at catching typos; less effective at phonetic variants.

Phonetic algorithms: Soundex, Metaphone, Double Metaphone. Reduce names to phonetic codes representing how they sound rather than how they are spelled. "Smith" and "Smyth" produce the same Soundex code. Effective for English names; less effective for non-English names.

N-gram matching: Breaks names into character sequences (n-grams) and measures overlap. More robust to transpositions and partial matches.

ML-based matching: Learns name similarity from training data — for example, learning that "bin Laden" and "ben Laden" should be treated as high-similarity even though their character-level distance is moderate. ML matching can incorporate language-specific phonetic rules, script-specific transliteration patterns, and contextual features (date of birth, address, nationality) alongside the name.

"""
Sanctions Name Matching: Implementation Examples

Demonstrates multiple matching approaches and their
trade-offs for sanctions screening applications.
"""

from difflib import SequenceMatcher
import re


def normalize_name(name: str) -> str:
    """
    Basic name normalization before matching.
    - Convert to uppercase
    - Remove punctuation and extra whitespace
    - Normalize common prefixes/honorifics
    """
    # Uppercase
    name = name.upper()

    # Remove punctuation
    name = re.sub(r"[^\w\s]", " ", name)

    # Normalize whitespace
    name = " ".join(name.split())

    # Remove common honorifics
    honorifics = {"MR", "MRS", "MS", "DR", "PROF", "REV", "SIR", "LORD"}
    parts = name.split()
    if parts and parts[0] in honorifics:
        name = " ".join(parts[1:])

    return name


def levenshtein_similarity(s1: str, s2: str) -> float:
    """
    Returns similarity score between 0.0 and 1.0 using
    Levenshtein-based sequence matching.
    """
    s1_norm = normalize_name(s1)
    s2_norm = normalize_name(s2)
    return SequenceMatcher(None, s1_norm, s2_norm).ratio()


def soundex(name: str) -> str:
    """
    Simplified Soundex implementation for English names.
    Returns 4-character code: first letter + 3 digits.
    """
    name = normalize_name(name)
    if not name:
        return ""

    # Keep first character
    first = name[0]

    # Mapping table
    mapping = {
        "BFPV": "1", "CGJKQSXYZ": "2", "DT": "3",
        "L": "4", "MN": "5", "R": "6",
        "AEHIOUWY": "0"  # 0 = ignored
    }

    code = [first]
    prev_digit = None

    for char in name[1:]:
        digit = "0"
        for letters, d in mapping.items():
            if char in letters:
                digit = d
                break

        if digit != "0" and digit != prev_digit:
            code.append(digit)
        prev_digit = digit if digit != "0" else prev_digit

        if len(code) == 4:
            break

    # Pad to length 4
    while len(code) < 4:
        code.append("0")

    return "".join(code[:4])


def phonetic_similarity(name1: str, name2: str) -> bool:
    """Returns True if names share the same Soundex code."""
    return soundex(name1) == soundex(name2)


class SanctionsScreener:
    """
    Simple sanctions screening implementation demonstrating
    multi-algorithm matching with configurable threshold.
    """

    def __init__(
        self,
        watchlist: list[dict],
        fuzzy_threshold: float = 0.85,
        use_phonetic: bool = True
    ):
        """
        Args:
            watchlist: List of sanctioned entity records
                       Each record: {"name": str, "aliases": list[str],
                                     "dob": str | None, "nationality": str | None}
            fuzzy_threshold: Minimum similarity score to generate alert
            use_phonetic: Whether to also check phonetic similarity
        """
        self.watchlist = watchlist
        self.fuzzy_threshold = fuzzy_threshold
        self.use_phonetic = use_phonetic

    def screen(
        self,
        query_name: str,
        query_dob: str | None = None,
        query_nationality: str | None = None
    ) -> list[dict]:
        """
        Screen a name against the watchlist.

        Returns list of potential matches with scores.
        Empty list = no match found above threshold.
        """
        matches = []

        for entity in self.watchlist:
            # Build all names to check (primary + aliases)
            names_to_check = [entity["name"]] + entity.get("aliases", [])

            best_score = 0.0
            best_matched_name = ""

            for watchlist_name in names_to_check:
                score = levenshtein_similarity(query_name, watchlist_name)

                if self.use_phonetic and phonetic_similarity(query_name, watchlist_name):
                    # Phonetic match boosts score slightly
                    score = max(score, 0.75)

                if score > best_score:
                    best_score = score
                    best_matched_name = watchlist_name

            if best_score >= self.fuzzy_threshold:
                # Check supporting data to estimate false positive likelihood
                supporting_match_count = 0

                if query_dob and entity.get("dob"):
                    if query_dob == entity["dob"]:
                        supporting_match_count += 2  # Strong indicator
                    elif query_dob[:4] == entity["dob"][:4]:
                        supporting_match_count += 1  # Birth year match

                if query_nationality and entity.get("nationality"):
                    if query_nationality == entity["nationality"]:
                        supporting_match_count += 1

                matches.append({
                    "entity": entity,
                    "matched_name": best_matched_name,
                    "name_score": best_score,
                    "supporting_data_score": supporting_match_count,
                    "alert_priority": "HIGH" if best_score >= 0.95 and supporting_match_count >= 2
                    else "MEDIUM" if best_score >= 0.90 or supporting_match_count >= 1
                    else "LOW"
                })

        return sorted(matches, key=lambda x: (-x["name_score"], -x["supporting_data_score"]))

8.3 The False Positive Problem: Sources and Scale

The false positive rate in sanctions screening is typically far higher than in AML transaction monitoring. While mature AML programs achieve false positive rates of 80–95%, sanctions screening programs routinely operate at false positive rates of 99%+ — that is, fewer than 1 in 100 alerts represents a genuine sanctions match.

Sources of False Positives

Common names: Names that are extremely common in populations with high representation on sanctions lists create systematic false positive problems. Mohammed, Hassan, Khan, Ali, Ahmed, Ibrahim — names common across Muslim-majority populations worldwide — also appear frequently on OFAC and other sanctions lists because of the geographies of US sanctions programs. This creates a demographic pattern: customers with names from certain cultural backgrounds generate sanctions alerts at higher rates than others, regardless of actual sanctions risk.

Aliases and transliterations: Sanctioned individuals are typically listed under multiple aliases on the watchlist — OFAC SDN entries routinely include 10–30 alternate names and transliterations. Each alias is a potential false positive trigger for legitimate individuals with that name or a similar one.

Business name conflicts: Common words in business names ("National", "International", "Trade", "Commercial", "General") appear frequently in both legitimate business names and the names of sanctioned entities. "National Trading Company" as a business name will generate matches against multiple sanctioned entities globally.

Outdated or incomplete watchlist data: Deceased individuals and dissolved entities remain on watchlists for years. Legitimate individuals and entities whose names happen to match now-deceased sanctioned persons continue to generate alerts.

Sensitivity threshold calibration: Regulators expect sanctions screening to be comprehensive — the cost of a missed true match is extremely high. This regulatory expectation pushes institutions to set sensitivity thresholds conservatively (low similarity scores triggering alerts), which increases false positive volume.

The Cost of False Positives

False positives are not merely an operational inconvenience. They represent:

  1. Transaction delays: Wire transfers flagged for sanctions review are delayed — sometimes significantly — while the alert is dispositioned. For time-sensitive payments, this can cause material harm.

  2. Customer experience damage: Customers whose payments are repeatedly delayed due to name matching experience significant frustration. For customers from populations systematically over-screened, this creates a discriminatory experience of the banking relationship.

  3. Analyst fatigue: Analysts processing hundreds of false positives per week become fatigued — reducing the quality of review on the genuine cases that matter.

  4. Operational cost: At £8–30 per alert to disposition (depending on complexity), high false positive volumes represent significant compliance operational cost.


8.4 Calibration: Threshold Setting and Fuzzy Matching Configuration

The Threshold Decision

The screening threshold — the minimum similarity score required to generate an alert — is the primary calibration lever in sanctions screening. The trade-off:

  • Lower threshold (more sensitive): More alerts, more false positives, lower risk of missing a true match
  • Higher threshold (less sensitive): Fewer alerts, fewer false positives, higher risk of missing a true match

The regulatory expectation is that institutions should be able to demonstrate that their screening configuration would catch relevant sanctions matches. This creates pressure to set thresholds conservatively. The question is: how conservative?

"""
Threshold calibration analysis for sanctions screening.
Shows the impact of different threshold settings on
alert volume and estimated true positive capture.
"""

import pandas as pd
import numpy as np
from typing import Callable


def evaluate_threshold(
        query_names: list[str],
        watchlist: list[dict],
        similarity_fn: Callable[[str, str], float],
        threshold: float,
        known_true_matches: list[tuple[str, str]]  # (query_name, watchlist_name) pairs
) -> dict:
    """
    Evaluate a threshold setting against a labeled dataset.

    Args:
        query_names: Names to screen
        watchlist: Sanctions watchlist
        similarity_fn: Name similarity function
        threshold: Alert threshold to evaluate
        known_true_matches: Ground-truth true positive pairs

    Returns:
        Performance metrics for this threshold
    """
    alerts_generated = []

    for query in query_names:
        for entity in watchlist:
            all_names = [entity["name"]] + entity.get("aliases", [])
            for wl_name in all_names:
                score = similarity_fn(query, wl_name)
                if score >= threshold:
                    alerts_generated.append((query, wl_name, score))
                    break  # One alert per entity

    # Assess against known true matches
    true_match_set = set(known_true_matches)
    alert_pairs = {(q, w) for q, w, _ in alerts_generated}

    true_positives = len(alert_pairs & true_match_set)
    false_positives = len(alert_pairs) - true_positives
    false_negatives = len(true_match_set - alert_pairs)

    return {
        "threshold": threshold,
        "total_alerts": len(set(alerts_generated)),
        "true_positives": true_positives,
        "false_positives": false_positives,
        "missed_matches": false_negatives,
        "false_positive_rate": false_positives / len(alert_pairs) if alert_pairs else 0,
        "recall": true_positives / len(true_match_set) if true_match_set else 1.0,
    }


def calibration_analysis(
        query_names: list[str],
        watchlist: list[dict],
        similarity_fn: Callable[[str, str], float],
        known_true_matches: list[tuple[str, str]],
        thresholds: list[float] | None = None
) -> pd.DataFrame:
    """Run calibration analysis across multiple thresholds."""

    if thresholds is None:
        thresholds = [0.70, 0.75, 0.80, 0.85, 0.90, 0.95]

    results = [
        evaluate_threshold(query_names, watchlist, similarity_fn, t, known_true_matches)
        for t in thresholds
    ]

    df = pd.DataFrame(results)
    df["alerts_per_true_positive"] = df["total_alerts"] / df["true_positives"].replace(0, 1)
    return df

Supporting Data as a Secondary Filter

The solution to the threshold dilemma is not to raise the threshold (risking missed true matches) but to use supporting data to differentiate high-probability false positives from genuine matches within the alert population.

Date of birth: The most powerful disambiguation field. If a customer's date of birth does not match the sanctioned person's date of birth, the probability of a true match drops dramatically — though not to zero (reported DOBs can be incorrect or falsified).

Nationality/country of origin: A UK citizen born in Manchester with no connection to a sanctioned individual's known country of origin is less likely to be a true match.

Address: Physical address information can support differentiation, though addresses are easily falsified.

Document identifiers: Passport number, national ID number — if available — can definitively rule in or out a match.

The practical application: a screening system should generate an alert when the name similarity threshold is met, but the alert's priority (and the analytical effort devoted to its review) should be weighted by the quantity and quality of supporting data alignment.

A name similarity score of 0.88 with no date of birth match and no nationality match → LOW priority alert. A name similarity score of 0.92 with date of birth match and nationality match → HIGH priority alert requiring urgent review.


8.5 Real-Time vs. Batch Screening

Payment Screening: The Real-Time Requirement

For payment transactions — wire transfers, SWIFT messages, ACH payments — sanctions screening must occur before the payment is executed. This creates a hard real-time requirement: screening must complete in time to block a payment before it is processed.

For international wire transfers, the SWIFT messaging standard (MT103, MT202) includes structured fields for originator, beneficiary, and intermediary bank information. These fields must be screened. The screening window: milliseconds to seconds, depending on system architecture.

The tension: high-speed payment processing and thorough sanctions screening have opposing requirements. Payment systems want throughput; compliance systems want depth. Optimizing for both requires careful architecture.

Straight-through processing with exception handling: Screen at message receipt. If no alert: process immediately. If alert: hold payment in a suspense queue for compliance review. This model handles the common case (no match) with maximum speed while maintaining appropriate controls for alert cases.

Filtering approaches for high-volume low-risk flows: For certain payment types where the sanctioned entity risk is systematically very low (e.g., small domestic consumer payments), some institutions implement a pre-filter that routes clearly low-risk payments through a lightweight check while routing higher-risk payment types (international wires, correspondent flows) through the full screening engine.

Customer Screening: The Periodic and Trigger-Based Requirement

Customer screening is not (only) a one-time event. Customers must be re-screened: - At regular intervals (at minimum annually for all customers; more frequently for high-risk customers) - When a new sanctions designation is published — requiring rapid re-screening of the customer base against the new designation - When the customer's information changes (new name, new address, new entity structure)

Designation-triggered screening is particularly operationally challenging. When OFAC publishes a new SDN addition, financial institutions must screen their entire customer base against the new designation "as soon as practicable." For large institutions with millions of customers, this means having sufficient compute capacity to re-screen millions of names on short notice.

"""
Batch screening implementation: screening a customer population
against a sanctions list, with change detection to identify
only newly relevant matches (delta screening).
"""

from datetime import datetime


class BatchScreeningJob:
    """
    Manages batch screening of a customer population.
    Implements delta screening: only generates alerts for
    matches that weren't present in the prior screening run.
    """

    def __init__(self, screener: 'SanctionsScreener'):
        self.screener = screener
        self.prior_alerts: dict[str, set[str]] = {}  # customer_id → set of matched entity IDs

    def run_batch(
            self,
            customers: list[dict],
            run_id: str
    ) -> dict:
        """
        Screen all customers and return new/changed/resolved alerts.

        Each customer: {"customer_id": str, "name": str, "dob": str | None,
                        "nationality": str | None}
        """
        current_alerts: dict[str, set[str]] = {}
        all_matches = []

        for customer in customers:
            matches = self.screener.screen(
                customer["name"],
                query_dob=customer.get("dob"),
                query_nationality=customer.get("nationality")
            )

            entity_ids_matched = {
                m["entity"]["entity_id"] for m in matches
            }
            current_alerts[customer["customer_id"]] = entity_ids_matched

            for match in matches:
                all_matches.append({
                    "customer_id": customer["customer_id"],
                    "customer_name": customer["name"],
                    "entity_id": match["entity"]["entity_id"],
                    "entity_name": match["entity"]["name"],
                    "name_score": match["name_score"],
                    "alert_priority": match["alert_priority"],
                    "is_new": match["entity"]["entity_id"] not in
                              self.prior_alerts.get(customer["customer_id"], set()),
                    "run_id": run_id,
                    "screened_at": datetime.utcnow().isoformat()
                })

        # Identify resolved alerts (previously matched, no longer matching)
        resolved = []
        for cust_id, prior_entity_ids in self.prior_alerts.items():
            current_entity_ids = current_alerts.get(cust_id, set())
            for entity_id in (prior_entity_ids - current_entity_ids):
                resolved.append({
                    "customer_id": cust_id,
                    "entity_id": entity_id,
                    "resolution": "below_threshold",
                    "run_id": run_id
                })

        self.prior_alerts = current_alerts

        return {
            "run_id": run_id,
            "customers_screened": len(customers),
            "total_matches": len(all_matches),
            "new_alerts": [m for m in all_matches if m["is_new"]],
            "ongoing_alerts": [m for m in all_matches if not m["is_new"]],
            "resolved_alerts": resolved,
        }

8.6 OFAC Compliance Obligations and True Match Consequences

What Happens at a True Match

When a sanctions screening alert is confirmed as a true match — a genuine sanctioned individual or entity — the compliance obligations are immediate and specific.

US OFAC (SDN match): 1. Block/reject: The transaction must be blocked (assets frozen if already received) or rejected (wire transfer declined before processing) 2. OFAC reporting: OFAC requires reporting of blocked transactions and rejected transactions. Blocked property must be reported within 10 business days of blocking. Rejected transactions must be reported within 10 business days of rejection 3. Record retention: All records of OFAC screening matches, blocking actions, and reporting must be retained for five years 4. Voluntary Self-Disclosure: If the institution discovers a past violation, voluntary self-disclosure to OFAC before OFAC discovers the violation is a significant mitigating factor in civil penalty proceedings

UK OFSI: Similar obligations: freeze, report to OFSI within 10 days of awareness, maintain records. OFSI has broad civil penalty powers for violations.

EU: Freeze assets immediately; report to national competent authority (varies by member state) within the timeframe specified in the applicable regulation.

The Penalty Framework

The cost of a missed true sanctions match can be severe:

OFAC civil penalties: Up to $337,267 per violation (2024 adjusted figure) or twice the transaction value, whichever is greater. Penalties are compounded for egregious cases. OFAC's $8 billion settlement with BNP Paribas in 2014 remains the largest sanctions penalty in history.

Criminal prosecution: Willful violations can result in criminal prosecution, with individuals facing imprisonment.

License revocation: For financial institutions, loss of banking license is the ultimate sanction — rarely imposed but representing existential risk.

The asymmetry of consequences explains the calibration pressure toward sensitivity: the cost of a missed true positive (potential eight-figure penalty) vastly exceeds the cost of a false positive (analyst time + customer inconvenience).


8.7 Regulatory Expectations and Best Practice

What Regulators Look For

OFAC publishes a framework for assessing sanctions compliance programs, identifying five essential components:

  1. Management commitment: Senior management and board engagement with the sanctions compliance program
  2. Risk assessment: Regular assessment of the institution's specific sanctions exposure (products, geographies, customers)
  3. Internal controls: Policies, procedures, and controls designed to prevent, detect, and remediate sanctions violations
  4. Testing and auditing: Regular testing of controls, including screening systems, with independent audit
  5. Training: Sanctions-specific training for relevant personnel

For screening systems specifically, OFAC and other regulators expect institutions to be able to demonstrate: - What lists are being screened - What matching algorithm and threshold settings are used - How frequently lists are updated - How alerts are managed and documented - How true matches are handled and reported

Technology vs. Process

Maya's observation after her first year managing Verdant's sanctions screening program: "The technology is the easy part. We bought a screening tool that works. The hard part is the process — making sure every alert is reviewed with appropriate urgency, documented appropriately, and that the true matches get escalated immediately when they appear. Technology finds the needles. Process is how you make sure you actually look at every needle."

This observation mirrors the structure of OFAC's compliance framework: controls (technology) plus process (human review and escalation) plus documentation (audit trail) plus governance (management oversight).

Priya's Calibration Checklist

When Priya evaluates a client's sanctions screening program, she works through a standard calibration checklist:

  1. List coverage: Are all required lists being screened? (SDN, EU Consolidated, UK Consolidated, UN; plus any jurisdiction-specific lists required by the institution's regulatory footprint)
  2. List update frequency: How quickly does the screening system update when new designations are published? (Same-day updates are the standard expectation)
  3. Threshold documentation: Is the matching threshold documented and justified? Has it been tested against a labeled dataset?
  4. Alias coverage: Does screening include all aliases from the watchlist, or only primary names?
  5. Supporting data utilization: Is date of birth, nationality, and other supporting data used to refine alert priority?
  6. Payment screening latency: For payment screening, is the system integrated into the payment processing chain before execution?
  7. Alert documentation: Is every alert dispositioned in documented form? Are clearance decisions recorded with rationale?
  8. True match escalation: Is there a documented escalation path for confirmed true matches to compliance senior management?
  9. OFAC reporting procedures: Are staff trained on what to do when a true match is confirmed, including reporting timelines?
  10. Annual testing: Is the screening configuration tested annually against a sample of known true matches?

Chapter Summary

Sanctions screening is the process by which financial institutions prevent transactions with sanctioned individuals, entities, and countries — one of the highest-stakes compliance functions in banking because the regulatory consequences of a missed true match are severe.

The sanctions landscape encompasses OFAC (US), OFSI/HM Treasury (UK), EU, and UN sanctions regimes, each with specific lists, requirements, and extraterritorial considerations.

Matching algorithms range from exact matching through fuzzy similarity measures (Levenshtein distance, phonetic matching, n-gram similarity) to ML-based matching that learns name similarity patterns from training data.

The false positive problem in sanctions screening is acute — false positive rates exceeding 99% are common — driven by common name conflicts, transliteration variants, and the regulatory pressure to calibrate for maximum sensitivity.

Threshold calibration requires a documented balance between sensitivity (catching true matches) and specificity (reducing false positive volume). Supporting data (date of birth, nationality, document identifiers) is the primary tool for differentiating high-probability false positives from genuine alerts within the flagged population.

Real-time vs. batch screening serve different functions: payment screening requires real-time integration in the processing chain; customer screening uses periodic batch processes supplemented by designation-triggered screening.

True match consequences are immediate: block or reject, report to regulator (OFAC, OFSI, national authority) within prescribed timelines, maintain records for five years.

OFAC's five-component compliance framework — management commitment, risk assessment, internal controls, testing/auditing, training — provides the benchmark against which regulators assess sanctions compliance programs.


Continue to Chapter 9: Beneficial Ownership and Corporate Transparency →