对账与差额清算,以及对账抓不住的东西
目标
核对我方总账与对方机构的结算文件,将偏差缩小到逐笔交易;计算净额结算头寸;并找出原理上无法通过对账发现的重复转账。还要能将结果写成可发送到客户之外的形式。
为什么重要
对账是 FDE 在银行现场最常进行的工作,也是最常出错的工作。
第一个陷阱是对账对象。我方总账中混有仅授权但尚未完成清算的交易,这些交易不出现在对方结算文件中是正常的。如果不区分状态就进行对账,数十笔正常交易会被列为异常,真正的偏差反而被淹没其中。
第二个陷阱是如何解读偏差。必须分别查看笔数和金额。如果笔数相同而只有金额不同,说明双方对同一笔交易记录了不同金额;如果金额相同而只有笔数不同,说明一方合并或拆分了交易。原因不同,因此向客户提报的渠道也不同。
第三点最为重要。对账询问的是“两本账是否相同”,而不是“账本是否正确”。 如果超时后重发导致同一笔钱转出两次,我方总账和对方文件中都会同样留下两笔记录。总额对账会完美通过。唯一能够发现问题的位置是幂等键。
本次事件如下。收到 2026-08-25 的结算文件后,有人反馈总额不一致。共有三家机构,其中两家以不同方式出现偏差。
步骤
- 创建并运行
/root/bank/gen_settle.py,生成/root/bank/settle.db和/root/bank/partner_20260825.csv。 - 将结算文件导入
settle.db的partner表,并在/root/bank/recon_totals.csv中输出按机构汇总的总计。表头为counterparty,our_count,our_amount,partner_count,partner_amount,count_gap,amount_gap,我方只统计state为SETTLED的交易。 - 分别将
/root/bank/only_ours.csv和/root/bank/only_partner.csv按msg_id,counterparty,amount表头输出,并在/root/bank/side_counts.txt中写三行:only_ours=、only_partner=、naive_only_ours=。最后一个值表示不区分state进行对账时的数量。 - 将金额不同的交易输出到
/root/bank/amount_mismatch.csv,表头为msg_id,our_amount,partner_amount,gap。 - 在
/root/bank/dup_msg.csv中按idem_key,msg_id,amount表头保留与重复幂等键关联的所有报文,并在/root/bank/dup.txt中写三行:dup_keys=、dup_msgs=、extra_amount=。 - 在
/root/bank/net_position.csv中按counterparty,out_amount,in_amount,net表头输出各机构的净额结算头寸。net为收款减去付款。 - 在
/root/bank/pending.txt中写六行:auth_count=、auth_amount=、auth_out_amount=、auth_in_amount=、stale_auth=、suspense_account=。stale_auth是sent_at早于2026-08-22的授权交易,suspense_account是暂收款科目代码2210。 - 在
/root/bank/recon_report.md中按## 대사 결과 요약、## 건수는 같은데 금액이 다른 건、## 금액은 같은데 건수가 다른 건、## 대사로는 잡히지 않는 것、## 차액정산 포지션、## 개인정보 취급六个章节编写报告。账号只保留前 3 位和后 4 位,按110-****-**1234的形式脱敏;不得以任何形式写入居民登记号码和客户真实姓名。
参考
- 若要使用 SQL 处理 CSV,请先创建表再导入:
CREATE TABLE partner(...)后执行.import --csv --skip 1 <파일> partner。 - 同一个文件导入两次会使行数翻倍。导入前加入
DROP TABLE IF EXISTS partner,即可保证无论运行多少次结果都相同。 - 重复转账的超额部分通过
SUM(amount * (건수 - 1))计算。第一次发送是正常的,因此不应扣除。 - 常见错误 1:第 3 步不按
state缩小范围。这样偏差看起来不是 2 笔,而是 18 笔。 - 常见错误 2:第 5 步按
msg_id查找重复。每次重发都会分配新的报文编号,因此不会找到任何结果。 - 常见错误 3:第 8 步因为在调查画面中看到了账号,就将其原样写入报告。在报告中留下不必要的个人信息,本身就是事故。
创建我方总账与对方结算文件
创建并运行 /root/bank/gen_settle.py,生成 /root/bank/settle.db 和 /root/bank/partner_20260825.csv。
使用 python3 创建 /root/bank/settle.db 和 /root/bank/partner_20260825.csv。259 笔报文中有 16 笔仅处于授权状态。
导入结算文件并核对各机构总计
将结算文件导入 settle.db 的 partner 表,并在 /root/bank/recon_totals.csv 中输出按机构汇总的总计。表头为 counterparty,our_count,our_amount,partner_count,partner_amount,count_gap,amount_gap,我方只统计 state 为 SETTLED 的交易。
将对方文件导入 settle.db 的 partner 表后,可以用一条 SQL 完成对账。我方只统计 state 为 SETTLED 的交易,并分别查看笔数和金额。
筛出仅存在于一方的交易
分别将 /root/bank/only_ours.csv 和 /root/bank/only_partner.csv 按 msg_id,counterparty,amount 表头输出,并在 /root/bank/side_counts.txt 中写三行:only_ours=、only_partner=、naive_only_ours=。最后一个值表示不区分 state 进行对账时的数量。
对账对象仅限已完成清算的交易。仅授权交易不出现在对方文件中是正常的;如果不按 state 缩小范围,16 笔正常交易都会被列为异常。
找出金额不同的交易
将金额不同的交易输出到 /root/bank/amount_mismatch.csv,表头为 msg_id,our_amount,partner_amount,gap。
连接双方都存在的报文编号,只保留金额不同的记录。gap 为我方金额减去对方金额,其总和应与机构汇总中的金额差相同。
通过幂等键查找重复转账
在 /root/bank/dup_msg.csv 中按 idem_key,msg_id,amount 表头保留与重复幂等键关联的所有报文,并在 /root/bank/dup.txt 中写三行:dup_keys=、dup_msgs=、extra_amount=。
按幂等键而不是报文编号分组。如果同一幂等键关联两条以上报文,说明重发被重复处理。超额部分是每个键保留一笔后剩余的交易。
计算净额结算头寸
在 /root/bank/net_position.csv 中按 counterparty,out_amount,in_amount,net 表头输出各机构的净额结算头寸。net 为收款减去付款。
按机构分别汇总付款(OUT)和收款(IN),net 为收款减去付款。对象是已完成结算的交易。
统计未结算项和暂收款余额
在 /root/bank/pending.txt 中写六行:auth_count=、auth_amount=、auth_out_amount=、auth_in_amount=、stale_auth=、suspense_account=。stale_auth 是 sent_at 早于 2026-08-22 的授权交易,suspense_account 是暂收款科目代码 2210。
按方向分别统计仅授权的交易,并单独统计授权日期较早的交易。未结算项应按账龄管理,而不是只看笔数。
编写脱敏后的对账报告
在 /root/bank/recon_report.md 中按 ## 대사 결과 요약、## 건수는 같은데 금액이 다른 건、## 금액은 같은데 건수가 다른 건、## 대사로는 잡히지 않는 것、## 차액정산 포지션、## 개인정보 취급 六个章节编写报告。账号只保留前 3 位和后 4 位,按 110-****-**1234 的形式脱敏;不得以任何形式写入居民登记号码和客户真实姓名。
需要六个章节。而且该文档会发送到客户之外——账号必须脱敏,居民登记号码和真实姓名则完全不要写入。