夏令时切换之后,两个市场的收盘落在了同一时刻
目标
用时区规则把四个市场的当地时间成交记录转换成 UTC,区分夏令时切换日的特殊时间,标出交易时段阶段和交易日标签,然后找出各市场收盘在 UTC 中重叠的日子,以及日历本身的矛盾。
为什么重要
如果用固定偏移量来做时间转换,不会出现异常。只是一个带着一小时偏差的值被存了下来,而这一事实,要等到切换周末过去、对账对不上的时候才会暴露。在资本市场里,这种偏差会直接触及收盘批处理和汇总。而且各市场的切换日期不同,每年总有几个星期,一边已经是夏令时,另一边还是冬令时。只有在这几个星期里,两个市场的收盘才会落在同一个瞬间,这种事是真的会发生的。
步骤
- 用
python3在/root/session/data中生成markets.json、calendar.json和trades.csv。 - 用时区规则把当地时间转换成 UTC,以
trade_id,market,utc_ms写入/root/session/normalized.csv。在该当地时间里不存在的时间不要放进去。 - 把同样的成交用固定的冬令时偏移量来转换,把结果不同的写入
/root/session/fixed_diff.csv。 - 把不存在的时间写入
/root/session/nonexistent.csv,把出现两次的时间写入/root/session/ambiguous.csv。 - 把每笔成交的交易时段阶段写入
/root/session/phase.csv,格式为trade_id,phase。 - 把日历日与交易日不一致的成交写入
/root/session/tradingday.csv。 - 把每个基准日各市场的常规收盘写入
/root/session/close_utc.csv,把收盘时间接近的市场对写入/root/session/overlap.csv。 - 把日历本身的矛盾写入
/root/session/calendar_defects.csv,并把前面各步骤的数量汇总到/root/session/summary.json。
参考
utc_ms是以 UTC 为基准的毫秒整数。出现两次的时间,采用fold为 0 的一方,也就是先经过的那个瞬间。- 第 3 步的冬令时偏移量,是该时区在 2025-01-15 12:00 的偏移量。不要把这个值写在代码里,请从时区中求出。
- 第 5 步的阶段有
pre、regular、post、closed和holiday五个。阶段包含起点,不包含终点。半日市只改变常规收盘,休市日要比其他任何情况都先判定。 - 第 6 步中,交易日可能与日历日不同的市场,在
calendar.json的overnight_markets里。在这些市场里,早于盘后阶段结束时间的成交,属于前一天的交易日。 - 第 7 步中,跨过午夜的交易时段的收盘在第二天。接近的标准是
calendar.json的overlap_minutes,市场名称按字典序靠前的一方写入market_a。 - 第 8 步的矛盾有
휴장일에반일장정의和반일장마감이정규보다늦음两种(这两个韩文名称依次意为“休市日定义了半日市”“半日市收盘晚于常规收盘”)。 - 常见错误 1:把不存在的时间直接转换后存储。不会出现异常,会变成一小时之后的值。
- 常见错误 2:把休市日判定放在交易时段阶段判定之后。休市日的成交会被分类为常规交易时段。
- 公开文档:IANA tz、zoneinfo、PEP 615、RFC 3339。市场名称和交易时段的时间是本实验的合成数据,只有时区标识符是真实的 IANA 名称。
生成四个市场的交易时段定义、日历和成交数据
用 python3 在 /root/session/data 中生成 markets.json、calendar.json 和 trades.csv。请原样使用不使用随机数的生成脚本。
不能直接使用客户数据,所以要做出形态相同的合成数据。不使用随机数,是为了无论谁运行多少次都得到同样的数据。评分器会把数据转换成标准形式来核对指纹,所以如果手工修改,后面的步骤就会全部被堵住。
用时区规则把当地时间转换成 UTC
在 /root/session/normalized.csv 中,首行写 trade_id,market,utc_ms,并把成交以 UTC 毫秒写出。在该当地时间里不存在的时间不要放进去。
用 zoneinfo.ZoneInfo 附加时区。要判断是不是不存在的时间,可以转换到 UTC 再回到同一个时区,看墙上时钟的值是否保持不变。出现两次的时间,采用先经过的一方。
用固定偏移量转换,找出出现偏差的日子
在 /root/session/fixed_diff.csv 中,首行写 trade_id,tz_utc_ms,fixed_utc_ms,diff_min,并写出用固定冬令时偏移量转换的值,与用时区规则转换的值不同的成交。
冬令时偏移量是该时区在 2025-01-15 12:00 的偏移量。不要把值写在代码里,请从时区中求出。diff_min 是用固定偏移量的值减去规则值得到的以分钟计的差。不存在的时间不放进这张表。
区分不存在的时间和出现两次的时间
在 /root/session/nonexistent.csv 中写入 trade_id,market,local_ts,在 /root/session/ambiguous.csv 中写入 trade_id,market,local_ts,first_utc_ms,second_utc_ms。
在春天把时钟拨快的市场里,被跳过的那一小时是不存在的。在秋天拨回去的市场里,同一个墙上时钟的值会经过两次。看 fold 为 0 与为 1 时的偏移量是否不同,就能同时抓住这两种情况,至于是其中哪一种,则用往返测试来区分。
为每笔成交标出交易时段阶段
在 /root/session/phase.csv 中,首行写 trade_id,phase,并为所有成交标上 pre、regular、post、closed、holiday 之中的一个。
最先判定休市日。然后按盘前、常规、盘后的顺序来看,哪里都不在,就是已收盘的时间。有半日市的日子,只改变常规收盘。阶段包含起点,不包含终点。
为跨过午夜的交易时段贴上交易日标签
在 /root/session/tradingday.csv 中,首行写 trade_id,calendar_date,trading_day,只写出两个值不同的成交。
有跨过午夜的交易时段的市场,写在数据里。在这些市场里,早于盘后阶段结束时间的成交,属于前一天的交易时段。其他市场始终与日历日相同。
把各市场收盘汇总成 UTC,找出重叠的批处理窗口
在 /root/session/close_utc.csv 中写入 market,date,close_utc_ms,在 /root/session/overlap.csv 中写入 date,market_a,market_b,gap_min。
基准日列表和接近的标准在数据里。处于休市日的市场,那一天没有收盘。跨过午夜的交易时段的收盘在第二天,所以日期必须加一天。市场对按字典序靠前的一方先写。
找出日历本身的矛盾,并汇总全部
在 /root/session/calendar_defects.csv 中写入 market,date,defect,并在 /root/session/summary.json 中汇总 trades、nonexistent、ambiguous、fixed_diff、phases、trading_day_differs、overlaps 和 calendar_defects。
矛盾有两种。同一天既是休市日又被写成半日市的情形,以及半日市收盘比常规收盘晚的情形。汇总中的数量不要用手数,请从与前面步骤相同的计算中取得。phases 是以阶段名称为键的计数对象。