技术博客

日终批量处理(EOD)总览:清算、结算、核算的依赖顺序

第 15 / 141 篇 · 2026-09-11 · secfin · EOD批处理任务调度

A 股 15:00 收市,但券商系统的真正高峰在晚上。交易所和登记公司 17 点后陆续下发清算文件,券商要在当晚完成整套日终批量处理(End of Day,EOD):算清每个客户今天该收该付多少钱多少券、完成内部账务、和三家外部机构对平账、最后把系统切到下一个交易日。这条链上有上百个任务,几亿行数据,窗口只有几个小时,而且顺序错一步,后面的账全错。这篇给 EOD 一个总览:分哪几个阶段、依赖关系从哪来、编排上怎么保证。

五个阶段与它们的依赖

EOD 的主线是五个阶段,每个阶段的输出是下一个阶段的输入:

  1. 数据接收与检查:收交易所的成交回报文件、中登的清算文件/股份余额文件、存管银行的资金流水文件、行情收盘价文件。这一阶段的任务是文件级校验:日期对不对、批次齐不齐、记录数和文件头的控制数对不对。文件没到齐,后面一律不许跑——用半个交易日的数据清算,比不清算更糟。
  2. 清算:按清算文件逐客户计算应收应付资金和证券,轧差出净额。产出是客户级的清算结果(清算流水)和法人级的应收应付总额。
  3. 结算(交收):按清算结果实际动账——客户资金账户扣/加钱,持仓扣/加券,在途转可用。这是"清算算出来的数"真正落到六个余额上的时刻。
  4. 核算:把当天的业务流水转成会计分录,过科目账,跑试算平衡。产出会计凭证和科目余额,给财务报表和监管报送用。
  5. 对账与日切:三方对账(交易所/中登/银行)+ 内部账实核对,全部平了以后执行日切:系统日期推进到下一交易日,初始化新交易日的运行时状态(费率版本、涨跌停价、在途释放)。

EOD 主线:五个阶段的串行依赖文件接收交易所/中登/银行控制数校验清算逐客户算应收应付轧差出净额结算/交收余额与持仓落账在途转可用核算流水→会计凭证试算平衡对账日切三方对平切下一交易日依赖理由:清算依赖文件:清算文件是权威数据源,文件不齐清算结果是错的结算依赖清算:动账必须按清算净额动,先动账后算账等于裸奔核算依赖结算:会计凭证按已落账的流水生成,先记账后动账会出现账实不符日切依赖对账:账不平就切日,差错会被带进下一交易日,失去当日追平机会

顺序为什么不能乱

链路里每一段依赖都不是流程洁癖,是数据上的硬约束。

"先清算后结算"好理解:结算的每一笔动账都要引用清算流水号,没有清算结果就没有动账依据。反过来操作——先按成交回报实时把钱扣了,晚上再清算——听起来更实时,但实时扣的是"毛额",清算给的是"净额"(买入 100 万又卖出 80 万的客户,应收应付轧差后只动 20 万),两者金额口径不同,轧差清算那篇会算这个量级的差别。

"先结算后核算"容易被轻视。有团队觉得会计账反正是后台的事,可以先跑核算再跑结算。问题在于:核算的分录是按"已发生的资金/持仓变动"生成的,如果结算还没落账,凭证记的就是一笔尚未发生的业务,账实之间凭空多出一个时间差,对账系统会把这个时间差当差错挂出来。顺序保证了"先有事实,后有账目"。

"先对账后日切"是最后一道保险。日切是不可逆动作——系统日期一旦推进,今天账上的不平就只能作为"隔日差错"走红冲蓝补流程,成本翻倍。所以日切任务前面挂着全量对账的硬栅栏,不平就停链等人处理,这在清算差错处理那篇会展开成分级 SOP。

阶段内部:依赖图与栅栏

五个阶段是串行的,但阶段内部是高度并行的。清算阶段按账户哈希分片成几十个清算任务并行跑(账务性能那篇讲分片),但分片任务全部完成前,不能跑"法人汇总"这个下游任务——这就是栅栏同步。

整个 EOD 在工程上就是一张任务依赖图(DAG):节点是任务,边是数据依赖。调度器的工作是拓扑排序 + 栅栏管理:

  • 任务声明自己的前置依赖(depends_on: [clear_shard_*]),调度器只在前置全部成功后才放行;
  • 通配依赖(等所有分片)天然形成栅栏,栅栏点也是校验点的最佳挂载位:分片清算全完后校验"分片合计 = 文件控制数",过了才进汇总;
  • 失败任务默认阻断下游而不是跳过——EOD 里"跳过一个失败任务继续跑"几乎从来不是对的,跳过意味着拿残缺数据往下算。

调度器要拒绝两种诱惑。一是"为了赶时间并行跑有依赖的任务"——窗口紧的时候这是最诱人的错误,依赖顺序是正确性不是性能。二是"失败自动重试到成功"——数据类任务的失败多数不是抖动,重试一万次也是错,自动重试策略只对"文件未到"这类等待型任务有意义。

可重入:EOD 的保命属性

EOD 最现实的工程要求是可重入(rerunnable):链跑了一半挂了,修好问题后能从断点续跑,也能整个日期从头重跑,结果不变。这要求每个任务做到:

  • 产出按(交易日 × 任务)幂等:重复跑覆盖或拒绝重复,不允许叠加。清算流水表按交易日+流水号做唯一约束,重跑先删当日该任务的产出再插入;
  • 不依赖易失的中间状态:任务间通信全部走库表/文件,不走内存;
  • 动账类任务(结算)和校验类任务(对账)分开,重跑结算前先回滚当日已落账的结算流水,回滚本身也要留痕。

可重入不是可选项。中登文件重发(文件有差错会发修正版)、行情文件迟到、任务中途被运维误杀——这些在一年两百多个交易日里是必然事件,EOD 的健壮性就体现在"重跑一遍毫无心理负担"。

时间预算

最后说窗口。以中型券商为例,文件 17:30 左右到齐,第二天早上 8:30 柜面开门前必须全部完成且对平,理论窗口 15 小时,但实际给批量的窗口只有 4~6 小时——夜间还要留给数据备份、参数下发、外围系统(两融、资管、期权)各自的 EOD。清算性能优化那篇会讲怎么把 4 小时的批量压到 40 分钟,方法论就是在这张 DAG 上找瓶颈:IO、并行度、SQL 三个战场。

EOD 是券商核心系统里"批量思维"的极致:白天是低延迟的联机世界,晚上是高吞吐的批量世界,两个世界共享同一套账户数据,靠日切这个闸门隔开。理解了 EOD 的依赖顺序,就理解了为什么券商系统的架构长这个样子——后面清算、结算、对账各篇,都是在这条主线上取一段放大。