做一个反洗钱规则引擎
目标
使用 SQL 和 Python 实现五条可疑交易检测规则,计算每位客户的预警分数,并仅依据事实,为分数最高的客户撰写可疑交易报告草稿。
为什么重要
反洗钱(AML)是银行 IT 最常遇到的监管业务之一。法律规定银行承担两类报告义务:一天内现金往来达到一定金额时,无论其他情况如何都要报告的大额现金交易报告(CTR);以及不论金额多少,只要怀疑存在洗钱行为就要提交的可疑交易报告(STR)。CTR 是机械式判定,而 STR 需要作出判断。规则引擎就是帮助完成这项判断的工具。
规则引擎并不是什么华丽的模型,而是由一组规则组成,例如:“单日现金总额是否超过阈值”“是否多次拆分成略低于阈值的金额”“是否与高风险国家发生交易”“客户身份核验是否已过去很久”“长期不活跃的账户是否突然转移大额资金”。单条规则会产生很多误报,但如果同一个人同时命中多条规则,就会构成信号。因此,需要为每条规则赋予权重并求和。
这项工作还伴随两项约束。报告中只写事实——交易编号、金额、日期和命中的规则。“可疑”不是结论,而是需要由证据支撑的判断。此外,不得告知客户已经提交报告。这是法律规定的保密义务,系统设计必须确保这一事实不会泄露到渠道界面或客户通知中。
本实验的规则(基准日为 2026-08-31)
- R1 大额现金:同一客户在同一天、以相同方向(存款
CASH_DEP或取款CASH_WDR)发生的现金总额达到 10,000,000 韩元或以上。权重为 1。 - R2 疑似拆分:一位客户的现金交易中,金额达到 8,000,000 韩元但低于 10,000,000 韩元的交易有 3 笔或以上。权重为 3。
- R3 高风险国家:
cp_country存在于high_risk_country表中的转账。权重为 2。 - R4 客户核验超期:
risk_grade为HIGH或pep为 1,且距kyc_date已过去 365 天或以上。权重为 1。 - R5 休眠后活动:8 月的首个交易日距
last_activity已过去 180 天或以上,且该笔首笔交易金额达到 5,000,000 韩元或以上。权重为 2。 - 分数等于所命中规则的权重之和。规则名称应写成
R1+R3这样的形式,按编号顺序用+连接。
步骤
- 将右侧示例中的生成器保存为
/root/bank/make_aml.py,并运行python3 make_aml.py。aml.db中会写入 20 位客户、124 笔交易和 3 个高风险国家。 - 将 R1 的结果写入
/root/bank/ctr.csv,表头为cust_id,biz_date,direction,cash_total。direction为DEP或WDR。 - 将 R2 的结果写入
/root/bank/structuring.csv,表头为cust_id,txn_count,cash_total,first_date,last_date。 - 将 R3 的结果写入
/root/bank/highrisk.csv,表头为txn_id,cust_id,type,cp_country,amount。 - 将 R4 的结果写入
/root/bank/kyc_due.csv,表头为cust_id,risk_grade,pep,kyc_date,days_since;将 R5 的结果写入/root/bank/dormant.csv,表头为cust_id,last_activity,first_txn_date,dormant_days,amount。 - 将至少命中一条规则的客户写入
/root/bank/alerts.csv,表头为cust_id,rules,score;按分数降序排列,分数相同时按cust_id升序排列。 - 将分数最高客户的报告草稿写入
/root/bank/str_<cust_id>.json。键包括cust_id、rules(列表)、score、txn_ids(该客户命中规则的交易编号列表)、total_amount(这些交易的金额总和)、narrative(至少 100 字,只写事实)、prepared_by(填写角色而非人名)和customer_notified(必须为false)。 - 在
/root/bank/aml_report.md中撰写报告,包含## Detection rules and results(检测规则与结果)、## Reporting targets(报告对象)、## False positives and limitations(误报与局限)、## Confidentiality and personal information(保密与个人信息)四个章节。
参考
- 使用
substr(ts, 1, 10)只截取日期,即可按天汇总。julianday('2026-08-31') - julianday(kyc_date)是经过的天数。 - 每条规则使用一条 SQL 就足够了。第 6 步需要合并五项结果,使用 Python 更方便。
- 常见错误 1:在 R1 中把存款和取款合并计算。必须按方向分别计算。
- 常见错误 2:把 R5 的首笔交易当成 8 月全月金额最大的交易。这里要求的是 8 月的首笔交易。
- 常见错误 3:在报告中填写客户真实姓名或账号。连
name_masked都不需要——客户编号已经足够。
生成客户与交易数据
将右侧示例中的生成器保存为 /root/bank/make_aml.py,并运行 python3 make_aml.py。aml.db 中会写入 20 位客户、124 笔交易和 3 个高风险国家。
请将实验说明中的生成器保存为 /root/bank/make_aml.py,并使用 python3 运行。如果手工修改,后续步骤中的所有数字都会出现偏差。
R1 大额现金——按单日总额计算
将 R1 的结果写入 /root/bank/ctr.csv,表头为 cust_id,biz_date,direction,cash_total。direction 为 DEP 或 WDR。
只筛选现金类型,按 cust_id、substr(ts,1,10) 和方向执行 GROUP BY,再添加 HAVING SUM(amount) >= 10000000。存款和取款分别汇总,合计正好为 1,000 万韩元的日子也包括在内。看的不是单笔,而是当天的合计。
R2 疑似拆分——统计略低于阈值的交易
将 R2 的结果写入 /root/bank/structuring.csv,表头为 cust_id,txn_count,cash_total,first_date,last_date。
筛选现金类型且 amount 达到 8,000,000、低于 10,000,000 的交易,按 cust_id 分组,只保留 COUNT(*) >= 3 的客户。日期使用 MIN/MAX 提取。
R3 高风险国家——与清单核对
将 R3 的结果写入 /root/bank/highrisk.csv,表头为 txn_id,cust_id,type,cp_country,amount。
按 cp_country 对 txn 和 high_risk_country 执行 INNER JOIN 即可。没有金额条件。汇往 AE 的 2,000 万韩元交易不应命中。
R4·R5——客户核验超期与休眠后活动
将 R4 的结果写入 /root/bank/kyc_due.csv,表头为 cust_id,risk_grade,pep,kyc_date,days_since;将 R5 的结果写入 /root/bank/dormant.csv,表头为 cust_id,last_activity,first_txn_date,dormant_days,amount。
R4 仅使用 customer 即可:(risk_grade='HIGH' OR pep=1) AND julianday('2026-08-31')-julianday(kyc_date) >= 365。R5 先按客户求出 MIN(ts)(CTE),再重新连接该笔交易。休眠时间虽长但交易金额较小的客户不应命中。
合并规则并计算预警分数
将至少命中一条规则的客户写入 /root/bank/alerts.csv,表头为 cust_id,rules,score;按分数降序排列,分数相同时按 cust_id 升序排列。
从第 2~5 步的五个 CSV 中收集 cust_id,建立每位客户的规则集合,再加上权重(R1 1、R2 3、R3 2、R4 1、R5 2)。rules 按编号顺序用 + 连接;排序方式为分数降序、cust_id 升序。
仅依据事实撰写可疑交易报告草稿
将分数最高客户的报告草稿写入 /root/bank/str_<cust_id>.json。键包括 cust_id、rules(列表)、score、txn_ids(该客户命中规则的交易编号列表)、total_amount(这些交易的金额总和)、narrative(至少 100 字,只写事实)、prepared_by(填写角色而非人名)和 customer_notified(必须为 false)。
目标是 alerts.csv 的第一行。按该客户命中的每条规则收集相应交易(R1 为当天的现金交易,R2 为 800 万–1,000 万韩元的现金交易,R3 为高风险国家交易,R5 为 8 月首笔交易),去重后排序。没有命中规则的交易不要放进去。使用 json.dump 写入;customer_notified 在 Python 中为 False。
撰写 AML 检查报告
在 /root/bank/aml_report.md 中撰写报告,包含 ## Detection rules and results(检测规则与结果)、## Reporting targets(报告对象)、## False positives and limitations(误报与局限)、## Confidentiality and personal information(保密与个人信息)四个章节。
把 task 中的四个章节标题(反引号中的英文标题,或括号中意思相同的中文标题;韩文、日文标题也可以)分别写成以 ## 开头的一行。在检测规则章节中以“N 位”的形式写入预警客户数,在报告对象章节中写入最高分客户编号,在保密章节中写明不把报告事实告知客户的句子(no tipping-off——例如:不得告知客户 / the customer is not notified)。不要填写真实姓名。