【Backtrader】怎么获取指数数据

908 阅读7分钟

前言

之前已经写了 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 来说是反的,所以要用 sed reverse 一下,为了简单处理,直接就把表头给删了,后面加载数据的时候记得设置 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沪深300000300全指可选000989三板做市899002
A股指数000002深证成份全收益指数399002500沪市000802全指医药000991创新成指899003
B股指数000003深证成份B股指数399003细分医药000814全指金融000992三板成指899001
上证380000009深证100全收益指数399004有色金属000819全指信息000993三板龙头899301
上证180000010中小企业100指数399005中证环保000827CSSW证券399707三板制造899302
上证50000016创业板指数399006中证1000000852500深市399802三板服务899303
新综指000017深证300指数399007小康指数000901工业4.0399803三板医药899304
中型综指000020中小企业300指数399008中证100000903中证体育399804三板消费899305
超大盘000043深证200指数399009中证500000905互联金融399805三板研发899306
上证中盘000044深证700指数399010中证800000906环境治理399806三板活跃899307
上证小盘000045深证1000指数399011300医药000913高铁产业399807
上证中小000046创业板300指数399012300金融000914中证新能399808
上证全指000047深市精选指数399013800能源000928保险主题399809
上证流通000090深证中小创新指数399015800消费000932CSSW传媒399810
上证100000132深证创新指数399016800医卫000933CSSW电子399811
上证150000133中小企业创新指数399017800金地000934养老产业399812
市值百强000155创业板创新指数399018800信息000935中证国安399813
科创50000688创业板碳中和科技动力指数399030500等权000982大农业399814
深证创新100指数399088全指能源000986军工指数399959
深证新指数399100全指材料000987800地产399965

总结

好像也没有什么好总结的了,唯一有感触的是之前 shell 方面的积累还是有些用的。不过感觉可以用 python 写一个啊,比较 shell 的语法太弱了,后面再优化吧,先搞起来。

不在非战略的机会点上消耗战略性资源。——华为