用订单事件重建最终状态
目标
仅根据订单事件日志重建每个订单的最终状态,找出与 OMS 状态字段不一致的订单,识别因排序时钟不同而结论反转的订单,并通过与顺序无关的规则消除这种波动。
为什么重要
在证券系统中,“订单状态”不是一个存储值,而是由多个事件折叠得到的结果。如果折叠规则没有明确规定,界面、通知和结算批处理就会各自采用不同规则进行折叠,从而得出不同答案。
本练习强制执行的四点都是实际现场规则。不要相信状态字段——处理器一旦漏掉某个事件,该订单的状态字段就会永久错误,而事件不会再次流入。区分撤单请求与撤单确认——如果在请求时就更改状态,请求后发生的成交会全部消失。将同一成交到达两次视为正常情况——重传不会消失,因此必须由接收方过滤;如果不去重,数量会悄无声息地膨胀。使答案不受顺序影响——与其精确猜中顺序,不如始终采用与顺序无关的规则。
本次事件如下。结算团队反馈:“已经收到成交通知,但 OMS 界面中有订单显示为已撤销。”对象是 2026-08-27 常规交易时段的 900 笔订单,我们掌握的资料只有一份事件日志和一份 OMS 状态快照。
重放规则如下。按时间升序读取事件(时间相同时按 seq 升序)。new 将状态设为 received 并确定订单数量。ack 改为 open,reject 改为 rejected,cancelled 改为 cancelled。replace 只更改订单数量,不修改状态。cancel_req 不做任何更改。对于 fill 和 partial_fill,先将自身数量加入累计成交量;如果累计值大于等于订单数量,则状态设为 filled,否则设为 partially_filled。
步骤
- 创建
/root/cap/order,原样运行答案中的生成器,生成/root/cap/order/events.jsonl和/root/cap/order/oms_status.csv。更改随机种子会导致结果与评分值不符。 - 在
/root/cap/order/shape.txt中写十行:events=、orders=,以及针对八种事件类型各写一行,例如type_new=形式的type_<종류>=。 - 按
ts_exch升序(相同时按seq)重放事件,生成/root/cap/order/final_exch.csv。表头为order_id,state,cum_qty,order_qty,每个订单一行,并按order_id升序排列。 - 对比
final_exch.csv和oms_status.csv。在/root/cap/order/omsdiff.txt中写四行:status_mismatch=、qty_mismatch=、any_mismatch=、qty_only_mismatch=;在/root/cap/order/omsdiff.csv中写入不一致的订单列表,表头为order_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty。 - 将同一日志按
ts_recv排序,生成/root/cap/order/final_recv.csv;在/root/cap/order/clock.txt中写入exch_filled=、recv_filled=、flipped=;在/root/cap/order/flipped.csv中按order_id,exch_state,recv_state表头写入状态反转的订单。 - 找出同一订单内相同
exec_id出现两次的情况,在/root/cap/order/dup.csv中按order_id,exec_id,dup_qty,dup_amount_krw写入,并在/root/cap/order/dup.txt中写入dup_orders=、over_orders=、dup_qty_total=、dup_amount_total=。 - 加入两项修正规则,生成
/root/cap/order/final_fixed.csv——使用exec_id过滤重复成交,并在重放结束后,如果累计成交量大于等于订单数量,就将状态固定为filled。在/root/cap/order/fixed.txt中写入flip_before=、flip_after=、final_filled=、final_cancelled=。 - 在
/root/cap/order/report.md中编写报告。需要## 무슨 일이 있었나、## 근거、## 돈으로 얼마인가、## 왜 이런 일이 생기나、## 무엇을 고쳐야 하나五个章节。
参考
- 所有金额均为整数韩元。若使用浮点数处理,总和会产生细微偏差,导致评分失败。
- 如果只使用一个排序键,同一时刻的两个事件每次都可能产生不同答案。始终按
(시각, seq)两个键排序。 - 常见错误 1:第 4 步只比较状态字符串。这样会停留在 27 笔,完全漏掉状态正确但数量缺失的 40 笔订单。
- 常见错误 2:第 6 步只统计累计成交量超过订单数量的情况。未超量的重复成交更危险——因为不会触发任何警报。
- 常见错误 3:第 7 步只过滤重复项,却保持原状态判断规则不变。这样仍会保留 40 笔发生反转的订单。
创建事件日志和 OMS 快照
创建 /root/cap/order,原样运行答案中的生成器,生成 /root/cap/order/events.jsonl 和 /root/cap/order/oms_status.csv。更改随机种子会导致结果与评分值不符。
使用 python3 原样运行生成器即可。更改随机种子会导致结果与评分值不符,因此请保持不变。
统计事件流的构成
在 /root/cap/order/shape.txt 中写十行:events=、orders=,以及针对八种事件类型各写一行,例如 type_new= 形式的 type_<종류>=。
按 type 分别计数即可。只有能够解释 ack 为什么不是 900,下一步才会更容易。
按交易所时间重放
按 ts_exch 升序(相同时按 seq)重放事件,生成 /root/cap/order/final_exch.csv。表头为 order_id,state,cum_qty,order_qty,每个订单一行,并按 order_id 升序排列。
先按 ts_exch 升序、相同时按 seq 升序排列事件,再依次折叠。只要记住 cancel_req 不改变状态,其余规则均按说明执行。
与 OMS 状态字段对比
对比 final_exch.csv 和 oms_status.csv。在 /root/cap/order/omsdiff.txt 中写四行:status_mismatch=、qty_mismatch=、any_mismatch=、qty_only_mismatch=;在 /root/cap/order/omsdiff.csv 中写入不一致的订单列表,表头为 order_id,replay_state,oms_status,replay_cum_qty,oms_filled_qty。
进行两次比较:只比较状态字符串时的数量,与连同成交数量一起比较时的数量并不相同。这个差值正是本步骤的重点。
按接收时间重新重放并比较
将同一日志按 ts_recv 排序,生成 /root/cap/order/final_recv.csv;在 /root/cap/order/clock.txt 中写入 exch_filled=、recv_filled=、flipped=;在 /root/cap/order/flipped.csv 中按 order_id,exch_state,recv_state 表头写入状态反转的订单。
使用与第 3 步相同的函数,只将排序键替换为 ts_recv。按订单比较两个结果的 state,只收集不同的订单。
找出同一成交到达两次的情况
找出同一订单内相同 exec_id 出现两次的情况,在 /root/cap/order/dup.csv 中按 order_id,exec_id,dup_qty,dup_amount_krw 写入,并在 /root/cap/order/dup.txt 中写入 dup_orders=、over_orders=、dup_qty_total=、dup_amount_total=。
检查同一订单内是否有相同 exec_id 出现两次。如果只统计累计成交量超过订单数量的情况,连一半都找不到。
加入不受顺序影响的规则
加入两项修正规则,生成 /root/cap/order/final_fixed.csv——使用 exec_id 过滤重复成交,并在重放结束后,如果累计成交量大于等于订单数量,就将状态固定为 filled。在 /root/cap/order/fixed.txt 中写入 flip_before=、flip_after=、final_filled=、final_cancelled=。
加入两项规则:使用 exec_id 过滤重复成交;重放结束后,如果累计成交量大于等于订单数量,就将状态固定为已成交。随后分别使用两个时钟重放,统计结果是否一致。
编写调查报告
在 /root/cap/order/report.md 中编写报告。需要 ## 무슨 일이 있었나、## 근거、## 돈으로 얼마인가、## 왜 이런 일이 생기나、## 무엇을 고쳐야 하나 五个章节。
需要五个章节。原因章节必须说明时钟和顺序,措施章节必须说明去重。数字应原样写入正文。