TDengine在弘源泰平量化投资中的实践

370 阅读5分钟

公司简介

深圳市弘源泰平资产管理有限公司组建于2016年,团队核心成员来自于知名高校,有丰富的资产配置与策略构建的实践经验。弘源泰平以套戥交易绝对收益型配置工具为起点,致力于为用户提供流动性好、费率公允的资产配置工具。产品线全面、丰富,涵盖股、债、商品等各大类资产,通胀、趋势等各类因子。

场景简介+核心诉求

我们的量化交易系统每天要接收大量的行情数据,也要基于行情产生大量的决策信号。这些数据都需要及时存下来,供盘中和盘后使用。

传统存放行情数据的方式有文件系统、关系型数据库或者文档数据库。我们尝试了MySQL和知名的时序数据库InfluxDB,但是性能都没有达到预期。分别遇到了如下问题:

  • MySQL:在写入大量实时的时序数据时,性能不理想;即便是优化之后,对于资源的浪费仍然十分惊人。而且随着数据量的增加,对设备数据的实时查询、时间范围分析的需求增加,基于MySQL的查询分析操作,响应时间会越来越长,甚至会无响应。缺乏自动建表功能,使用很不方便。
  • InfluxDB:虽然是时序数据库,但是经过测试后性能不能满足预期,在完成同样数据量的写入时对于资源的使用程度也并不能令人满意。

最后,我们改用TDengine彻底解决了实时写入大量数据点和快速查询的问题。

TDengine具体落地

对于策略研究员而言,历史行情和信号是交易策略研究的重要素材。下面以行情数据和策略信号数据为案例予以介绍。

数据建模

首先,将行情数据和信号数据分别存储。在TDengine中分别创建了一个行情数据库和信号数据库。

虽然是时序数据库,但是TDengine使用了关系型数据库的模型,建库,建表,使用SQL,十分便于传统关系型数据库的用户入手。并且,他们还很有创意地设计了超级表的概念,与我们的场景十分契合。

因为所有行情数据结构相同,行情库中只需要一个超级表,下面每个工具(衍生品基金等)对应一个子表。比如CU2101表示2021年1月份到期的铜期货交易合约。在合约到期之前,都会有行情数据写入。下面重点介绍策略信号数据库。

信号库有两张超级表,分别对应合约级别信号和策略级别信号,每个交易信号对应一张子表,当前共有 40,000多张表,表结构分别如下所示:

下面是信号库执行show tables的截图:

数据库配置以及写入

我们选用的TDengine版本是2.2.0.0,由于单机版尚无压力,目前还不需要集群。此外,机器有40核,而TDengine的每一个vnode又是拥有独立运行线程的工作单元。所以,根据文章《这几个神秘参数,教你TDengine集群的正确使用方式》,我调整了minTablesPerVnode、tableIncStepPerVnode和maxVgroupsPerDb参数,让vnode的数量恰好等于CPU核数,让每个核独立运行一个线程,实现了数据的合理化分布,以争取达到最佳性能。

写入性能

当前,我们大概每秒写入3万行数据。单节点TDengine可以十分轻松地实现这个级别数据量的写入。同时,消耗服务器资源又比InfluxDB与MySQL小的多。因此,即便未来业务扩大,我们也不需要担心额外的硬件成本。

资源消耗

我们当前的服务器配置如下:64G内存+40核 1.8GHz CPU+机械硬盘。

在业务运行期间,taosd的%CPU只有4%上下浮动,进程使用的物理内存百分比为11.2%。虽然内存占用稍多,但这是由于我们的vnode配置的比较多,每个vnode都有自己固定的内存缓冲区。因此,后续即便是继续大量增加新表或者加大写入量,内存占用也不会有明显的浮动了。

截至目前,通过TDengine录入的两个信号表已经写入了82亿条数据,原数据大概为92GB,实际占用存储空间为20G左右,压缩率高达23%,如果是整型数据应该还会更高。

查询性能

除了写入与存储,使用TDengine做日常查询的速度也十分优秀,即便是对于几十亿级别的大表,也是毫秒级响应。我们来看两个场景。

场景1:查询特定策略信号下一段时间的均值。

select avg(v) from stgbox.strategy_signal where stg_name = '{stg_id}' and signal_name ='{signal_name}' and ts >= '{from_date} 00:00:00' and ts <= '{to_date} 23:59:59.999' interval({interval})

以下是我们用场景1查询出的数据进行可视化分析的示例。

场景2:查询满足模糊查询条件的信号的最新值。

select name,last(v) from stgbox.global_signal where name like '%keyword%' group by name。

在修改cachelast缓存之前,查询效率如上。

后面在涛思数据的技术支持之下,我们将cachelast参数设置成了3。

再执行了同样的查询,查询效率得到了很大提升:

这两个都是我们比较典型的查询场景,TDengine完美地匹配了我们对功能以及性能上的需求。

写在最后

我们目前对TDengine的使用还处于初级阶段,TDengine不仅仅是时序数据库,还可以作为消息队列,支持数据订阅。以后我们会探索将TDengine用于更多的业务场景,以更好地服务于我们的各类分析与交易执行。

关于作者:

丁博,弘源泰平量化工程师。目前负责公司交易执行系统、交易策略信号系统和交易组合管理系统的研发。