个人量化交易初探之一(数据的爬取)

本文介绍了作者的量化交易初体验,主要针对A股市场历史数据进行分析,包括股票的财季、每日、资金流向及股东变化数据的爬取和存储。使用Python进行数据爬取,并选择MongoDB作为数据库。爬取数据主要包括东方财富和网易财经等网站的公开信息,涉及数据编码转换、数据库索引建立等技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本人大概是去年元旦左右开始接触量化交易的。从一开始的很多异想天开的想法,到逐渐定制交易策略,统计分析,到后面通过机器学习的多因子选股分析,经历了不少弯弯曲曲。希望和大家分享一下,也希望能找到更多的研究量化的朋友,共同学习和进步。

分析的基本对象是A股市场的历史数据。因为时间和资金量的关系,并没有去分析期货或者外汇市场。另外,考虑到A股的T+1交易规则以及手续费原因,也并没有爬取每天每隔几秒钟的实时数据,而是每天的价格信息和成交量等数据。这类数据基本上各类财经网站或者同花顺上也能获取。之所以通过爬虫爬到本地数据库进行分析,主要是考虑到工作和家庭(和视力)的原因,并没有太多的时间每时每刻盯着股票软件看,也没法同时分析3000+只股票。而是希望通过统计模型,来发现其中的规律。

这里面分析的方法都比较基本,也不涉及到诸如深度学习的东西(本人并不排斥深度学习,但从网上很多已有例子来看,在样本空间有限的情况下,过于复杂的深度学习结构容易导致模型的过拟合)。这里主要应用了一些基本的买卖策略和普通的机器学习策略。

采集的数据类型

根据需要,前期主要采集了写数值型的数据,包括:

  1. 股票每个财季的基本信息,这里主要是从每个财季的财报里面获取的,获取的网址是: [东方财富](http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=YJBB21_YJBB&token=70f12f2f4f091e459a279469fe49eca5&st=scode&sr=1&p=1&ps=50&js=var apple={pages:(tp),data: (x),font:(font)}&filter=(reportdate=2020-03-31))。 这里面获取了指定报告期的股票数据,包含同比增长,环比增长,每股收益率等等。
  2. 股票的每日数据,这里获得的是每只股票每天的开盘,收盘,最高,最低价,获取的网站是: 网易财经
  3. 股票每日资金流向数据,获取网站是 网易财经
  4. 后期分析的时候,额外加上了股东变化的数据,通过新浪财经获取:新浪财经

以上都是一些获取链接的例子,之后会详细描述具体爬取步骤。

数据库准备

先基本计算了下,A股所有股票每日的数据和每个财季的数据所占空间大概也就几个G的大小,所以也没必要使用复杂的分布式数据库,本地搭建个数据库也就够用了。

看了许多博客和专栏,很多人爬取股票的数据使用的是mysql database,个人觉得,对于这种结构化的行式数据,使用mysql 或者 cassandra 都是不错的选择。但本人当时恰好工作上使用 mongo 比较多,就顺手使用 mongodb 来做本地数据库了。没有太比较过 mysql 和 mongo 在读写性能的区别 (主要是读性能,因为写性能的瓶颈在于网络爬取速度),欢迎拍砖。

废话不多说了,直接上爬取流程把:

数据爬取和存储

股票的财季数据

在爬取之前,我们先分析一下上述网站能提供的资料信息,[东方财富](http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?type=YJBB21_YJBB&token=70f12f2f4f091e459a279469fe49eca5&st=scode&sr=1&p=1&ps=50&js=var apple={pages:(tp),data: (x),font:(font)}&filter=(reportdate=2020-03-31)) 链接提供的信息如图:

在这里插入图片描述

以上链接是给定报告期 (2020-03-31) 的股票基本信息,由于该报告期有3000+只股票,一页网页往往显示不下,所以,实际上是分页显示,这里一共有99页。上述贴图显示的是第一页信息,如需显示更多新页面,把 url 中的 p=1 改成 p=2,3,4,5… 就行了。第一页第一只股票是 平安银行 (000001)。第二页如下:

在这里插入图片描述

还是回到第一页显示的信息,如平安银行,有以下字段,分别解释如下(有些貌似没有统计意义的字段略过)

  • scode
  • sname
  • securitytype
  • trademarket
  • latestnoticedate (这个是最后一次报告时间)
  • reportdate (这个是报告期,这里是给定的 reportdate + HHMMSS, “020-03-31T00:00:00”)
  • publishname (行业分类)
  • firstnoticedate (这个是第一次报告时间,记住,这个是实际上的财报公告时间
  • basiceps (每股收益)
  • cutbasiceps (扣除非经常性损益后的基本每股收益)
  • totaloperatereve (总营收)
  • ystz(营收同比增长率)
  • yshz(营收环比增长率)
  • parentnetprofit(净利润,个人理解为归属母公司的净利润)
  • sjltz(净利润同比增长)
  • sjlhz(净利润环比增长)
  • roeweighted(净资产收益率)
  • bps(每股净资产)
  • mgjyxjje(每股现金流)
  • xsmll(销售毛利率)

我们从截图看到,具体数字部分,比如 totaloperatereve 所对应的数字是编码的,可读性比较差。其对应的数字可以在每页最后部分找到:

在这里插入图片描述

我们在爬取数据后,需要对数字编码进行转换,并存取到数据库中。代码如下:

class NameScraper:
    def __init__(self):
        """ Initialization """
        self.url = "http://dcfm.eastmoney.com/em_mutisvcexpandinterface/api/js/get?"
        self.params = {
            'type': 'YJBB21_YJBB',
            'token': '70f12f2f4f091e459a279469fe49eca5',
            'st': 'scode',
            'sr': 1,
            'p': 1,
            'ps': 50,
            'js': 'var apple={pages:(tp),data: (x),font:(font)}',
            'filter': '(reportdate=^2020-03-31^)'
        }
        self.encoding = "utf_8_sig"
        self.name_mgr = DBManager("name_all")
        self.years = range(2015, 2020)
        self.seasons = ["03-31", "06-30", "09-30", "12-31"]
        self.network_max_try = 10

    @staticmethod
    def parse_font(fonts):
        """ Since the numbers are encoded in original page, we need to parse the encoder to mapping file """
        font_mapping = dict()
        map_field_key = "FontMapping"
        if map_field_key in fonts.keys():
            for f in fonts[map_field_key]:
                font_mapping[f["code"]] = f["value"]
        else:
            print("Error, font has not key " + map_field_key)
        return font_mapping

    @network_decorator
    def get_response(self):
        return requests.get(self.url, params=self.params).text

    def get_table(self, page):
        """ Get one page from site"""
        self.params['p'] = page
        response = self.get_response()
        if response is None:
            return None, None
        pat = re.compile("var\sapple={pages:(\d+),data:\s+(\[.*\]),font:(.*)}")
        page_all = re.search(pat, response)
        if page_all is None:
            print("Not found for wep page: " + str(page) + ", now exit!")
        page_group = page_all.group(1)
        data_group = page_all.group(2)
        font_group = page_all.group(3)
        debug_print("Page " + str(page) + " has " + str(page_group) + " entries!", 1)
        font = json.loads(font_group)
        font_dict = NameScraper.parse_font(font)
        for key in fo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值