SPL 量化 获取数据
下载数据
我们将股票数据分享在百度网盘上,并不定期更新,可免费下载。
目前可供下载的数据有 A 股的日 K 线数据、股票代码列表和上市公司的基本面数据
下载链接:
https://pan.baidu.com/s/1fTuqDtIjvxzX22EcbeOvtA?pwd=cd5r
下载数据的文件格式为 btx 和 ctx,都是 SPL 的特有二进制格式。
btx 称为集文件,是一种简单、开放的二进制文件格式,它覆盖了 csv 文件的所有功能,并且性能要比 csv 文件快 4-5 倍。
ctx 称为组表,是一种轻量的列存技术,可有效提高数据分析计算性能,适用于存储大数据。
实际应用中,通常用集文件存储小表,用组表存储大表。
SPL 可以支持 btx 和 ctx 文件的操作计算,也支持各种数据源与 btx、ctx 之间的相互转换。
日 K 线数据
文件名:trade.ctx。
提供 2000-01-01 至今的 A 股日线行情。
数据示例:
数据结构:
字段名 |
字段类型 |
含义 |
code |
Integer |
股票代码 |
tdate |
Date |
日期 |
open |
Double |
开盘价 |
close |
Double |
收盘价 |
low |
Double |
最低价 |
high |
Double |
最高价 |
volume |
Long |
成交量 |
amount |
Double |
成交金额 |
pfactor |
Double |
当日价格复权因子 |
注意,股票代码用了整数,而不是常规的字符串,这里 1 表示 000001,2 表示 000002…以此类推。整数的运算性能会远高于字符串。
公司基本面
文件名称:company.btx。
提供 2007 年至今的财务数据,每季度一条。
数据示例:
数据结构:
字段名 |
字段类型 |
含义 |
code |
Integer |
股票代码 |
statdate |
Date |
财报统计的季度的最后一天 |
totalshare |
Double |
总股本 |
liqashare |
Double |
流通股本 |
netprofit |
Double |
净利润 (元) |
mbrevenue |
Double |
主营营业收入 (元) |
未来会再补充更多信息
股票代码列表
文件名称:stock.btx。
提供股票基础信息,包括股票代码、名称、所属行业、上市日期、退市日期等。
数据示例:
数据结构:
字段名 |
字段类型 |
含义 |
code |
Integer |
股票代码 |
name |
String |
股票名 |
exchanges |
String |
中国三大交易所的简写 |
industry |
String |
所属行业 |
ipodate |
Date |
上市日期 |
outdate |
Date |
退市日期 |
数据读取
日 K 线
(1) 用游标读取样例数据
首先可以用游标读出一小部分数据查看一下。
代码示例:
A |
B |
|
1 |
=file("trade.ctx").open().cursor().fetch(100) |
用游标读取前 100 条数据 |
运行效果:
(2) 读取某支股票数据
读取某支股票在任意时间段的数据。
代码示例:
A |
||
1 |
=file("trade.ctx").open().cursor(;code==600000 && tdate>=date("2024-01-01") && tdate<=date("2024-12-31")).fetch() |
读取代码为 600000 的股票在 2024 年的数据 |
运行效果:
(3) 读取部分列
也可以只读取部分列。
代码示例:
A |
B |
|
1 |
=file("trade.ctx").open().cursor(code,tdate,close;code==600000).fetch() |
读取 600000 股票的收盘价 |
运行效果:
基本面
(1) 可以用 T 函数直接读入 btx 文件。
当数据文件不大时,可以直接全部读入内存。
代码示例:
A |
B |
|
1 |
=T("company.btx") |
读入基本面数据 |
2 |
=A1.select(code==600000 && statdate>=date("2024-01-01") && statdate<=date("2024-12-31")) |
选出 600000 股票 2024 年数据 |
运行效果:
A1
A2
(2) 如果只使用某支股票的基本面时,也可以使用游标读入以减少内存占用。
代码示例:
A |
B |
|
1 |
=file("company.btx").cursor@b() |
将数据读成游标 |
2 |
=A1.select(code==600000 && statdate>=date("2024-01-01") && statdate<=date("2024-12-31")) |
选出 600000 股票 2024 年数据 |
3 |
=A2.fetch() |
取出 A2 选中的数据 |
运行效果:
(3) 也可以只读取少部分列。
代码示例:
A |
|
1 |
=file("company.btx").cursor@b(code,statdate,totalshare).fetch(100) |
运行效果:
股票代码列表
读取股票列表可以用 T 函数全部读入。
A |
|
1 |
=T("stock.btx") |
运行效果:
复权数据
复权概念
由于股票经常发生分红、配股、分拆或合并等事件,使得股价出现较大变化,复权就是用来修正这种股价变化。
例如某支股票每股 10 元,持有 100 股,总资产 1000 元
发生拆股后,持有的 100 股变为 500 股,但是股价也会自动调整为原来的 1/5,变为 2 元,总资产还是 1000 元,并没有发生变化。
股价从 10 元变为 2 元,看似暴跌了 80%,但投资者的资产始终没变,因此股价的变化其实是 0。
为了避免这种价格“暴跌”的影响,需要对股票的价格进行修复,这一过程就叫做复权。
复权分为前复权和后复权。
前复权就是保持新时间点的价格不变,对历史时间点的价格进行调整,使得股价连续。而后复权正好相反,保持早期时间点价格不变,调整后续时间点的价格。
按上面例子,股价从 10 元变为 2 元,向前复权时,会维持今天的股价不变,而把昨天的股价由 10 元变成 2 元与当前价格保持一致,之前的股价也都一律按比例缩小,股价变为一条连续的曲线。
反之,向后复权就是保持昨天的价格 10 元不变,调整当前价格 2 元变 10 元,之后的股价也都按比例增大。
复权价的计算方法有多种,由于数据口径或计算方法的不同,得到的复权价格也会有所不同。这里我们采用的是涨跌幅复权法。
日 K 线数据里,pfactor 为复权因子。需要注意的是:该复权因子为当日复权因子,而不是累计复权因子,这样的好处是更方便随意指定一个时间区间计算出和该区间相关的复权价格。
比如以浦发银行为例,数据如下图所示。在 2024 年 7 月 18 日发生了复权动作,复权因子 pfactor 为 0.96460176991。前复权数据就是保持当日股价不变,昨日股价调整为 8.9*0.96460176991=8.584955752212391,以此类推,之前的股价也按比例调整。反之,后复权数据就是保存历史股价不变,当日股价调整为 8.75/0.96460176991=9.0711009174311,之后的股价同样也按比例调整。
由图中也可以看出 pfactor 为当日复权因子,只有在发生复权动作时参数才会变,其余都是 1。网上有些渠道提供的因子可能为累计因子,因此看起来会有所不同,使用时需注意区分。
复权价格
因为复权后价格才有涨跌的可比性,所以做量化策略时通常会使用复权价格,我们可以写一个脚本实现从原始价格到复权价格的计算。
A |
B |
C |
D |
E |
|
1 |
=data=file("trade.ctx").open().cursor(;code==cn && tdate>=start && tdate<=end).fetch() |
||||
2 |
if opt |
if pos(opt,"o") |
if pos(opt,"f") |
=data=data.sort(-tdate) |
=data.derive(open:o_open,close:o_close,low:o_low,high:o_high).run(tmp=data.(pfactor).to(#-1),if(#==1,cp=1,cp=tmp.iterate(~~*~,1)),open=open*cp,close=close*cp,low=low*cp,high=high*cp).sort(tdate) |
3 |
else if pos(opt,"b") |
=data.derive(open:o_open,close:o_close,low:o_low,high:o_high).run(open=open/pfactor,close=close/pfactor,low=low/pfactor,high=high/pfactor) |
=data.derive(open:o_open,close:o_close,low:o_low,high:o_high).run(tmp=data.(pfactor).to(#),if(#==1,cp=1,cp=tmp.iterate(~~/~,1)),open=open*cp,close=close*cp,low=low*cp,high=high*cp) |
||
4 |
else |
if pos(opt,"f") |
=data=data.sort(-tdate) |
=data.run(tmp=data.(pfactor).to(#-1),if(#==1,cp=1,cp=tmp.iterate(~~*~,1)),open=open*cp,close=close*cp,low=low*cp,high=high*cp).sort(tdate) |
|
5 |
else if pos(opt,"b") |
=data=data.sort(tdate) |
=data.run(tmp=data.(pfactor).to(#),if(#==1,cp=1,cp=tmp.iterate(~~/~,1)),open=open*cp,close=close*cp,low=low*cp,high=high*cp) |
||
6 |
else |
=data |
|||
7 |
return [E2:E6].conj() |
脚本参数:
参数名 |
含义 |
opt |
复权类型。"f": 前复权;"b": 后复权;"o": 保留原价与 "f" 和 "b" 连用,如 "fo"。 |
cn |
股票代码,例如:600000 |
start |
起始日期,例如:2020-01-01 |
end |
截止日期,例如:2024-12-31 |
注意:这段脚本里的前复权和后复权,都是区间的起止时点,也就是 start,end 为基础来计算的。
将上述脚本保存为 fetch_data.splx,调用脚本读取数据:
A |
B |
|
1 |
600000 |
股票代码 |
2 |
2024-01-01 |
开始日期 |
3 |
2024-12-31 |
截止日期 |
4 |
=call("fetch_data.splx",,A1,A2,A3) |
未复权数据 |
5 |
=call("fetch_data.splx","f",A1,A2,A3) |
前复权数据 |
6 |
=call("fetch_data.splx","b",A1,A2,A3) |
后复权数据 |
7 |
=call("fetch_data.splx","fo",A1,A2,A3) |
前复权保留原价 |
运行效果:
A4 未复权
A5 前复权
A6 后复权
A7 前复权保留原价,原价以”o_”开头。
对于经常使用的脚本,还可以用 register 登记成一个函数来使用。
代码示例:
A |
B |
|
1 |
=register@o("fetch_data","fetch_data.splx") |
将脚本登记为函数 |
2 |
600000 |
股票代码 |
3 |
2024-01-01 |
开始日期 |
4 |
2024-12-31 |
截止日期 |
5 |
=fetch_data(A2,A3,A4) |
未复权数据 |
6 |
=fetch_data@f(A2,A3,A4) |
前复权数据 |
7 |
=fetch_data@b(A2,A3,A4) |
后复权数据 |
8 |
=fetch_data@fo(A2,A3,A4) |
前复权保留原价 |
运行效果同上。