常见的量化数据
量化交易需要和大量数据打交道,了解这些数据是我们必须做的功课,下面是一些常见的量化数据:
- 标的相关数据
- 股票、基金、转债、指数的基本信息,名称、代码、行业、上市信息、是否ST等
- 公司相关的数据,转债的发行信息
- 指数、基金成分股持仓,基金规模、管理人信息
- 这些数据都是和标的息息相关,通常不定期更新,可能存在滞后性
- 行情数据
- 股票k线数据,基金净值,复权因子
- PE、PB等估值数据
- 每日涨跌停价格,停复牌数据等
- 财务数据
- 各种财务指标,利润表、资产负债表、现金流量表的各个项目的数据等
- 财务数据都具有滞后性,不是实时更新,并且可能存在后期修正的情况
- 宏观数据
- M1、M2、GDP、CPI 和 PPI 等
- 市场统计数据
- 每日龙虎榜,市场整体涨跌数等市场统计数据等
- 其他数据
- 新闻,公告,媒体数据
- 一些券商、分析师、机构出的统计、调研数据、研报
- 交易日历,即每天是否开盘交易
上面的数据中,绝大部分都属于结构化数据,有一小部分属于非结构化数据。
结构化数据:指那些以固定格式存储的数据,它们遵循特定的数据模型。比如说,基础的行情数据可以简单的定义为高开低收、成交额,并且都是浮点数。结构化数据易于处理、搜索、查询和分析,因为它们遵循一致的格式和结构。
非结构化数据:非结构化数据则没有固定的格式或结构,它们可能以文本、图像、音频或视频的形式存在。非结构化数据的处理和分析通常更加复杂,因为需要特定的技术和方法来提取有用信息。常见的非结构化数据:新闻公告、社交媒体、研报等。处理这些数据可能需要使用文档解析,内容提取,NLP,LLM等技术进行处理,处理难度大、成本高,通常机构可能会去处理这方面的数据。
下面我们要介绍的数据存储,将以结构化数据为主。
时序数据与截面数据
时序数据和截面数据都是量化研究中常见的两种数据类型,这里简单介绍下:
时序数据(Time Series Data),时序数据就是按照时间顺序排列的数据点集合,通常用于记录某个变量随时间变化的情况。时序数据可以用来追踪股票价格、交易量、宏观经济指标等随时间的变化。时序数据具有以下特点
- 时间依赖性,数据点之间存在时间上的连续性,后续的数据点可能受到前面数据点的影响
- 趋势和周期性:时序数据可能显示出趋势(如上升或下降的趋势)、季节性(如周期性变化)或周期性(如经济周期)
- 预测和回溯:基于以上两点,时序数据分析可以用于预测未来的走势(如股票价格预测)和回溯分析(如历史表现回顾)
截面数据(Cross-Sectional Data),截面数据是在特定时间点或时间段内,从多个个体或标的(如公司、投资者、资产等)收集的数据集合。这些数据通常用于比较不同个体或标的在某一时刻的状态或特征。截面数据具有以下特点:
- 横截面比较:数据用于比较不同个体在相同时间点的表现,如不同公司的财务状况、不同投资者的持仓情况等
- 多样性:截面数据通常包含多个变量,可以用于多维度的分析
- 相关性和回归分析:截面数据常用于研究变量之间的相关性,以及进行回归分析来探究变量间的关系
数据的获取
关于数据的获取,大家可以参考专栏里之前的分享。
为什么要本地存储量化数据
既然有在线数据可以使用,为什么还要本地存储量化数据呢?这是因为本地存储有在线数据不可比拟的优势:
- 访问速度快效率高,在线数据因为需要走网络,服务器限流等,访问速度通常比本地要慢很多
- 稳定性可靠性高,在线数据受限于在线服务的可用性,限流,爬虫限制,甚至服务器故障等,可能出现不可用的情况,本地数据不会存在这个问题
- 成本低,方便备份与拷贝,本地存储非常廉价,而且拷贝非常方便,付费的在线服务通常需要你一直订阅相关服务才能一直使用
常见本地存储方案
CSV、Excel文件存储
CSV和Excel是最简单的文件格式,可以存储结构化数据,读写操作都非常简单方便。
缺点是效率不高,性能一般,不适合大量数据(上百万条千万条以上)存储,而且会丢失数据类型,比如日期类型存储再读取后,变为字符串类型。
性能:差
使用:最简单
HDF文件高效存储
HDF5是一种用于存储和组织大规模数据的文件格式,支持压缩、分块存储与读取,性能较好,支持常用的数据类型,甚至还可以存储图像、音频等复杂格式,并且也是也是存储为单个文件,方便拷贝与备份,常用于量化数据的大批量存储。
HDF5是更高性能的文件格式,相对应的,使用也比CSV和Excel复杂一些。
HDF5依然是单文件存储,如果要多人使用,只能将文件拷贝给对方。另外,HDF5需要专门的三方库来使用(简单使用的话,可以直接用pandas提供的接口)。
性能:较好
使用:较简单
数据库存储
- 数据库服务可以因为可以单独部署,这样团队可以共享一个数据库,不需要再拷贝文件
- 并且数据库使用非常灵活,可以进行各种优化最大化读写性能与存储容量
- 数据库可以存储大量数据,在千万级别甚至亿级别以上依然能保持较好的性能
- 与灵活性和高性能相对的,数据库部署与使用非常复杂,通常需要掌握SQL或其他专用的查询语法
- 市面上的数据库五花八门,如果没有相关知识和经验,选型也是一个头疼的问题
- 数据库因为是一个服务,需要对其进行运维,更适合有IT支持的团队
性能:可以达到非常高
使用:较为复杂
关系型数据库
- MySQL,PostgreSQL
MySQL和PostgreSQL是传统的面相通用场景的关系型数据库管理系统(RDBMS),用来存储量化数据当然也不在话下。他们都可以使用SQL语言进行操作,而且因为推出较早、非常流行,所有网上资料非常多。
但是由于是面相通用场景的,并没有针对超大数据量、时序数据、和在线分析处理做设计,在这些场景下,性能和功能不如专用的数据库。不过,个人小规模使用也足够了。
非关系型数据库
- Clickhouse
ClickHouse 是一个开源的列式数据库管理系统(DBMS),专为在线分析处理(OLAP)和大数据场景设计。它提供了极高的查询性能,尤其是在处理大规模数据集时。
clickhouse使用SQL来进行插入和查询,并提供了大量用于统计分析的SQL方法。
得益于clickhouse简单易用易维护、高性能的特点,国内有非常多的机构使用clickhouse存储行情数据和因子数据。 - 时序数据库:TDengine/InflexDb
时序数据库可以说是专门为存储时序数据设计的,面对时序数据具有较高的性能。常用的时序数据库包括国外的InflexDB,以及国内的TDengine,它们都支持SQL语言,适合用来存储行情等时序数据,并不适合存储因子数据。
其他特殊数据库
这两个数据库比较有意思,它们都支持SQL,可以像关系型数据库和非关系型数据库一样去操作,使用灵活,性能够好,但是又不需要部署的服务,通常存储为单个数据库文件。有时候也叫它们嵌入式数据库,因为它们不需要启动单独的服务,是嵌入在我们的程序中运行的。这类数据库使用非常简单,无需配置。
-
SQLite
SQLite 是一个开源的嵌入式关系型数据库引擎。它不需要单独的服务器进程或操作系统的运行时支持,可以直接集成到应用程序中。SQLite 是自包含的,它的整个数据库都存储在一个独立的磁盘文件中。主打一个轻量高效,使用简单,是低成本的数据库,适合个人电脑或一些低性能的设备。 -
DuckDB
DuckDB是近几年兴起的一个开源嵌入式分析型数据库,和SQLite一样,整个数据库存储在单个文件中,主要面相在线分析(OLAP),使用SQL操作- 对于超大数据量(亿以上)、时序数据、在线分析等量化常用场景都能轻松应对,具有非常高的性能
- DuckDB使用非常简单,比SQLite还要简单,而且和pandas等python库深度集成
实战
实战部分我们会对几种存储方案给出代码案例,供大家参考学习。关系型数据库和非关系型数据库由于使用较为复杂,适合有开发经验的人使用,这里就不介绍这两部分了。
使用CSV/Excel存储数据
注意,使用excel存储数据,需要先安装 openpyxl,在终端中执行:
pip install openpyxl
下面是对应的python代码:
import qstock as qs
import pandas as pd
# 使用qstock拉取沪深300指数 2010 年至今的数据,fqt=2 表示后复权
df = qs.get_data('000300', start='20100101', end='20240229', fqt=2)
# 保存数据
df.to_csv('d:/quant_data/000300.csv')
# 读取数据
df1 = pd.read_csv('d:/quant_data/000300.csv')
# 如果读取一份下载的数据出现编码错误,可以试试
df2 = pd.read_csv('d:/quant_data/000300.csv', encoding='gbk')
# 或者
df3 = pd.read_csv('d:/quant_data/000300.csv', encoding='utf-8')
# 保存到Excel
df.to_excel('d:/quant_data/000300.xlsx')
# 从Excel读取数据
df1 = pd.read_excel('d:/quant_data/000300.xlsx')
注意:
- 目录 d:/quant_data 需要你自己新建,表示D盘下的 quant_data 文件夹
- 在python中写windows文件路径,直接使用单个左斜杠分隔就可以,路径中不区分大小写
使用HDF5存储数据
注意,使用hdf5存储数据,需要先安装 pytables,在终端中执行:
pip install pytables
下面是对应的python代码:
import qstock as qs
import pandas as pd
# 使用qstock拉取沪深300指数 2010 年至今的数据,fqt=2 表示后复权
df1 = qs.get_data('000300', start='20100101', end='20240229', fqt=2)
# 使用qstock拉取贵州茅台 2010 年至今的数据,fqt=2 表示后复权
df2 = qs.get_data('600519', start='20100101', end='20240229', fqt=2)
# 保存数据,hdf可以在单个文件中存储多组数据,使用key来区分
df1.to_hdf('d:/quant_data/hdf_data.hdf5', key='000300')
df2.to_hdf('d:/quant_data/hdf_data.hdf5', key='600519')
# 读取数据,同样的,也可以通过指定key来读取某一块数据
my_df1 = pd.read_hdf('d:/quant_data/hdf_data.hdf5', key='000300') # 读取沪深300数据
my_df2 = pd.read_hdf('d:/quant_data/hdf_data.hdf5', key='600519') # 读取贵州茅台数据
使用DuckDB存储数据
注意,DuckDB的使用需要会基本的SQL,使用前需要安装,不会SQL也不要紧,直接问AI就行。在终端中执行:
pip install duckdb
注意:
- duckdb可以和pandas深度集成,可以直接在sql中操作df
- 数据库有库和表的概念,在真正存储数据前需要确保存在相关的库和表
- 一个库中可以有多个表,每个表存储某一类型的数据
- duckdb中有默认库,我们可以不用新建
- 下面的例子中,我们使用一种便捷的办法基于 DataFrame 结构新建一个存储k线数据的表
对应的SQL语法 create table 表名 as from DataFrame变量名
- 写入数据,使用 INSERT 语句,如果数据库表和DataFrame结构一样,可以直接将 DataFrame 的数据写入
对应的SQL语法 insert into 表名 from DataFrame变量名 - 读取数据在SQL中称为查询数据,使用SELECT语句
- SQL语法是 select * from 表名 where 条件 ,如果没有条件,就会查询表中所有数据,此时可以省略 where 部分,即 select * from 表名
- 比如下面的,查询沪深300的数据 select * from t_kline_data where code=‘000300’
下面是对应的python代码:
import qstock as qs
import pandas as pd
import duckdb
# 新建一个duckdb数据库连接,参数是数据库文件存储的位置
con = duckdb.connect('d:/quant_data/test.db')
# 使用qstock拉取沪深300指数 2010 年至今的数据,fqt=2 表示后复权
df1 = qs.get_data('000300', start='20100101', end='20240229', fqt=2)
# 使用qstock拉取贵州茅台 2010 年至今的数据,fqt=2 表示后复权
df2 = qs.get_data('600519', start='20100101', end='20240229', fqt=2)
# 使用上面建好的连接进行操作,通常使用 .sql 方法,参数就是sql语句
# 直接基于 df1 新建一个表,同时 df1 中的数据也会插入进去
con.sql('create table t_kline_data as from df1')
# 这里读取一下,使用 select 查询语句
my_df1 = con.sql('select * from t_kline_data').df()
# 再把茅台的数据也插入进去,使用 insert 语句
con.sql('insert into t_kline_data from df2')
# 这样,沪深300和贵州茅台的k线数据就都保存到 t_kline_data 表了
# 读取沪深300的数据
my_df1_1 = con.sql("select * from t_kline_data where code='000300'").df()
# 读取贵州茅台的数据
my_df2_1 = con.sql("select * from t_kline_data where code='600519'").df()
上面是各种本地数据存储方法的基本使用。
关于DuckDB,DuckDB的优势在于极高的分析、查询、存储性能,不需要运维,开箱即用,但是要求会使用SQL。SQL本身也是一门完善的语言,有非常非常多的用法,单独写一本书也够了,如果大家有兴趣,可以网上找DuckDB资料学习,也可以参考DuckDB的官方文档(英文) https://duckdb.org/docs/sql/introduction。
对于日常使用,大家可以直接使用 CSV/Excel/HDF5,就能满足大多数需求了。