- 使用C语言等替代关键函数。如指标库TA-Lib就是用C语言实现的
- 减少跨语言调用次数。循环调用talib.SMA,还不如调用一次bottleneck.move_mean
- 易用性上,numba优于Cython。因为Cython对不同Python版本不同操作系统要分别编译,过于复杂
- 长表
- 如同数据库,每一列为一个字段,如开高低收。必定有两个关键的列做为索引,一列表示股票代码,另一表示时间
- 两个索引中有大量重复值,如果只存一个字段利用率只有1/3,得存多个字段才经济
- 两个关键字段最好如同数据库一样建B+树索引,否则这两个字段可能因数据太多无法加载到内存
- 两个索引的排序先后也对速度有影响。
- 先时间分组。真实场景。数据也是按时间生成的。适合做横截面,比如同一天所有股票的涨跌幅排序
- 先股票分组,组内时间排序。适合历史分析。可以快速将一指股票取出,NaN也提前就drop了,计算技术指标也方便
- 复合索引存一个文件,其它每个字段一个文件,添加新字段时速度快
- 宽表
- 整表为一个字段,例如都为收盘价,列头为股票代码,索引为时间
- 由于上市时间有先后,二维矩阵会出现大量NaN,停牌也会出现NaN,空间利用率低
- 停牌中段出现的空值对talib这类的指标计算有影响,必须特别处理才行
- 整块存储时,一支股票为一列,这一列均匀分布在文件中,为了加载这列股票,整个文件都必须加载,超大文件时根本不可行
- 列式存储时,按整表取出,底层4000多支股票合并会占用大量空间和时间
- 两个索引一个文件,其它二维数据一个文件
- 股票支数太多4000多支,talib至少要调用4000多次,单核很难在3s内下次行情推送过来前计算完成
- 由于GIL的原因,IO密集型可以用多线程,而计算密集型只能用多进程
- 多进程最大的问题是数据跨进程。数据序列化和反序列化才能传给子进程,而数据通常几个G,序列化不现实
- 共存内存和内存文件映射是一个不错的方案
- 宽表。大文件会被全量加载,内存吃紧
- 长表。索引需要一个好的机制
- 无论哪种方案都对大数据量都比较麻烦
- 由于NaN处理实在麻烦
- 二维计算对各种指标库要求太高。
所以还是用长表,然后配合groupby比较方便,
- 算指标时按股票代码分组,按时间排序。为了加速,最好每天收盘后就做好排序整理
- 算横截面时按时间分组
指标计算简单了,但只利用到了单核,计算还是慢。文件还大。多进程时又受限于数据序列化
- 水平品种分割
- 4000支股票放在一个进程中处理不过来,放在4000多个文件也打开处理很慢。应当分组存放
- 通过股票代码后一位,可以均匀分布10个文件,10个进程分别处理
- 根据数据量的大小和CPU核数,可以最后两位分成100个,或100%5分成20个,需要进行取舍
- 垂直时序分割
- 数据长计算久,历史数据可以长数据,但对实时性要求高的部分则最好能计算最新部分的一小段
- 日线可以按年划分,分钟线可以按月划分
- 由于指标需要预加载部分数据,所以数据需要一段前一时段的行情
- 横截面处理
- 在4000支股票的长表数据分割到10个文件前可以进行基于基础行情的横载面的指标
- 如果已经划分到10个文件了,只能合并10个文件才能进行横载面计算,横截面处理的机会目前不多
- 按天下载全部A股数据,比按支下载4000多次要快。所以数据原始格式为按天排序的长表
- 按年将表加载,然后统一表头,为今后可能切换数据源做预留
- 可以进行初步的横截面计算
- 股票按股票代码最后的数字进行分成10个文件,可以同时做一下
- 多进程对10个文件进行时序指标的计算
- 再考虑是否要合并计算横截面