利用Backtrader进行期权回测之六:用Scrapy爬取期权合约

在期权回测中我用到了三种数据:期权日数据、ETF日数据和期权合约数据。其中前两种数据的获取方法已经在本系列的第1篇文章中做了说明,这里补充一下怎么获取期权合约数据。

期权合约数据是从上交所和深交所下载的。虽然深交所的合约可以手工下载excel文件再转化为CSV之后通过python程序读取。然而,如果需要长期持续地获取数据,最好还是通过爬虫自动爬取。

本文演示怎么通过scrapy爬虫获取上交所/深交所的期权合约信息。假定读者对scrapy有一定的了解,知道scrapy的基础用法。对scrapy不了解的读者可以先看scrapy官方教程中文版

1. 上交所期权合约

上交所的数据在上交所当日合约可以查到。

1.1 上交所网页的反爬措施

分析上交所的URL,可以看到有两个地方需要特殊处理:

  • 原网页通过一个回调函数处理返回数据,所以请求里要给一个回调函数名称。HTTP请求的参数形式如下:jsonCallBack=jsonpCallbacknnnnn。最后的“nnnnn”是一个随机数。可以通过python的randint()函数生成。
  • 请求里有一个时间戳参数,形式如下:_=1596089949708。等号后面的数字是当前时间的毫秒数,可以通过time.time()*1000来获得。

1.2 创建爬虫

打开一个命令行窗口,进入要创建爬虫项目的目录,然后输入下面的命令:

scrapy startproject optdata

其中optdata是要创建的项目名称。scrapy创建的内容如下:

optdata/
    scrapy.cfg
    optdata/
        __init__.py
        items.py
        pipelines.py
        settings.py
        spiders/
            __init__.py
            ...

1.3 实现爬虫

进入刚才创建的“optdata\optdata\spiders”目录,用自己喜欢的编辑器新建一个名为“OptCodeSpider.py”的文件。我习惯用pycharm。

import scrapy

import json
import time
import re
import pandas as pd
from datetime import date
from random import randint, random

# 爬取上交所期权合约
class SSEOptCodeSpider(scrapy.Spider):
    name = "sseoptcode"        # 上交所期权合约爬虫名称

	# 定义爬虫使用的header信息
	headers = {
		'Referer': 'http://www.sse.com.cn/assortment/options/disclo/preinfo/',
		'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
        }
        
	# 爬取下来的数据保存在这个文件里
    data_file = 'D:\Srcs\python\stockenv\data\options\opts_sse.csv'

	# 爬取的期权合约信息
    opts = []

	# 获取起始URL的内容
    def start_requests(self):
    	# 构造上交所期权数据的URL
        url = self.get_opt_code_url()

		# 获取页面内容。上交所当日合约可以全部放在一个页面里,不需要处理翻页问题。
        yield scrapy.Request(url=url, headers=headers, callback=self.parse)
        
        
	# 解析页面内容
    def parse(self, response):
    	# 首先把返回数据转换成json数据
        json_str = response.text[18:-1]
        data = json.loads(json_str)

		# 循环处理每一条合约
        for opt in data['result']:
        	# 提取期权代码和期权名称
            code = opt['SECURITY_ID']
            name = opt['CONTRACT_SYMBOL']

			# 从“50ETF(510050)”这样的字符串中提取基金代码,即括号里的部分。
            s = opt['SECURITYNAMEBYID']
            m = re.search(r"\(\d+\)", s)  # 通过正则表达式匹配ETF代码
            if m is None:
                scrapy.log.msg('{}: 没有找到对应的ETF基金: {}'.format(code, s),
                               level=scrapy.log.WARNING)
                continue
            asset = m.group(0)[1:-1]

			# 提取期权类型,行权价,合约单位
            opt_type = opt['CALL_OR_PUT']
            strike = opt['EXERCISE_PRICE']
            unit = opt['CONTRACT_UNIT']

			# 提取到期日
            maturity = opt['EXPIRE_DATE']
            maturity = '{}-{}-{}'.format(maturity[0:4], maturity[4:6], maturity[6:])

			# 提取行权交收日
            dd = opt['DELIVERY_DATE']
            delivery_date = '{}-{}-{}'.format(dd[0:4], dd[4:6], dd[6:])
            
			# 添加当前期权合约到期权合约列表
            self.opts.append([code, name, asset, opt_type, strike, unit, maturity, delivery_date])

		# 保存结果到数据文件
        df = pd.DataFrame(self.opts, columns=['合约编码', '合约简称', '标的名称', '类型', '行权价(元)', '合约单位(份)', '期权行权日', '期权交收日'])
        df.to_csv(self.data_file, index=False)

        return


    # 生成查询期权合约的URL
    def get_opt_code_url(self):
    	# 生成时间戳
        timestamp = int(time.time() * 1000)
		
		# 构造URL
        url = 'http://query.sse.com.cn/commonQuery.do?jsonCallBack=jsonpCallback{0}&isPagination=true&expireDate=&securityId=&sqlId=SSE_ZQPZ_YSP_GGQQZSXT_XXPL_DRHY_SEARCH_L&pageHelp.pageSize=10000&pageHelp.pageNo=1&pageHelp.beginPage=1&pageHelp.cacheSize=1&pageHelp.endPage=5&_={1}'.format(
            randint(1000, 9999), timestamp)

        return url

1.5 运行爬虫

完成爬虫编写后,回到命令行界面,进入第1层optdata目录,执行爬虫:

scrapy crawl sseoptcode

然后读取并检查“opts_sse.csv”文件:

df = pd.read_csv('D:\Srcs\python\stockenv\data\options\opts_sse.csv')
df[:5]

可以看到如下结果:

图1:上交所期权合约
综上,上交所期权合约的爬虫就实现完成了。读者可以自行对此进行完善,比如添加更严谨的错误处理逻辑。

2. 深交所期权合约

深交所的期权合约在深交所产品目录/期权可以看到。

深交所的爬虫与上交所类似,区别在于:

  • URL不同
  • 合约数据项名称不同
  • 个别数据项的格式略有区别

这些差异都很容易处理,所以这里直接给出代码。这个类和上交所的爬虫类一样,都放在“OptCodeSpider.py”文件里。

# 深交所期权合约爬虫
class SZSEOptCodeSpider(scrapy.Spider):
    name = "szseoptcode"        # 深交所期权合约爬虫名称

    headers = {
        'Referer': 'http://www.szse.cn/market/product/option/index.html',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
    }

    data_file = 'D:\Srcs\python\stockenv\data\options\opts_szse.csv'
    opts = []

	# 发送初始URL,开始爬取数据
    def start_requests(self):
        url = self.get_opt_code_url()
        yield scrapy.Request(url=url, headers=self.headers, callback=self.parse)

	# 解析爬取的数据
    def parse(self, response):
    	# 转换为json
        json_str = response.text
        data = json.loads(json_str)

		# 循环处理每一个合约
        for opt in data[0]['data']:
            code = opt['hydm']
            name = opt['hymc']

            asset = opt['bdmc']
            if asset is not None and asset == '300ETF':
                asset = '159919'
            else:
                scrapy.log.msg('{}: 没有找到对应的ETF基金: {}'.format(code, s),
                               level=scrapy.log.WARNING)
                continue

            opt_type = opt['hylx']
            strike = opt['xqj']
            unit = opt['hydw']

            maturity = opt['xqrq']
            delivery_date = opt['jsrq']

            self.opts.append([code, name, asset, opt_type, strike, unit, maturity, delivery_date])

		# 检查是否所有的页面都已经处理完成。
        pn = data[0]['metadata']['pageno']			# 当前页数
        pc = data[0]['metadata']['pagecount']		# 总页数
        if pn < pc:
        	# 如果还有更多的页面,继续获取下一个页面
            url = self.get_opt_code_url(pn + 1)
            yield scrapy.Request(url=url, headers=self.headers, callback=self.parse)
        else:
        	# 全部数据爬取完成,保存数据
            df = pd.DataFrame(self.opts,
                columns=['合约编码', '合约简称', '标的名称', '类型', '行权价(元)', '合约单位(份)', '期权行权日', '期权交收日'])
            df.to_csv(self.data_file, index=False)

        return


    # 生成查询期权合约的URL
    def get_opt_code_url(self, page_no=1):
        url = 'http://www.szse.cn/api/report/ShowReport/data?SHOWTYPE=JSON&CATALOGID=ysplbrb&TABKEY=tab1&PAGENO={}&random={:.16f}'.format(
            page_no, random())

        return url

执行爬虫:

scrapy crawl szseoptcode

3. 小结

通过使用scrapy可以自动爬取上交所和深交所的期权合约数据,上交所和深交所也采取了一些简单的反爬措施,不过很容易解决。而且这个解决方案也可以用来爬取上交所/深交所的其它数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值