前言
之前已经写了 3 篇 Backtrader Guidance 详探的文章:
下面就是用真实数据来模拟实战了,测试策略还是用比较能反映市场规律的数据来测试,所以「指数数据」是比较好的选择,那么用哪些指数,指数的历史数据怎么获取呢?我们来研究一下。
正文
指数历史数据
简单搜了一下,发现在 网易财经 上能找到指数的历史数据,并且下载。通过分析其请求,可以发现下载数据的 URL 结构如下:
`http://quotes.money.163.com/service/chddata.html?code=${code}&start=${startDate}&end=${endDate}&fields=${fields.join(';')}`
// eg:
// http://quotes.money.163.com/service/chddata.html?code=0000001&start=19901219&end=20220810&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;VOTURNOVER;VATURNOVER
经过进一步测试,发现开始日期只要写的足够小就可以了,不用非得等于其基准日期。另外,fields 的顺序变化也会体现在最终结果当中,你甚至可以打印两列 TOPEN。fields 的说明如下:
| Field | 说明 |
|---|---|
| TCLOSE | 收盘价 |
| HIGH | 最高价 |
| LOW | 最低价 |
| TOPEN | 开盘价 |
| LCLOSE | 前收盘 |
| CHG | 涨跌额 |
| PCHG | 涨跌幅 |
| VOTURNOVER | 成交量 |
| VATURNOVER | 成交金额 |
批量下载脚本
找到规律之后,写个脚本进行批量下载就简单多了,有几个关键点要注意一下:
- 我们这里只保留开盘价、最高价、最低价、收盘价和成交量,注意这几个字段的顺序,在导入 Backtrader 的时候会有影响;
- 下载下来的数据最新的记录排在最前,这对于 Backtrader 来说是反的,所以要用
sedreverse 一下,为了简单处理,直接就把表头给删了,后面加载数据的时候记得设置header=False。 - 注意 URL 中的 code 是 7 位的,笔者目前还没有找到规律,暂时实现的逻辑是,0 开头就加 0,否则就加 1。比如:0000001,1399001。但是这个规律目前只是猜测,还需要进一步证实。
- 最后,为了防反爬,加了一个 sleep,这个尺度就不好把握了,估计只是下几个指数的数据应该不会出问题。
#!/bin/bash
start="19901201"
date=$(date +'%Y%m%d')
codes=(000001 399001) # change here
for code in ${codes[*]}; do
echo "Data of [$code] is downloading..."
pre=0
if [ ${code:0:1} != "0" ];then
pre=1
fi
filename="datas/$code-$date.csv"
curl -o $filename "http://quotes.money.163.com/service/chddata.html?code=$pre$code&start=$start&end=$date&fields=TOPEN;HIGH;LOW;TCLOSE;VOTURNOVER"
sed -i '1d' $filename
sed -i '1!G;h;$!d' $filename
sleep 0.5
# awk -v FS="," -v OFS="," "{print \$1,\$4,\$5,\$6,\$7,\$8 >\"$filename\"}" $filename
done
BUG:
暂时还有个 bug,如果直接用 Backtrader 加载文件,会报:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc8 in position 0: invalid continuation byte
的错误。需要把文件再保存一下,笔者是直接在 VSCode 里保存一下就行了,什么也不用改,后面再研究研究怎么解决。
导入 Backtrader
接下来就是把下载下来的数据导入到 Backtrader 了。导入用 cerebro.adddata(data) 即可,但是 data 实例如何生成就是个问题了。有两种方法:使用 GenericCSVData 和自定义 Feed 类。本质都是传入参数,让列数据能够正确匹配。具体代码如下:
使用 GenericCSVData
data = bt.feeds.GenericCSVData(
dataname=datapath,
fromdate=datetime.datetime(2022, 1, 1),
todate=datetime.datetime(2022, 8, 31),
nullvalue=0.0,
headers=False,
dtformat=('%Y-%m-%d'),
datetime=0,
open=3,
high=4,
low=5,
close=6,
volume=7,
openinterest=-1
)
自定义 Fead 类 原理还是要配置好列,只不过相当于专门封装了一个类来专门加载网易财经的数据,复用起来能方便点。代码如下:
class NetsCSVData(bt.feeds.GenericCSVData):
params = (
('nullvalue', 0.0),
('dtformat', ('%Y-%m-%d')),
('headers', False),
('datetime', 0),
('open', 3),
('high', 4),
('low', 5),
('close', 6),
('volume', 7),
('time', -1),
('openinterest', -1)
)
# Useage
data = NetsCSVData(
dataname=datapath,
fromdate=datetime.datetime(2022, 1, 1),
todate=datetime.datetime(2022, 8, 31),
)
常见指数
最后就是指数列表了,从上海证券交易所的官网上找到了这个地址:www.csindex.com.cn/zh-CN/indic… ,挑了一些排在前面的列出来。当然还有 深交所 也能查到很多。
| 上证 | 深证 | 中证 | 中证 | 新三板 | |||||
|---|---|---|---|---|---|---|---|---|---|
| 上证指数 | 000001 | 深证成份指数 | 399001 | 沪深300 | 000300 | 全指可选 | 000989 | 三板做市 | 899002 |
| A股指数 | 000002 | 深证成份全收益指数 | 399002 | 500沪市 | 000802 | 全指医药 | 000991 | 创新成指 | 899003 |
| B股指数 | 000003 | 深证成份B股指数 | 399003 | 细分医药 | 000814 | 全指金融 | 000992 | 三板成指 | 899001 |
| 上证380 | 000009 | 深证100全收益指数 | 399004 | 有色金属 | 000819 | 全指信息 | 000993 | 三板龙头 | 899301 |
| 上证180 | 000010 | 中小企业100指数 | 399005 | 中证环保 | 000827 | CSSW证券 | 399707 | 三板制造 | 899302 |
| 上证50 | 000016 | 创业板指数 | 399006 | 中证1000 | 000852 | 500深市 | 399802 | 三板服务 | 899303 |
| 新综指 | 000017 | 深证300指数 | 399007 | 小康指数 | 000901 | 工业4.0 | 399803 | 三板医药 | 899304 |
| 中型综指 | 000020 | 中小企业300指数 | 399008 | 中证100 | 000903 | 中证体育 | 399804 | 三板消费 | 899305 |
| 超大盘 | 000043 | 深证200指数 | 399009 | 中证500 | 000905 | 互联金融 | 399805 | 三板研发 | 899306 |
| 上证中盘 | 000044 | 深证700指数 | 399010 | 中证800 | 000906 | 环境治理 | 399806 | 三板活跃 | 899307 |
| 上证小盘 | 000045 | 深证1000指数 | 399011 | 300医药 | 000913 | 高铁产业 | 399807 | ||
| 上证中小 | 000046 | 创业板300指数 | 399012 | 300金融 | 000914 | 中证新能 | 399808 | ||
| 上证全指 | 000047 | 深市精选指数 | 399013 | 800能源 | 000928 | 保险主题 | 399809 | ||
| 上证流通 | 000090 | 深证中小创新指数 | 399015 | 800消费 | 000932 | CSSW传媒 | 399810 | ||
| 上证100 | 000132 | 深证创新指数 | 399016 | 800医卫 | 000933 | CSSW电子 | 399811 | ||
| 上证150 | 000133 | 中小企业创新指数 | 399017 | 800金地 | 000934 | 养老产业 | 399812 | ||
| 市值百强 | 000155 | 创业板创新指数 | 399018 | 800信息 | 000935 | 中证国安 | 399813 | ||
| 科创50 | 000688 | 创业板碳中和科技动力指数 | 399030 | 500等权 | 000982 | 大农业 | 399814 | ||
| 深证创新100指数 | 399088 | 全指能源 | 000986 | 军工指数 | 399959 | ||||
| 深证新指数 | 399100 | 全指材料 | 000987 | 800地产 | 399965 |
总结
好像也没有什么好总结的了,唯一有感触的是之前 shell 方面的积累还是有些用的。不过感觉可以用 python 写一个啊,比较 shell 的语法太弱了,后面再优化吧,先搞起来。
不在非战略的机会点上消耗战略性资源。——华为