跳转至



量化数据的持久化方案


回测需要较长周期的数据。无论我们使用哪一家数据源,都不可能让我们跨网络、反复多次传输大量数据,还要保证低延时。此外,我们在量化研究的过程中,也会逐步积累自己的因子库。这些因子数据,甚至比原始数据的量还要大。比如,通过TA这个库,我们可以一次提取出86个左右的技术指标因子,这将是同频率OHLC数据的10倍之多。因此,如何高效率地存储和检索,就是一个比较有挑战性的问题。

本系列我们将考察一些流行的技术方案,比如关系型数据库、时序数据库、基于文件存储的方案、基于列的数据库和分布式方案等。很难说哪一种技术会绝对胜出,读者可以根据我们的评估和自己的需求进行选择。

我们需要什么样的数据处理能力?

量化数据主要是行情数据(以及由此衍生出来的因子数据)、财报(以及由此衍生出来的因子数据)、交易记录等。因此,它具有海量和PIT的主要特性。同时,一个完整的量化系统中,还必然存在交易信息、账号、回测状态等数据需要保存。

海量数据

存储量化需要的数据,并提供快速响应的检索,可能比我们多数人想像的要难很多。首先遇到的问题就是海量行情数据的问题。仅以A股为例,今天(2023年12月)A股共有5000家上市公司。如果我们要保存分钟级的行情数据的话,一天就需要保存 \(5000 * 240 = 120万\)记录。一年按250天算,这个记录数将达到3000万条。如果我们从股权分置改革那一年(2005年)1000家算起,假定每年上市公司的数量是匀速增加的,则从2005年到现在,我们需要保存\(3000,0000 * 18 * 0.6 = 3.24\)亿条。如果加上5分钟、30分钟等其它级别数据、加上指数数据,这个数据是轻松突破4亿条的。

如果我们要保存tick级别的数据,还要保存委买、委卖(5档)数据,数据量还要增加两个量级。

显然,这已经进入了大数据处理的范畴了。但是,如果用户只需要存储日线数据及这一级别的因子数据,那么,数据量则会小很多,甚至关系型数据库都能处理。

行情数据在读写上,有以下特点:

  1. 绝大多数是读请求
  2. 写请求(可以)以相当大的批次(>1000行)进行更新,甚至没有更新
  3. 对已添加到数据库的数据不需要修改(除非数据出错)
  4. 读取时,常常会读取相当多的行,但只提取列的一小部分
  5. 宽表(考虑到行情数据与因子连接的情况)
  6. 列中的数据相对较小,要么是数字,要么是很短的字符串
  7. 查询要求尽可能快

我们在后面可以看到,有一些数据存储方案几乎是完全按照这个要求来定制的。

PIT特性

PIT是指Point-in-time Data。有一些经济数据,在发布之后,往往还会进行修正。比如宏观数据中的GDP,公司财务报表等等。我们以公司财报为例,如果它在2021年1季末发布了财报1.0,在半年报发布时,对1季度的财报进行了修正,此时1季度的财报的版本应该是2.0。如果数据源在发布时,记录了数据版本和它生成的时间(而不仅仅是归属时间),这种数据就称为Point-in-Time data (PIT)。

如果选用关系型数据库,只要数据本身带有时间,则它的存储不是问题。如果是基于文件型的数据存储方式,则需要考虑恰当的索引方式,才能保存PIT属性。

Zillionare 2.0的数据存储方案

无论采用哪种落地技术,我们一般都会选择缓存+持久化的方案。在Zillionare 2.0中,当天的行情数据都保存在缓存中,更新和查询速度都很快,但容量小;另外,需要进行一些数据合成。比如,要查询某个标的过去250天的日线数据,我们需要先从持久化存储中,提取249天的行情数据,再加上存储在redis的当天的数据 -- 这个数据每分每秒都在更新 -- omicron在返回数据之前,进行数据合成,从而保证交给用户的数据是完整的。

Zillionare 2.0使用的缓存是Redis 7.0,使用的持久化存储是influxdb -- 这是最优秀的时间序列数据库。

Zillionare从上游数据源获取分钟数据,以及接近5秒左右的一个实时价格数据(不含open, high, low,只有当前价格)。Omicron对外提供5分钟,15分钟,30分钟,60分钟数据。因此,当Zillionare从上游数据源接收到分钟数据,并保存到redis中时,我们就需要利用最新的分钟线数据,将其合成为5分钟,15分钟,30分钟,60分钟数据及当日日线数据。这个任务量比想像的要大很多。实际上,它每5分钟都要运行一次,考虑到我们有6000个标的,因此,在5,15,30和60分钟会聚的时刻,一次性地要执行3万次的重采样任务 -- 还必须在几秒钟内完成。

我们利用了Redis 7.0的一个新技术,服务端lua脚本,来执行这个合成任务。不依靠python来完成这项任务,是这项任务能及时完成的关键。这省去了网格传输时间、同时lua也比python快很多 -- 它基本上是以c的速度在运行。

预告

我们并非只考察最强大的储存方案。实际上,最好的技术方案,是能满足需求的、性价比最高的那个方案。因此,我们会考察几乎所有可能的方案,以便读者根据的需要来进行选择。当然,Zillionare作为一个通用的量化框架,会选择最强大的方案,同时确保即使在个人机器上,也依然能够运行。

这些方案包括:

  • 只适合单人使用的存储方案
  • HDF5
  • ZARR
  • XArray
  • 只适合存储日线及以上级别的存储方案
  • Postgres
  • Mysql
  • 海量存储方案
  • influxdb
  • clickhouse
  • mongodb
  • 分布式计算方案
  • Ray
  • Dask