技术博客

日切(Cutoff)与交易日历:系统日、自然日、交易日的三套时间

第 16 / 141 篇 · 2025-06-11 · secfin · 日切交易日历时间模型

问一个简单的问题:周五晚上 22 点,客户通过手机银行转入一笔钱,这笔存款算哪天的?再问一个:周五 14:57 的卖出成交,资金哪天可以取?两个问题的答案都不是看墙上钟能答出来的,因为券商系统里同时存在三套时间:自然日(物理时间)、系统日(账务归属日)、交易日(交易所开市的日子)。很多账务 bug 的本质,是代码里这三套时间用混了。

三套时间各管什么

自然日:物理日历,决定"什么时候计息""什么时候开始算逾期"。利息、融资费用、合约期限都按自然日算——钱在账户里躺了几个 24 小时,就该计几天的息,跟交易所开不开市无关。

交易日:交易所定义的开市日,决定"T+N 的 N 是几天"。A 股卖出资金 T+1 可取,这个 1 是下一个交易日:周五卖出,下一个交易日是周一,所以周一可取;国庆前一天卖出,要等假期结束。所有交收类规则(T+1 可用、T+1 交割、新股中签缴款期限)的 T 都是交易日。

系统日(账务日/工作日期):券商系统内部"当前正在处理哪个营业日"的标志。它不等于自然日——15:00 收市后,物理时间还在今天,但交易系统已经在为"下一个交易日"接单(夜市委托);EOD 批量在日切完成前,账务上还停在"今天"。系统日是数据库里一个全局唯一的当前值,所有账务记录按它归属。

三套时间:周五 15:00 收市后的时间线周五 15:00周五晚 EOD周六/周日周一 9:15自然日周五 → 周六 → 周日 → 周一(物理流逝,计息按它)交易日周五收市 → 下一个交易日=周一(T+1 的 1 跨过了周末)系统日EOD 日切前=周五账务日;日切后=周一账务日(周末不切日)关键:日切完成后系统日直接跳到周一,周五晚~周一开盘之间进来的委托,账务归属全部记到周一的系统日(夜市委托/隔夜委托就是这么来的)

日切这个动作在做什么

日切(Cutoff)不是"把日期加一"这么轻描淡写,它是 EOD 链的最后一个任务,实际做这些事:

  1. 推进系统日:全局当前系统日从 $D$ 推进到下一个交易日 $D'$。注意是"下一个交易日"而不是"明天"——周五日切推进到周一,长假前日切推进到节后第一天。这一步的瞬间,所有联机交易的账务归属切换。
  2. 释放在途:上一交易日(或更早)沉淀的在途资金/持仓,凡是到期日的都转成可用。在途记录上都带着"预计可用交易日",日切时按交易日历匹配释放。
  3. 刷新日级参数:新交易日的涨跌停价(按新收盘价重算)、可充抵保证金折算率版本、费率版本、合约到期检查……日级参数以"生效交易日"为键,日切时把当日的参数集装载成当前集。
  4. 初始化运行时状态:清空前一交易日的临时表、重置当日序号发生器、加载新交易日的日历上下文。

日切是全局栅栏:它执行期间交易系统必须短暂停新委托(通常几秒到几十秒),因为"委托归属哪个系统日"不能有中间态。日切失败 = 第二天开不了市,所以它是 EOD 里监控等级最高的任务,也是为什么 EOD 总览里说"对账不平不许日切"。

交易日历:一张被严重低估的参数表

交易日历表(trading_calendar)字段朴素:日期、是否交易日、市场(沪深北/港股通各自的日历不同)、备注(节假日名)。但它是全系统的隐式中枢,依赖它的地方比想象多得多:

  • T+N 计算:所有"第 N 个交易日"的推算都要查它;
  • 日切目标日:推进到"下一个交易日"由它定义;
  • 结息与费用:计息区间按自然日,但"结息日遇非交易日顺延"这类规则要查它;
  • 报表口径:日报按系统日出,周报月报按交易日集合汇总。

工程纪律是:任何代码不许用"周末/法定节假日"的本地逻辑猜交易日。猜就会被临时调休打脸(调休工作日交易所不开市),被台风临时休市打脸(港交所经常,沪深也有先例),被国庆拼假打脸。交易日历是运营维护的权威数据,节前由交易所公告、运营人员提前录入复核,系统只查不算。沪港通/深港通还要同时维护两套日历,"内地开市但香港不开市"的日子港股通通道关闭,额度计算、交收安排全是按通道日历走的。

三套时间混用的经典事故形态

用自然日算 T+1。 周五卖出,代码写 available_date = today + 1,周六一早客户发现钱显示可取了——实际交收周一才发生,这笔钱取走就是券商垫资。正确写法永远是查交易日历取"下一交易日"。

夜市委托记错系统日。 日切后、开盘前客户下的隔夜委托,物理时间是周日晚 21 点,归属的系统日必须是周一。如果委托表用插入时的物理日期做账务日,周一的清算会漏掉这批委托,或者周五的账里混进周一的委托,两边都对不上。委托表必须显式存系统日字段,由日切后的全局值写入,而不是 sysdate。

计息区间跨日切漂移。 利息按自然日算,但余额取"哪个时点的余额"要按系统日定义(通常取每日日切完成后的时点余额)。计息任务如果在日切中途跑,取到的可能是新旧系统日混合状态的余额,积数就脏了。所以 EOD 总览里计息类任务的位置有讲究:要么严格在日切前(用今天的时点余额),要么严格在日切后(作为新一天的第一批任务),不许骑在栅栏上。

报表取数口径混用。 给监管的"当日成交日报"按系统日汇总,给客户的"收益曲线"按自然日展示,风控的"近 5 日换手率"按交易日窗口算——三个"日"不一样。需求文档里出现"近 N 日"三个字,第一件事就是问清是哪套日。

实现清单

  1. 全局系统日存库(单行表),所有账务归属读它,不用系统时钟;
  2. 交易日历按市场维护,节前双人复核录入,提供"下一交易日/第 N 交易日/区间交易日数"三个基础函数,全系统统一调用;
  3. 业务日期字段分三类命名(biz_date 系统日 / cal_date 自然日 / trade_date 交易日),代码评审见到裸用 sysdate 的账务逻辑直接打回;
  4. 日切任务全程留痕:切换前后值、执行耗时、释放的在途笔数,第二天运营早检的第一项就是看日切报告。

三套时间不是券商系统故意复杂化,是业务本身就在三个时间维度上运行:钱按物理时间生息,券按交易时间交收,账按账务时间归属。把三套时间建模清楚,是后面所有批量任务正确性的地基。