撤销通知来了两次,持仓翻成了反方向
目标
把对已经发出的成交的撤销和更正通知,与原成交连起来,不擦除原账簿,而是追加冲销记录,做出新的账簿。重新计算持仓并与已经发出的报告对照,确认即使两次收到同一条通知,账簿也相同,然后给出重新报告对象和更正报告。
为什么重要
成交账本是已经流向很多地方的数据。如果悄悄改写其中的一行,昨天发出的报告与今天看到的账本就会对不上,而对不上的原因,哪里都留不下来。所以金融账簿是往后写的。撤销,是追加符号相反的冲销记录;更正,是冲销之后,再以修正后的值重新写一遍。困难的不是这条规则本身,而是边缘情况。通知可能指向并不存在的成交,同样的通知可能因重发而到来两次,更正也可能落到已经被撤销的成交上。只要漏掉一个,持仓就会翻到反方向,而这件事通常要到第二天报告出错之后才会暴露。
步骤
- 用
python3在/root/bust/data中生成fills.jsonl、notices.jsonl、position_report.csv和rules.json。 - 按到达顺序读取通知,把判定写入
/root/bust/notice_match.csv。 - 把要应用的撤销作为冲销记录追加进去,生成
/root/bust/ledger_bust.jsonl。 - 把更正作为冲销和重新记账追加进去,生成
/root/bust/ledger_final.jsonl,并把按订单统计的平均单价输出到/root/bust/order_avg.csv。 - 重新计算持仓,只把出现偏差的标的写入
/root/bust/position_diff.csv。 - 把同一个通知数据流放进去两次,生成
/root/bust/ledger_twice.jsonl和/root/bust/idem.json。 - 把报告时间之后到达的通知收集起来,生成
/root/bust/rereport.csv。 - 把每一处变更的来源写入
/root/bust/trace.csv,把整体摘要写入/root/bust/report.json。
参考
- 通知的
seq就是到达顺序。判定要按这个顺序进行,必须记住已应用的撤销,才能区分重复撤销。 - 判定名称有四个。前面已经见过的同一个通知标识符是
중복통지,所指向的成交在账本中没有是대상없음,所指向的成交在前面已经被撤销的是이미취소됨,其余的是적용(这四个韩文判定名依次意为“重复通知”“无对象”“已被撤销”“适用”)。按这个顺序来检查。 - 账簿记录有十一列。
entry_id、kind、fill_id、order_id、symbol、side、qty、px、notional、source_notice、entry_time。kind是fill、reversal、rebook三种。 - 冲销记录的
entry_id,是在通知标识符后面加上-rv,重新记账则加上-rb。entry_time是通知时间。 - 如果更正通知的
new_qty和new_px是null,这个值就原样使用原成交的值。 - 金额用
decimal计算,在小数点后第四位用ROUND_HALF_UP舍入。请写成字符串。 - 持仓是买入相加、卖出相减来计算的。冲销记录的数量是负数,所以符号会自动颠倒。
- 数据文件
rules.json中有持仓报告时间。不要用date来取今天。 - 常见错误 1:修改原记录来作更正。必须保留应用之前的数据,才能比较前后。
- 常见错误 2:没有记住通知标识符,使重发的撤销被冲销两次。
- 公开文档:FIX Standards、Fiximate、decimal。十八笔成交和十四条通知,是本实验的合成数据。
生成成交账本、撤销与更正通知、持仓报告
用 python3 在 /root/bust/data 中生成 fills.jsonl、notices.jsonl、position_report.csv 和 rules.json。请原样使用不使用随机数的生成脚本。
不能直接拿来客户的成交账本,所以要做出形态相同的合成数据。不使用随机数,是为了无论谁运行多少次都得到同样的数据,才能互相对照判定。评分器会把数据转换成标准形式来核对指纹,所以如果手工修改,后面的步骤就会全部被堵住。
把通知与原成交连起来作判定
在 /root/bust/notice_match.csv 中,首行写 seq,notice_id,type,target_fill_id,verdict,按到达顺序读取通知,写入 적용、대상없음、이미취소됨、중복통지 之中的一个(韩文判定名依次意为“适用”“无对象”“已被撤销”“重复通知”)。每条通知写一行。
按这个顺序看四件事。前面是不是已经见过同样的通知标识符,所指向的成交在账本里有没有,那笔成交前面是不是已经被撤销,其余的就是应用。每次应用撤销时,都要记住这个成交标识符,第三项检查才能进行。通知标识符不论判定如何,全部都要记住。
把撤销作为冲销记录追加,做出新账簿
在 /root/bust/ledger_bust.jsonl 中,把十八行原成交作为 kind 为 fill 的记录原样搬过来,并为每个要应用的撤销,追加数量和金额符号相反的 reversal 记录。不要修改原记录。
不擦除,而是追加。冲销记录的数量,是原成交数量加上负号,单价与原成交相同。这样金额也会自然变成相反的符号。entry_id 是在通知标识符后面加 -rv,并在 source_notice 里留下那条通知,日后就能追溯来源。原成交记录的 source_notice 是空字符串。
反映更正并重新得出平均单价
/root/bust/ledger_final.jsonl 是在第 3 步的账簿上,为每个更正追加 reversal 和 rebook 两行。在 /root/bust/order_avg.csv 中,首行写 order_id,net_qty,net_notional,avg_px,每个订单写一行。
更正是先冲销原记录,再以修正后的值重新写一行。通知中没有的值,请原样使用原成交的值。只改价格的更正,数量相同,只有金额不同。平均单价是账簿的金额之和除以数量之和,被全部撤销的订单,数量之和为 0,所以置为 0。除之前先看是不是 0。
重新计算持仓并与已有报告对照
在 /root/bust/position_diff.csv 中,首行写 symbol,reported_qty,recomputed_qty,diff,只写用最终账簿重新计算的持仓与 position_report.csv 不同的标的。
持仓是买入相加、卖出相减来计算的。冲销记录的数量是负数,符号会自动颠倒,所以不需要另外分支。diff 是用重新计算的值减去报告的值。值相同的标的不写。差别并不全都能用迟到的通知来解释,所以即使有解释不了的标的,也请保持原样。
测试两次收到同一条通知时账簿是否相同
在通知数据流之后,再原样接上一遍同样的通知(第二批的 seq 加 1000),生成 /root/bust/ledger_twice.jsonl,并在 /root/bust/idem.json 中写入 entries、same_as_final 和 ignored_duplicates。
重发是正常的事。如果不记住通知标识符,已经被撤销过一次的成交就会被冲销两次,持仓会翻到反方向。第二批的标识符全部都是前面见过的,所以一条都不能被应用,这样账簿就会与第 4 步的完全相同。ignored_duplicates 是在接起来的整个数据流中,被判定为重复通知的通知数,same_as_final 是真或假。
把报告之后到达的通知收集成重新报告对象
在 /root/bust/rereport.csv 中,首行写 notice_id,type,target_fill_id,symbol,position_delta,写入已应用的通知中,通知时间晚于 rules.json 的持仓报告时间的那些。
在持仓报告发出之后到达的通知,只修改账簿是不够的,它会让已经发出的报告变成错的。基准时间在 rules.json 里,所以不要用今天的日期。position_delta 是这条通知使持仓变动了多少,卖出的符号相反。只改价格的更正不触碰数量,所以是 0,但不要把它从重新报告对象中去掉。
撰写更正报告和变更追溯表
在 /root/bust/trace.csv 中,首行写 entry_id,kind,fill_id,source_notice,reason,qty,px,notional,写入最终账簿中 kind 不是 fill 的全部记录,并在 /root/bust/report.json 中写入整体摘要。
接收的人必须能决定下一步做什么,报告才有用。给每个变更附上它来自哪条通知,以及该通知的原因,几个月之后也能追溯。reason 从通知数据中取,但必须是实际被应用的通知的。用同一个标识符重发的副本原因不同,就是在这里出现分歧。要放进摘要的键有 fills、notices、적용、대상없음、이미취소됨、중복통지、bust_applied、adjust_applied、entries_before、entries_after、position_mismatch 和 rereport(其中四个韩文键就是判定名称)。