量化投资从0开始系列 ---- 13. 大商所日统计数据

前文说了上海期货交易所的数据是json格式,非常容易用程序解析。这里再看看大连商品交易所的实现方式。

还是按F12先从浏览器查看网络请求,大商所的页面采用的传统ajax方式,返回的是一个html子页面,然后客户端做局部刷新。请求地址是:

POST	http://www.dce.com.cn/publicweb/quotesdata/dayQuotesCh.html

html的解析相对而言是实现最繁琐且执行效率最低的方式,应该尽量避免。

进一步观察发现系统还提供了一个导出文本的接口,回应中包含的数据跟网页是一致的,且格式是结构化的,基本可以看做是tsv格式的文件。那么我们就用这个接口来实现。

POST http://www.dce.com.cn/publicweb/quotesdata/exportDayQuotesChData.html

除去前文提到过的实现模式,这里的要点是对文本文件的解析。先看文本文件的格式:

每种合约一行,字段之前用tab分割(准确的说有时候一个tab,有时候两个tab,所以说不是完全合规的tsv格式 )。其中每一类商品合约结束的时候有一个小计行,整个文件结束的时候还有一个合计行(截图上没显示,可以自行查看官方数据)。

这里需要做的就是从中正确提取各个字段的值,对应的包括按换行符分割数据,清洗不需要的统计行,处理分隔符得到正确的数值,代码片段如下:

    r = requests.get(dce_url, headers=headers)    
    tsv_origin = r.content.decode().split('\r\n')
        tsv = []
        for item in tsv_origin:
            item = item.strip('\t')
            if '商品名称' in item or '小计' in item or '总计' in item or item == '':
                continue
            else:
                tsv.append(re.compile("\t+").sub('\t', item).split('\t'))

在保存的时候,还要自己把当天的日期加上,因为服务端返回的数据是根据请求的日期查询出来的,所以在返回的数据中省略的日期。

        columns = ['name', 'month', 'open', 'high', 'low', 'close', 'presettlement', 'settlement', 'zd1_chg', 'zd2_chg', 'volume', 'openinterest', 'openinterestchg', 'turnover']
        df = pd.DataFrame(data=tsv, columns=columns)
        df['trade_date'] = trade_date

        self._save(df)

完整的代码如下:

import multiprocessing
import re

import pandas as pd
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests

from common.utils import *
from tushare_client.base import AbstractDataRetriever
from tushare_client.stock_calendar import StockCalendar


class DceDaily(AbstractDataRetriever):
    def __init__(self):
        super().__init__('futures_dce_daily')

    def _full(self, **kwargs):
        self._get_data_list('20110101', today())

    def _delta(self, **kwargs):
        df_origin = self.query(fields='max(trade_date)')
        if df_origin.empty or df_origin.iat[0, 0] is None:
            self._get_data_list('20110101', today())
        else:
            self._get_data_list(df_origin.iat[0, 0], today())

    def _get_data_list(self, start_date, end_date, max_worker=multiprocessing.cpu_count() * 2):
        df_cal_date = StockCalendar().query(
            fields='cal_date',
            where=f'`exchange`=\'dce\' and is_open=\'1\' and cal_date >\'{start_date}\' and cal_date <= \'{end_date}\'',
            order_by='cal_date')

        with ThreadPoolExecutor(max_worker) as executor:
            future_to_date = \
                {executor.submit(self._get_daily_data, trade_date=row['cal_date']): row
                 for index, row in df_cal_date.iterrows()}
            for future in as_completed(future_to_date):
                row = future_to_date[future]
                try:
                    data = future.result()
                except Exception as ex:
                    self.logger.error(f"failed to retrieve {row['cal_date']}")
                    self.logger.exception(ex)

    def _get_daily_data(self, trade_date):
        dce_url = f'http://www.dce.com.cn/publicweb/quotesdata/exportDayQuotesChData.html?dayQuotes.variety=all&dayQuotes.trade_type=0&year={trade_date[0:4]}&month={int(trade_date[4:6])-1}&day={trade_date[6:8]}&exportFlag=txt'
        headers = {
            'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0',
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2'
        }
        r = requests.get(dce_url, headers=headers)
        tsv_origin = r.content.decode().split('\r\n')
        tsv = []
        for item in tsv_origin:
            item = item.strip('\t')
            if '商品名称' in item or '小计' in item or '总计' in item or item == '':
                continue
            else:
                tsv.append(re.compile("\t+").sub('\t', item).split('\t'))

        columns = ['name', 'month', 'open', 'high', 'low', 'close', 'presettlement', 'settlement', 'zd1_chg', 'zd2_chg', 'volume', 'openinterest', 'openinterestchg', 'turnover']
        df = pd.DataFrame(data=tsv, columns=columns)
        df['trade_date'] = trade_date

        self._save(df)


if __name__ == '__main__':
    DceDaily().retrieve()

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值