2026-04-22 · architecture / fintech
金融科技工程
面向中国工程团队的金融科技系列。从账务底盘、支付、清结算、交易所、风控合规到可靠性与灾备,中国与全球视角并举,讲清楚金融系统在工程落地中的真实挑战。





























反欺诈(Anti-Fraud)回答的是”这笔交易是不是坏人发的”;反洗钱(Anti-Money Laundering,AML)回答的是更结构性、更合规化的问题:这个客户是不是坏人?这条资金链路是不是把”黑钱”洗白?这个对手方是不是我国或国际社会正在制裁的实体? 从工程角度看,前者关心毫秒级决策、F1 分数与体验;后者关心监管可解释性、文档留痕、报表准时、历史可回溯七到十年。做错了反欺诈,公司亏钱;做错了 AML,公司被罚款、管理层被追责、牌照被吊销。2012 年汇丰银行(HSBC)因对墨西哥毒贩资金清洗不力被美国司法部罚没 19.2 亿美元,2020 年 FinCEN 文件(FinCEN Files)揭露全球多家大型银行累计处理可疑交易超过 2 万亿美元——这两个事件重塑了每一家金融机构对 AML 的投入强度。
本篇读者画像:支付公司风控或合规工程师、数字银行 KYC 系统研发、加密资产交易所合规团队、需要理解”监管科技”(RegTech)落地方式的架构师。我们默认读者已阅读上一篇《反欺诈》,理解设备指纹、图谱与行为序列;本文聚焦合规框架下的系统设计:监管怎么说、客户怎么识别、名单怎么筛、交易怎么监测、可疑怎么上报、链上钱怎么追。最后一节讨论 GDPR 与 AML 留存期的张力——这是工程上最微妙的一处。
工程师习惯把法律当”外部 API”——不必读全文,但必须知道每个端点返回什么、要求什么字段。AML 的监管栈有三层:国际标准(FATF)、主权国家法律、行业指引。
金融行动特别工作组(Financial Action Task Force,FATF)于 1989 年由 G7 发起,2012 年发布当前版本《四十项建议》(The FATF Recommendations),并通过相互评估(Mutual Evaluation)与”灰名单/黑名单”机制对各国施加压力。与工程相关的核心条款:
SAR 与 CTR 上报给 FinCEN(Financial Crimes Enforcement Network),SAR 规定在发现后 30 天内提交,异常严重可 60 天。
工程上我们并不逐条实现法条,而是把所有来源汇集成一张”合规矩阵”:管辖地 × 客户类型 × 交易产品 × 风险等级 → 规则集合。下面的 KYC 生命周期就是矩阵的”横切”。
flowchart LR
A[获客/开户申请] --> B[CIP 身份识别]
B --> C{PEP/制裁命中?}
C -- 是 --> D[EDD 加强尽调]
C -- 否 --> E[CDD 基础尽调]
D --> F[风险评级 R1-R4]
E --> F
F --> G[关系建立]
G --> H[持续监测]
H --> I{触发事件?}
I -- 信息变更/交易异常/名单更新 --> J[触发式复核]
I -- 周期到期 --> K[定期复核]
J --> F
K --> F
G --> L[关系终止]
L --> M[档案封存 5-10 年]
CIP 是开户的”门槛”。自然人最小字段集:姓名、出生日期、地址、身份证号(或护照号 + 签发国)。企业客户还需工商注册信息、法定代表人、受益所有人(UBO,通常持股 25% 以上)。
CREATE TABLE customer (
customer_id BIGINT PRIMARY KEY,
customer_type VARCHAR(16) NOT NULL, -- INDIVIDUAL / CORPORATE
legal_name VARCHAR(200) NOT NULL,
legal_name_en VARCHAR(200), -- 英文/拼音,用于制裁筛查
date_of_birth DATE,
nationality CHAR(2), -- ISO 3166-1 alpha-2
residence_country CHAR(2),
id_type VARCHAR(32), -- NATIONAL_ID / PASSPORT / BR_CODE
id_number_hash CHAR(64) NOT NULL, -- SHA-256(id || salt)
id_number_enc VARBINARY(256), -- AES-GCM 密文
created_at TIMESTAMP NOT NULL,
onboarded_by VARCHAR(64) NOT NULL, -- 业务线 / 渠道
risk_rating CHAR(2), -- R1..R4
review_due_at DATE, -- 下次定期复核
status VARCHAR(16) NOT NULL -- ACTIVE / SUSPENDED / CLOSED
) PARTITION BY RANGE (YEAR(created_at));
CREATE INDEX idx_customer_name_en ON customer(legal_name_en);
CREATE INDEX idx_customer_review ON customer(review_due_at, status);
CREATE TABLE customer_document (
doc_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
doc_type VARCHAR(32) NOT NULL, -- ID_FRONT / ID_BACK / PASSPORT / SELFIE / LIVENESS_VIDEO / UTILITY_BILL
storage_uri VARCHAR(512) NOT NULL, -- S3 / OSS 加密桶
ocr_json JSON,
verify_status VARCHAR(16) NOT NULL, -- PASS / FAIL / MANUAL
hash_sha256 CHAR(64) NOT NULL, -- 反篡改
uploaded_at TIMESTAMP NOT NULL
);
CREATE TABLE beneficial_owner (
customer_id BIGINT NOT NULL,
ubo_name VARCHAR(200) NOT NULL,
ubo_id_hash CHAR(64) NOT NULL,
ownership_pct DECIMAL(5,2) NOT NULL,
is_pep BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (customer_id, ubo_id_hash)
);几个工程细节:
hash
列,加盐防止彩虹表。CDD 的输出是客户风险评级(Customer Risk Rating,CRR)。典型权重模型(信息性,非监管规定):
| 维度 | 取值 | 得分 |
|---|---|---|
| 国籍/地区 | 中国大陆 / 港澳台 / FATF 黑名单国 | 10 / 20 / 80 |
| 行业 | 零售 / 贵金属 / 赌博 / 军工 | 10 / 50 / 70 / 80 |
| 产品 | 活期存款 / 跨境汇款 / 私行理财 | 10 / 40 / 60 |
| 渠道 | 面签 / 远程 / 第三方代理 | 10 / 30 / 50 |
| PEP | 否 / 国内 / 外国 | 0 / 30 / 60 |
| UBO 透明度 | 清晰 / 多层 / 离岸壳公司 | 10 / 40 / 80 |
加权后映射到 R1(低)至 R4(高),R3/R4 进入 EDD:要求资金来源证明(Source of Funds)、财富来源证明(Source of Wealth)、高管审批。EDD 必须由独立于业务线的合规审批,形成”二线”。
远程开户(eKYC)是近年快速演化的领域。一条典型 eKYC 链路:
sequenceDiagram
participant U as 用户
participant APP as 移动端
participant KYC as KYC 服务
participant OCR as OCR/NFC
participant FR as 人脸/活体
participant VC as 公安/运营商/银行联核
participant S as 制裁/PEP
U->>APP: 开户
APP->>OCR: 身份证正反面
OCR-->>APP: 结构化字段
APP->>OCR: NFC 读取 eMRTD (护照/港澳台居住证)
OCR-->>APP: DG1/DG2 + BAC/PACE 验签
APP->>FR: 活体自拍 (动作/静默)
FR-->>APP: liveness_score
APP->>KYC: 提交 (字段 + 照片 + video_hash)
KYC->>VC: 二/三/四要素核验
VC-->>KYC: 比对结果
KYC->>FR: 证件照 vs 自拍 (face_match)
FR-->>KYC: similarity
KYC->>S: 姓名 + 证件号 + 国籍
S-->>KYC: 名单命中结果
KYC-->>APP: 通过/转人工
中国二代身份证可通过 NFC 读取芯片信息,但 芯片解密必须由持牌公安数据接入方(例如经”互联网+可信身份认证平台”CTID)完成,普通开发者不能自行解芯片。常见方案:现场拍照 OCR + 后端调用”身份证二要素/三要素”核验 API 确认真伪;或接入 CTID 获得不落地号码的网证。
国际证件(护照、港澳台居住证)遵循 ICAO 9303 eMRTD 规范,芯片包含 DG1(MRZ)、DG2(人脸)、DG3(指纹,一般银行无权限)、SOD(签名)。工程上用 iOS Core NFC / Android NfcA 读取,BAC/PACE 派生会话密钥后读取数据组,并用签发国 CSCA 根证书验证 SOD 链。
活体(Liveness)分两类:
人脸比对给出 similarity ∈ [0, 1],阈值与业务风险挂钩:开户 0.82、登录 0.75、刷脸支付 0.92(示例阈值,实际以算法供应商校准为准)。每次比对必须留存 原图 + 比对图 + 分数 + 模型版本,否则监管审计无法复现。
中国常见的身份联核:
“四要素”常被用作支付绑卡的最低标准。核验接口响应通常返回 “一致/不一致/无此记录”,不返回明文,避免”用他人身份证号+随机手机号反查”。
电子签名须符合《电子签名法》与 ETSI EN 319 411(欧盟)、eIDAS 规则。金融开户合同需要可靠电子签名(可靠签名需要具备签名人专有控制、可检测签名后变动),常见方案是接入合规 CA 的远程签章服务(如中国金融认证中心 CFCA),签名数据写入 PDF LTV(Long-Term Validation)结构,证书吊销列表(CRL)或 OCSP 证据一并存档。
2024 年以来,香港、日本、新加坡多起”AI 换脸视频会议诈骗”案件让远程开户面临新威胁。工程上的常见对抗:
移动网络极不稳定,一次开户常横跨 5–15 分钟,用户可能中断。系统必须幂等:
application_id(UUID),客户端断网重连后以
application_id 继续上传缺失步骤。DRAFT → ID_SUBMITTED → LIVENESS_DONE → VC_VERIFIED → SCREENING_PASS → APPROVED,非法跳转拒绝。application_id + doc_type 做去重。idempotency_key,供应商返回结果缓存 24h。| 名单 | 维护方 | 频率 | 格式 |
|---|---|---|---|
| OFAC SDN / Non-SDN | 美国财政部 | 不定期,通常当日 | XML / CSV |
| UN Consolidated Sanctions | 联合国安理会 | 不定期 | XML |
| EU Consolidated List | 欧盟理事会 | 按决议发布 | XML / CSV |
| HMT / OFSI | 英国财政部 | 每日 | CSV / XML |
| 人民银行反洗钱监测分析中心发布的 | PBoC | 人行系统内 | 内部 |
| 国际 PEP 库 | Dow Jones / Refinitiv World-Check / LexisNexis | 商业,每日 | API |
工程实现的第一步是把每家源做成标准化中间表:
CREATE TABLE sanction_entity (
list_code VARCHAR(16) NOT NULL, -- OFAC_SDN / UN / EU / HMT / PBOC
entity_uid VARCHAR(64) NOT NULL, -- 源系统内 ID
entity_type VARCHAR(16) NOT NULL, -- INDIVIDUAL / ENTITY / VESSEL / AIRCRAFT / CRYPTO_WALLET
primary_name VARCHAR(400) NOT NULL,
dob DATE,
nationality CHAR(2),
ids_json JSON, -- 证件号码集合
program VARCHAR(64), -- SDGT / UKRAINE-EO13662 / CYBER2 ...
updated_at TIMESTAMP NOT NULL,
PRIMARY KEY (list_code, entity_uid)
);
CREATE TABLE sanction_alias (
list_code VARCHAR(16) NOT NULL,
entity_uid VARCHAR(64) NOT NULL,
alias_name VARCHAR(400) NOT NULL,
alias_type VARCHAR(16), -- AKA / FKA / NKA
script VARCHAR(16), -- LATIN / CYRILLIC / ARABIC / HAN
PRIMARY KEY (list_code, entity_uid, alias_name)
);制裁筛查的核心是”不能漏”。同一个人在不同名单里可能写作 “Vladimir Putin”、“Владимир Путин”、“普京”、“V. V. Putin”。算法组合:
import unicodedata
import re
from pypinyin import lazy_pinyin
from rapidfuzz import fuzz
from jellyfish import jaro_winkler_similarity, metaphone
TITLES = {"mr", "mrs", "ms", "dr", "sir", "madam",
"先生", "女士", "博士", "教授", "同志"}
def normalize(name: str) -> str:
s = unicodedata.normalize("NFKD", name)
s = "".join(ch for ch in s if not unicodedata.combining(ch))
s = s.lower()
s = re.sub(r"[^\w\u4e00-\u9fff ]+", " ", s)
tokens = [t for t in s.split() if t not in TITLES]
return " ".join(tokens).strip()
def to_latin_tokens(name: str) -> list[str]:
n = normalize(name)
tokens = []
for tok in n.split():
if re.search(r"[\u4e00-\u9fff]", tok):
tokens.extend(lazy_pinyin(tok))
else:
tokens.append(tok)
return tokens
def name_score(query: str, candidate: str) -> float:
q = " ".join(to_latin_tokens(query))
c = " ".join(to_latin_tokens(candidate))
if not q or not c:
return 0.0
jw = jaro_winkler_similarity(q, c)
tsr = fuzz.token_set_ratio(q, c) / 100
pho_q = metaphone(q.replace(" ", ""))
pho_c = metaphone(c.replace(" ", ""))
pho = 1.0 if pho_q and pho_q == pho_c else 0.0
return 0.45 * jw + 0.45 * tsr + 0.10 * pho
def screen(query_name: str, aliases: list[str], threshold=0.88):
hits = []
for a in aliases:
s = name_score(query_name, a)
if s >= threshold:
hits.append((a, round(s, 3)))
return sorted(hits, key=lambda x: -x[1])
# 示例
candidates = ["Vladimir Vladimirovich Putin", "V. V. Putin",
"普京", "Владимир Путин"]
print(screen("vladimir putin", candidates, threshold=0.80))高命中率必然带来高误报。工业界典型做法:
不同业务场景对延迟与完整性的要求不同:
| 场景 | 模式 | 延迟要求 | 备注 |
|---|---|---|---|
| 开户 | 同步 | < 2s | 命中则阻断流程 |
| 跨境汇款 | 同步 | < 500ms 单条 | SWIFT MT 报文出库前 |
| 卡支付授权 | 异步 / 黑名单命中前置 | 名单查 <50ms | 仅对 SDN 精确匹配阻断 |
| 日终客户重扫 | 批量 | 夜间 1–4h | 名单变动全量扫描 |
| 新名单发布 | 事件驱动 | 发布后 1h 内重扫存量客户 | OFAC 更新 RSS 触发 |
| 历史回溯 | 离线 | 按需 | 稽查要求重现 |
工程上把筛查能力沉淀为两套服务:同步 API(毫秒级,索引驻留内存,规则简化)与批量引擎(Spark 作业,全算法全阈值)。两者共用规则定义,但性能与召回率有意不同。同步 API 的漏报由批量引擎在 T+0/T+1 兜底,兜底命中即升级为案件,客户账户冻结。
跨境合规系统里证件号校验常栽跟头:
000-xx-xxxx、666-xx-xxxx、9xx-xx-xxxx
等段。筛查引擎做精确匹配前必须按 (国家, 证件类型)
规范化,否则”中国身份证 vs 俄罗斯内护”
的字符串撞车会制造大量误报。
TMS 的目标不是阻断交易(那是反欺诈干的事),而是发现异常模式、支持 STR 上报。上游是账务与支付事件流,下游是案件管理。
flowchart LR
subgraph 源
P1[支付系统]
P2[核心账务]
P3[跨境汇款]
P4[证券清算]
P5[链上交易]
end
P1 & P2 & P3 & P4 & P5 --> KAFKA((事件总线))
KAFKA --> N[标准化 + 丰富<br/>客户画像/对手方/图谱]
N --> RULE[规则引擎<br/>Drools/自研]
N --> ML[异常检测模型]
RULE --> ALERT[预警池]
ML --> ALERT
ALERT --> CASE[案件管理]
CASE --> STR[STR/SAR 上报]
CASE --> FB[标注反馈]
FB --> ML
在中国场景(人民银行令〔2016〕第 3 号)常见阈值(简化,实际以最新规定为准):
美国 FinCEN:CTR 单日现金 > 10,000 美元事后 15 天;SAR 发现后 30 天。
| 场景 | 特征 | 规则描述(伪代码) |
|---|---|---|
| 分拆(Smurfing / Structuring) | 故意低于阈值 | 近 7 日内 ≥ 5 笔 8,000–9,999 美元现金存款 |
| 快进快出(Pass-through) | 大额入账后 24h 内 ≥ 90% 出账 | out_amt / in_amt > 0.9 AND hold_hours < 24 |
| 异地集中 | 大量不同地域打款到同一账户 | 30 日内打款人分布于 ≥ 10 个城市且单笔 < 阈值 |
| 过桥账户 | 同人多账户轮转 | 图谱 2-hop 内回流 ≥ 80% 本金 |
| 账户睡眠激活 | 长期不动后突发大额 | days_since_last_txn > 180 AND amount > R3_threshold |
| 高风险国家 | 往来 FATF 黑/灰名单国家 | 对手方国家 ∈ high_risk_list |
| 对敲(Mirror Trading) | 两地 A/B 对敲汇款 | 同一实控人不同地域账户近 T 日内对敲规模 |
| 地下钱庄(Hawala 变体) | 小额高频 + 无商业背景 | 月内日均 > N 笔且 MCC 聚焦于”个人转账” |
一段简化规则引擎的 Python 原型:
from dataclasses import dataclass
from datetime import datetime, timedelta
from collections import defaultdict, deque
@dataclass
class Txn:
id: str
customer_id: str
amount_usd: float
direction: str # IN / OUT
ts: datetime
counterparty: str
country: str
channel: str # CASH / WIRE / CARD / CRYPTO
class StructuringRule:
"""近 N 天内小于阈值的现金存款次数达标 → 报警"""
def __init__(self, window_days=7, threshold_low=8_000,
threshold_high=9_999, count=5):
self.window = timedelta(days=window_days)
self.low, self.high, self.count = threshold_low, threshold_high, count
self.buf: dict[str, deque[Txn]] = defaultdict(deque)
def feed(self, t: Txn):
if not (t.channel == "CASH" and t.direction == "IN"
and self.low <= t.amount_usd <= self.high):
return None
q = self.buf[t.customer_id]
q.append(t)
while q and q[0].ts < t.ts - self.window:
q.popleft()
if len(q) >= self.count:
return {"rule": "STRUCTURING",
"customer": t.customer_id,
"txn_ids": [x.id for x in q],
"score": min(1.0, len(q) / 10)}
return None
class PassThroughRule:
"""大额入账 24h 内 >= 90% 出账"""
def __init__(self, min_in=100_000, out_ratio=0.9,
window_hours=24):
self.min_in, self.ratio = min_in, out_ratio
self.window = timedelta(hours=window_hours)
self.in_events: dict[str, list[Txn]] = defaultdict(list)
self.out_sum: dict[str, float] = defaultdict(float)
def feed(self, t: Txn):
key = t.customer_id
if t.direction == "IN" and t.amount_usd >= self.min_in:
self.in_events[key].append(t)
elif t.direction == "OUT":
self.out_sum[key] += t.amount_usd
# 清理过期入账并评估
alerts = []
kept = []
for ev in self.in_events[key]:
if t.ts - ev.ts > self.window:
continue
kept.append(ev)
self.in_events[key] = kept
for ev in kept:
if self.out_sum[key] / ev.amount_usd >= self.ratio:
alerts.append({"rule": "PASS_THROUGH",
"customer": key,
"in_txn": ev.id,
"out_sum": self.out_sum[key]})
return alerts or None真实 TMS 通常跑在 Flink/Spark Streaming 上,状态后端 RocksDB,规则热更新;异常检测走 Isolation Forest、HMM、GraphSAGE 等。模型与规则不相互替代:规则用于监管可解释场景(大额、特定国别),模型用于”未见过的模式”。
合规报告的 可疑原因
字段必须是自然语言,“模型分数 0.97”不够。工程上常用 SHAP
或规则回译(Rule Distillation)把模型输出翻译成”近 30
天与高风险地区交易占比升至 68%,显著高于同业中位数 5%“。
机器学习模型在 TMS 中的角色不是”替代规则”,而是覆盖规则看不见的长尾。特征大致分四层:
| 层级 | 示例特征 | 窗口 | 备注 |
|---|---|---|---|
| 账户静态 | 开户渠道、KYC 等级、职业、行业 | — | 与 CRR 对齐 |
| 交易聚合 | 日/周/月笔数、金额、方差、最大值 | 1d/7d/30d/180d | 滑动窗口 |
| 对手方网络 | 入度、出度、集聚系数、PageRank、2-hop 高风险比 | 每日离线 | 图计算 |
| 时序模式 | 日内分布熵、跨境比例、现金比例、节假日异常 | 7d/30d | 周期性 |
模型组合常见做法:
离线训练与在线推理分离,特征存储使用 Feast / HBase / Redis,确保离线/在线一致性。模型上线前必须通过合规模型风险管理(Model Risk Management,MRM)评审:数据血缘、偏差测试、对保护群体的公平性(Disparate Impact)、降级方案。
任何阈值都是可审计对象。建立阈值注册表:
CREATE TABLE rule_config (
rule_code VARCHAR(64) PRIMARY KEY,
version INT NOT NULL,
params_json JSON NOT NULL,
effective_from TIMESTAMP NOT NULL,
effective_to TIMESTAMP,
approved_by VARCHAR(64) NOT NULL,
change_reason TEXT NOT NULL,
jira_ref VARCHAR(32)
);每次阈值调整必须有合规官签字与工单号,生产系统只读取”当前生效”版本。审计时回放:用历史版本对历史数据重放,证明过去的决策基于当时有效的规则。
stateDiagram-v2
[*] --> New: 规则/模型命中
New --> Triage: 分派
Triage --> Investigating: 一线分析
Investigating --> EscalatedL2: 升级二线
Investigating --> ClosedFalse: 排除误报
EscalatedL2 --> Filing: 决定上报
EscalatedL2 --> ClosedFalse: 排除
Filing --> Filed: 已提交 FIU
Filed --> [*]
ClosedFalse --> [*]
案件数据模型示例:
CREATE TABLE case_ (
case_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
opened_at TIMESTAMP NOT NULL,
trigger VARCHAR(64) NOT NULL, -- RULE:STRUCTURING / MODEL:IF-0.91 / LIST:OFAC_SDN
status VARCHAR(16) NOT NULL,
assignee VARCHAR(64),
severity CHAR(2), -- S1..S4
sla_due_at TIMESTAMP NOT NULL,
narrative TEXT, -- 最终叙事
decision VARCHAR(16), -- FILE / NO_FILE / SAR / CTR
filed_at TIMESTAMP
);
CREATE TABLE case_evidence (
case_id BIGINT,
kind VARCHAR(32), -- TXN / KYC_DOC / NETWORK_GRAPH / CHAIN_TRACE
ref VARCHAR(256),
added_by VARCHAR(64),
added_at TIMESTAMP,
PRIMARY KEY (case_id, kind, ref)
);
CREATE TABLE str_report (
report_id BIGINT PRIMARY KEY,
case_id BIGINT NOT NULL,
authority VARCHAR(32) NOT NULL, -- FINCEN / CAMLMAC / FIU_NL ...
format VARCHAR(16) NOT NULL, -- GOAML / FINCEN_SAR_XML / PBOC_CSV
payload_uri VARCHAR(512) NOT NULL,
submitted_at TIMESTAMP NOT NULL,
ack_id VARCHAR(128)
);上报格式:
工程上必须保证幂等上报:提交失败重试时不重复,靠
report_id 唯一约束与回执 ack_id 追踪。
不同 FIU 的报文格式各异。以 UN goAML
为例(许多司法辖区采用),一个简化的
<report> 片段:
<report>
<rentity_id>BANK_ABC_0001</rentity_id>
<submission_code>STR</submission_code>
<report_code>STR</report_code>
<submission_date>2026-04-15T09:12:44+08:00</submission_date>
<currency_code_local>CNY</currency_code_local>
<reason>疑似分拆现金存款,短期快进快出,疑涉非法集资</reason>
<action>账户限制交易</action>
<transaction>
<transactionnumber>T-2026041500931</transactionnumber>
<date_transaction>2026-04-12T10:22:00+08:00</date_transaction>
<transmode_code>CASH_DEPOSIT</transmode_code>
<amount_local>98500.00</amount_local>
<t_from>
<from_account>
<institution_code>BANK_ABC</institution_code>
<branch>SH001</branch>
<account>6222****1234</account>
<signatory>
<t_person>
<first_name>San</first_name>
<last_name>Zhang</last_name>
<birthdate>1985-07-02</birthdate>
<ssn>3101XXXXXXXXXXXXXX</ssn>
<nationality1>CN</nationality1>
</t_person>
</signatory>
</from_account>
</t_from>
<t_to>
<to_account>
<institution_code>BANK_ABC</institution_code>
<account>6222****9988</account>
</to_account>
</t_to>
</transaction>
</report>几个常见字段口径问题:
<from_funds_source>CASH</from_funds_source>
代替,绝不可伪造。<involved_parties>
群体上报,节省调查员反复提交。合规分析师的日常是”从告警出发,在数小时内还原资金图景”。一个高效的调查工作台应一键展开:
flowchart TB
ALERT[告警事件] --> VW1[客户 360 视图]
ALERT --> VW2[交易瀑布图]
ALERT --> VW3[关系网络图]
ALERT --> VW4[链上资金追踪]
VW1 --> D1[KYC 档案/EDD 附件]
VW1 --> D2[历史案件]
VW2 --> D3[30/90/365 日对比]
VW3 --> D4[2-hop 图谱/共享设备/共享 IP/UBO]
VW4 --> D5[链上分析平台调用]
VW1 & VW2 & VW3 & VW4 --> NAR[叙事生成草稿]
NAR --> REVIEW[二线合规复核]
工具链的关键:
链上地址天然伪匿名。商业链上分析平台(Chainalysis、TRM Labs、Elliptic、MistTrack、Arkham)做两件事:
2022 年 8 月,OFAC 把 Tornado Cash 及其智能合约地址列入 SDN 名单,这是 OFAC 第一次把智能合约地址本身列入制裁名单,引发激烈争议。2023 年 8 月美国司法部起诉其开发者。2024 年 11 月,美国第五巡回上诉法院在 Van Loon v. Department of the Treasury 案中裁定 OFAC 对 Tornado Cash 不可变智能合约的制裁超越授权(因其不属于 IEEPA 下的”财产”),相关地址随后被移除。但开发者刑事案件独立进行——代码即言论的讨论进入了金融合规领域。对工程师的启示:合约地址级制裁技术上可以被执行(交易所拒绝相关地址),但法律上仍在演化,筛查规则的”白/黑”有时需要日级刷新。
FATF R.16 在 2019 年的 Interpretive Note 明确要求 VASP 间传递发起人与受益人信息。国际证券化数据模型 IVMS101(InterVASP Messaging Standard 101)提供统一字段:
{
"originator": {
"originatorPersons": [{
"naturalPerson": {
"name": {"nameIdentifier": [{
"primaryIdentifier": "Zhang",
"secondaryIdentifier": "San",
"nameIdentifierType": "LEGL"
}]},
"geographicAddress": [{
"addressType": "HOME",
"country": "CN",
"townName": "Shanghai",
"streetName": "Nanjing Road",
"buildingNumber": "100"
}],
"nationalIdentification": {
"nationalIdentifier": "3101...X",
"nationalIdentifierType": "CCPT",
"countryOfIssue": "CN"
}
}
}],
"accountNumber": ["0xabc...def"]
},
"beneficiary": { "...": "..." }
}行业没有统一协议,多家互操作:
| 协议 | 发起方 | 传输方式 | 特点 |
|---|---|---|---|
| TRP (Travel Rule Protocol) | OpenVASP 系 | HTTPS | 轻量、开放规范 |
| TRISA (Travel Rule Info Sharing Alliance) | CipherTrace | gRPC + mTLS | 去中心 PKI |
| Sygna Bridge | CoolBitX | HTTPS | 早期亚洲主流 |
| Shyft Veriscope | Shyft | 链上发现 | 以太坊智能合约 |
| Notabene / SumSub / 21 Analytics | 商业 | 多协议桥接 | SaaS |
一个合规工程要点:VASP 间对手方发现(Counterparty Discovery,CD)是先决问题——“你知道这个收款地址属于哪家 VASP 吗?”常见做法组合:地址归属库查询(链上分析平台)+ 用户自报 + 交易所联合白名单。发现失败时按 FATF 指南,对于自托管(Self-Hosted)钱包应取得附加信息(发起人声明、地址所有权证明)。
中国对金融基础设施要求”穿透式监管”——支付机构、交易所、清算机构需要按日甚至实时向监管直报底层明细。人民银行”反洗钱现场非现场监管一体化平台”、证监会”全市场交易监测系统”、外汇局”跨境资金流动监测分析系统”都是这一思路。工程影响:机构要具备按明细维度(客户、账户、商户、对手方)生成 T+0 直报包的能力,数据口径一致性比速度更关键。
直报接入的工程清单:
(报送类型, 业务日期, 机构代码, 序列号)。OFAC 的 “50% 规则” 是最容易被低估的工程难点:被制裁方直接或间接合计持有 ≥ 50% 的任何实体自动受制裁,即使该实体本身不在 SDN 列表上。合规筛查必须下钻到股权穿透。
一个最小图模型:
CREATE TABLE entity (
entity_id BIGINT PRIMARY KEY,
name VARCHAR(400) NOT NULL,
name_en VARCHAR(400),
country CHAR(2),
type VARCHAR(16) NOT NULL -- NP / LP / TRUST
);
CREATE TABLE ownership (
parent_id BIGINT NOT NULL, -- 持股方
child_id BIGINT NOT NULL, -- 被持股方
pct DECIMAL(6,3) NOT NULL,
as_of DATE NOT NULL,
source VARCHAR(64) NOT NULL, -- 工商/OpenCorporates/客户申报
PRIMARY KEY (parent_id, child_id, as_of)
);50% 规则的实现是一个带权图可达性算法。给定制裁种子 S,对每个其他节点 n 计算 S 对 n 的合并持股:沿多条路径求和,但环与相互持股必须处理。
def aggregated_ownership(edges, seeds, target, depth=8):
"""
edges: list[(parent, child, pct)] pct in [0,1]
seeds: set[节点ID] 被视为"受制裁"的起点
返回 seeds 对 target 的聚合持股比例 (0..1)。
用 DFS + memo,允许多路径相加,避免简单回环。
"""
adj = {}
for p, c, v in edges:
adj.setdefault(p, []).append((c, v))
memo = {}
def dfs(node, visited):
if node == target:
return 1.0
if depth_exceeded := len(visited) > depth:
return 0.0
key = (node, frozenset(visited))
if key in memo:
return memo[key]
total = 0.0
for child, pct in adj.get(node, []):
if child in visited:
continue
total += pct * dfs(child, visited | {child})
memo[key] = total
return total
return sum(dfs(s, {s}) for s in seeds)
edges = [(1, 2, 0.60), (1, 3, 0.30), (2, 4, 0.80),
(3, 4, 0.40), (4, 5, 0.70)]
print(aggregated_ownership(edges, seeds={1}, target=5))
# 1→2→4→5: 0.6*0.8*0.7=0.336
# 1→3→4→5: 0.3*0.4*0.7=0.084
# 合计对 4 的持股 = 0.48+0.12=0.60 ≥ 50%,4 自动被制裁
# 但若按"先对 4 聚合再向下"的口径,5 持股仅 0.60*0.70=0.42 < 50%实际合规实现会分层判定:先判定实体 4 是否被穿透制裁(≥50%),是则把 4 加入种子集,再向 5 穿透;而不是简单把所有路径乘法加到终端。这与 OFAC 官方 FAQ 401/402 的”一旦某实体被穿透,即整体视为 blocked”相符。工程师容易写错的正是这一步。
除名单与 PEP 外,不利媒体(Adverse Media / Negative News)是 EDD 必查项。工程流程:
陷阱:LLM 做 NER 容易”编造人物”;生产环境仍以 CRF/BERT-NER 结合规则为主,LLM 只做摘要与话题归类,保留原文引用。
持续监测不仅看交易,还看”客户画像 drift”:
| 信号 | 数据源 | 触发动作 |
|---|---|---|
| 职业变动 | 社交数据、年报披露 | 重新计算 PEP 评分 |
| 国籍/居住国变更 | 自助更新、证件续签 | 重新计算合规矩阵 |
| 股权结构变动 | 工商/OpenCorporates 变更订阅 | 重新 UBO 穿透 |
| 制裁名单新增 | 每日拉取 | 全量回溯筛查 |
| 交易偏离画像 | TMS 输出 | 风险评级上调 |
| 媒体负面 | 不利媒体扫描 | 人工介入 |
| 监管处罚 | 证监/人行/外管局处罚公告 | 二线复核 |
这些信号统一进入”客户信号总线”,任何一条都能触发
review_due_at = today。
一笔链上资金最终要经交易所上岸才能产生”洗白”效果。VASP 的 KYT(Know-Your-Transaction)默认要做:
Travel Rule 协议百花齐放,实际运营中头疼点:
为保证多协议互通,内部抽象出独立于协议的领域模型:
CREATE TABLE tr_message (
tr_id CHAR(36) PRIMARY KEY, -- 内部 UUID
direction VARCHAR(8) NOT NULL, -- OUT / IN
originator_vasp VARCHAR(64),
beneficiary_vasp VARCHAR(64),
asset VARCHAR(32) NOT NULL, -- BTC / ETH / USDT-ERC20
amount DECIMAL(38,18) NOT NULL,
tx_hash VARCHAR(128),
protocol VARCHAR(16) NOT NULL, -- TRP / TRISA / SYGNA / NOTABENE
payload_enc VARBINARY(8000) NOT NULL, -- IVMS101 加密
status VARCHAR(16) NOT NULL, -- PENDING / ACK / REJECTED / EXPIRED
created_at TIMESTAMP NOT NULL,
acked_at TIMESTAMP
);设计上要点:payload 以 IVMS101 结构加密存储,对端公钥来自对手方 VASP Directory;所有协议适配器解码后落同一张表,便于稽查与跨协议迁移。
AML 要求”知道你的客户并留存 5–10 年”,隐私法要求”最小必要、到期删除”。典型冲突与工程处置:
| 冲突点 | AML 立场 | 隐私立场 | 工程折中 |
|---|---|---|---|
| 证件号 | 留存 | 最小必要 | 明文加密 + 哈希索引;仅合规角色可解密 |
| 生物特征 | 留存比对记录 | 专项同意、分类保护 | 分桶存储,生物特征单独 KMS 与单独到期 |
| 交易明细 | 5+ 年 | “目的达成即删除” | AML 法律义务作为 GDPR Art.6(1)(c) 合法依据 |
| 跨境传输 AML 数据 | 集团内共享 | 跨境管控 | SCC/BCR、数据本地化镜像、加密后传输 |
| 被拒客户信息 | 内部黑名单用于再次识别 | 尽量删除 | 保留最小字段(姓名哈希 + 原因码 + 保留期) |
中国《个人信息保护法》第 13 条赋予”履行法定义务”作为合法性基础;欧盟 GDPR Art.6(1)(c)/(e) 同理。关键是把法律依据写进数据生命周期策略,而不是拍脑袋。
近年一批隐私增强技术(Privacy Enhancing Technologies,PET)开始在 AML 领域探索:
这些技术尚未普及,但在跨机构情报共享(例如 Project COSMIC 允许新加坡七家银行在高置信度阈值下共享可疑客户)中已有生产级试点。工程师应关注,但不应在主链路上引入未经监管认可的密码学方案。
多个司法辖区要求金融数据本地化:中国、俄罗斯、印度、印尼、越南、沙特。对跨国集团来说 AML 数据(尤其原始交易与 KYC 文档)必须留在本地;跨境共享需去标识化或走监管备案通道。工程上的落地:每个辖区一套独立数据底座,合规分析在本地完成,仅派生指标(红绿灯级别)回到集团总部。
供应商层:
自建 vs 外采:
上线清单(Go-Live Checklist):
AML 工程与反欺诈最大的差异是可解释性与可稽查性优先于准确率:一套”召回高但无法向监管解释”的模型在 AML 场景是不可部署的。把监管要求翻译为”标准化合规矩阵 + 可回放的规则 + 可审计的模型”,把客户信息、交易流水、链上证据、名单命中、案件叙事沉淀为”一键重现十年前”的档案结构,这些都是反洗钱系统长期演进的主线。名单刷新更快、模糊匹配更准、链上穿透更深、跨境协作更紧密——每一项都直接牵动成本、用户体验与合规风险的平衡。
对中国市场的从业者,2025 年修订的《反洗钱法》与人民银行 3 号令将会把”受益所有人识别”“特定非金融机构接入”“分级分类管理”推得更深;对出海企业,MiCA、AMLR 与各辖区 Travel Rule 协议的互操作是未来两年的核心工程议题。把合规当工程问题做,而不是当文书工作做——这是本篇希望传递的底层态度。
把当前热点继续串成多页阅读,而不是停在单篇消费。
2026-04-22 · architecture / fintech
面向中国工程团队的金融科技系列。从账务底盘、支付、清结算、交易所、风控合规到可靠性与灾备,中国与全球视角并举,讲清楚金融系统在工程落地中的真实挑战。
2026-04-22 · architecture / fintech
从工程视角拆解跨境支付的参与方、资金流、汇率、合规与对账:代理行与 SWIFT MT103/gpi、Nostro/Vostro 账户、Wise/Airwallex 的"本地收本地付"、FX 点差与锁定、稳定币与 CBDC 跨境(mBridge)、中国出海商户的持牌链路、AML/制裁名单工程。
2026-04-22 · architecture / fintech
金融科技(FinTech)不是普通后端加一张账户表。钱的原子性、监管的硬边界、一个小数点的代价,把这个领域推进到工程强度最高的那一档。本文是【金融科技工程】25 篇的总目录与阅读地图:先交代为什么它比一般业务系统更难,再给出对账体、支付体、交易体、风控合规体四维分类,把后续 24 篇挂到骨架上,最后给出一份绿地项目的落地顺序建议。
2026-04-22 · architecture / fintech
在代码里正确地表示"一笔钱"远比看起来难。本文系统梳理金额的数值建模(浮点、定点、Decimal、最小单位)、币种标准(ISO 4217)、本地化显示、汇率换算与数据库存储,并给出 Go、Python、Java、Rust 的工程化示例。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。