tushare的源码分析

tushare是一个经典的python环境下的量化工具箱,最近仔细阅读了一下他的代码逻辑,做一定的解析.

开始

tushare的代码是从init.py文件下开始的,我们一块一块的分析.

"""
for trading data
"""
from tushare.stock.trading import (get_hist_data, get_tick_data,
                                   get_today_all, get_realtime_quotes,
                                   get_h_data, get_today_ticks,
                                   get_index, get_hists,
                                   get_k_data,
                                   get_sina_dd)

可以看出,tushare的10个api是从stock下的trading模块中打包出来的,查看trading.py.

先查看trading.py的依赖项

import time
import json
import lxml.html  
from lxml import etree  
import pandas as pd
import numpy as np
from tushare.stock import cons as ct    //引入cons.py
import re
from pandas.compat import StringIO
from tushare.util import dateu as du   //引入util下的dateu.py(工具箱)
from tushare.stock.reference import new_stocks    /
try:
    from urllib.request import urlopen, Request
except ImportError:
    from urllib2 import urlopen, Request

我们可以看出,引入了三个模块,分别是cons.py 作为连接函数,dateu作为日期工具 和 股票数据

API: get_hist_data 个股历史数据

def get_hist_data(code=None, start=None, end=None,
                  ktype='D', retry_count=3,
                  pause=0.001):
"""
    获取个股历史交易记录
Parameters
------
  code:string
              股票代码 e.g. 600848
  start:string
              开始日期 format:YYYY-MM-DD 为空时取到API所提供的最早日期数据
  end:string
              结束日期 format:YYYY-MM-DD 为空时取到最近一个交易日数据
  ktype:string
              数据类型,D=日k线 W=周 M=月 5=5分钟 15=15分钟 30=30分钟 60=60分钟,默认为D
  retry_count : int, 默认 3
             如遇网络等问题重复执行的次数 
  pause : int, 默认 0
            重复请求数据过程中暂停的秒数,防止请求间隔时间太短出现的问题
return
-------
  DataFrame
      属性:日期 ,开盘价, 最高价, 收盘价, 最低价, 成交量, 价格变动 ,涨跌幅,5日均价,10日均价,20日均价,5日均量,10日均量,20日均量,换手率
"""

(code=None, start=None, end=None,
ktype='D', retry_count=3,
pause=0.001):

预分配的是 code股票代码,起始日期start,终止日期end
数据类型是D(r日线),retry_count=3(如遇网络等问题重复执行3次 )
重复请求数据过程中暂停0.001秒

    symbol = _code_to_symbol(code)
    url = ''
    if ktype.upper() in ct.K_LABELS:
        url = ct.DAY_PRICE_URL%(ct.P_TYPE['http'], ct.DOMAINS['ifeng'],
                                ct.K_TYPE[ktype.upper()], symbol)
    elif ktype in ct.K_MIN_LABELS:
        url = ct.DAY_PRICE_MIN_URL%(ct.P_TYPE['http'], ct.DOMAINS['ifeng'],
                                    symbol, ktype)
    else:
        raise TypeError('ktype input error.')
    

数据是从凤凰网上爬下来的,ifeng.com,这段代码主要负责组装参数
第一句 _code_to_symbol(code)
我们查找这个函数

def _code_to_symbol(code):
    """
        生成symbol代码标志
    """
    if code in ct.INDEX_LABELS:
        return ct.INDEX_LIST[code]
    else:
        if len(code) != 6 :
            return ''
        else:
            return 'sh%s'%code if code[:1] in ['5', '6', '9'] else 'sz%s'%code

我们可以看出,_code_to_symbol主要是对输入的股票代码进行判断,然后组装成标准的例如sh600001这种代码的

code in ct.INDEX_LABELS
ct是从cons.py里面引入的,在cons里面,
INDEX_LABELS = ['sh', 'sz', 'hs300', 'sz50', 'cyb', 'zxb', 'zx300', 'zh500']
INDEX_LIST = {'sh': 'sh000001', 'sz': 'sz399001', 'hs300': 'sz399300',
'sz50': 'sh000016', 'zxb': 'sz399005', 'cyb': 'sz399006', 'zx300': 'sz399008', 'zh500':'sh000905'}

如果输入的code是'sh', 'sz', 'hs300', 'sz50', 'cyb', 'zxb', 'zx300', 'zh500'这几种里面的一个,返还一个示例代码
'sh': 'sh000001', 'sz': 'sz399001', 'hs300': 'sz399300',
'sz50': 'sh000016', 'zxb': 'sz399005', 'cyb': 'sz399006', 'zx300': 'sz399008', 'zh500':'sh000905'

如果输入的是一个6位数的股票代码

return 'sh%s'%code if code[:1] in ['5', '6', '9'] else 'sz%s'%code
则查看代码的第一位code[:1]
如果是5,76,9开头的,则是sh 上证股票,否则是sz 深圳股票

如果输入的既不是sh这种,也不是6位数代码, return ''

第二句
url = ''
预设一个url

第三句

  if ktype.upper() in ct.K_LABELS:
        url = ct.DAY_PRICE_URL%(ct.P_TYPE['http'], ct.DOMAINS['ifeng'],
                                ct.K_TYPE[ktype.upper()], symbol)

将函数中的ktype(默认是'D'),先转化成大写的(upper),这里是避免用户的错误输入,比如说输入'd',提高容错率

在con里面,我们找到了K_LABELS = ['D', 'W', 'M']
及 日线,周线和月线

 url = ct.DAY_PRICE_URL%(ct.P_TYPE['http'], ct.DOMAINS['ifeng'],
                                ct.K_TYPE[ktype.upper()], symbol)

此句是拼装url
DAY_PRICE_URL = '%sapi.finance.%s/%s/?code=%s&type=last'
K_TYPE = {'D': 'akdaily', 'W': 'akweekly', 'M': 'akmonthly'}
其实就是
http://api.finance.ifeng.com/akdaily/?code=sh600010&type=last
http://api.finance.ifeng.com/akweekly/?code=sh600818&type=last

日线
周线

这件事其实也非常简单,他是根据凤凰财经的日线图找到的

抓取过程的原理

我们可以监控network,然后分别点击日k,周k,月k就能看到对应的请求了
本质是一个ajax请求的api
之后的分钟线等原理都是一致的,不再过多陈述

下面就进入到数据获取的环节,数据获取使用的是 urllib.request模块,在模块的最初的引入urllib或者urllib2 是因为python2和python3的兼容.

 for _ in range(retry_count):
        time.sleep(pause)
        try:
            request = Request(url)
            lines = urlopen(request, timeout = 10).read()

我们request这个url,并且打开(下载这个网页),放到lines里面
如果lines=no data (判断条件是lines的长度<15,因为no data的长度是14),我们返还NONE,如果出现异常,则打印exception

      if len(lines) < 15: #no data
         return None
      except Exception as e:
          print(e)
      else:
          js = json.loads(lines.decode('utf-8') if ct.PY3 else lines)
          cols = []
          if (code in ct.INDEX_LABELS) & (ktype.upper() in ct.K_LABELS):
              cols = ct.INX_DAY_PRICE_COLUMNS
          else:
              cols = ct.DAY_PRICE_COLUMNS
          if len(js['record'][0]) == 14:
              cols = ct.INX_DAY_PRICE_COLUMNS
          df = pd.DataFrame(js['record'], columns=cols)
          if ktype.upper() in ['D', 'W', 'M']:
              df = df.applymap(lambda x: x.replace(u',', u''))
              df[df==''] = 0
          for col in cols[1:]:
              df[col] = df[col].astype(float)
          if start is not None:
              df = df[df.date >= start]
          if end is not None:
              df = df[df.date <= end]
          if (code in ct.INDEX_LABELS) & (ktype in ct.K_MIN_LABELS):
              df = df.drop('turnover', axis=1)
          df = df.set_index('date')
          df = df.sort_index(ascending = False)
          return df
  raise IOError(ct.NETWORK_URL_ERROR_MSG)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • sì 支zhī茶chá 对duì 酒jiǔ,赋fù 对duì 诗shī,燕yàn子zi 对duì 莺yīng 儿é...
    每个人的孟母堂阅读 1,187评论 0 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,511评论 18 139
  • 1.创建文件夹 !/bin/sh mkdir -m 777 "%%1" 2.创建文件 !/bin/sh touch...
    BigJeffWang阅读 9,999评论 3 53
  • 以下是本人通过阅读Grizzly版OpenStack源码,整理的简要的Nova模块源码结构,希望和大家相互交流。 ...
    Chenzongshu阅读 2,660评论 0 50
  • 【回顾盘面】: 10月13日(周五)两市依托5日线的支撑再收阳线,最后沪指报3390点,涨0.13%;深成指报1...
    齐利阅读 292评论 5 17