我国的股市总受人诟病,99.99%研究国内股票市场的文章都有指责该市场不成熟、不完善的祖传情绪,但一想到我国其他金融工具那×样,学习方法论的时候还是采用股票市场数据比较好吧。可是每次讲下载行情数据,学生都一脸嫌弃:股票数据还不满大街都是嘛!雷神能耍锤子,不代表你猕猴桃也可以。你得上手试一次,才知道这件事儿能不能成。
我们在不同场合对数据获取有不同的要求,一个个数值往Excel里贴那是肯定不行的。今天我们就来讲讲,获取股票行情数据的办法:
tushare库
pandas-datareader库
量化平台
API接口
网络爬虫
1 Tushare库
这里介绍tushare库获取股票行情的三种基本方法:
1.1 get_k_data()
get_k_data(code=None,#股票代码(字符串,不需要标明交易所)start=”,#起始时间,必须是YYYY-MM-DD格式(字符串)end=”,#终止时间ktype=’D’,#数据频率,可接受周W、日D、月M和5、15、30、60分钟(都是用字符表示的)autype=’qfq’,#前复权,可选hfq(后复权)和None(不复权)index=False,#股指retry_count=3,pause=0.001)#与爬取技术相关的参数
这个函数的优点很多:
速度快
股票代码不用区分交易所
交易频率可控
close是前复权的
返回结果只有高开低收和成交量
我们以600000为例:
importtushareastsdf=ts.get_k_data(‘600000′)df.head()
作为量化分析的基础,这样干净的数据让人眼前一亮;但我们仍然发现一个问题,课上我们讲过get_k_data的结果中数据索引不是日期。于是我们用以下代码将date列转化为索引:
importpandasaspddf.set_index(pd.to_datetime(df.date),inplace=True)
每次获取数据都需要转换索引,这是我当时拼命在课堂上缅怀pdr的主要原因。
1.2 bar()
由于课堂上我们用聚宽来获取期货数据,所以我没有提tushare获取非股票数据的方法。其实从0.8.0版(2017年6月)开始,tushare就是可以获取期货行情的;1.0.2版(2017年10月)的一次重大调整中,各种金融工具开始共用一个新的bar()函数。很遗憾,备课的时候我没能跟进该库的新发展,这里有必要隆重介绍一下。
bar()函数的语法为:
bar(code,#股票代码(字符串)conn=None,#接口:需要修改为ts.get_apis()start_date=None,#起始日期,接受YYYY-MM-DD和YYYYMMDDend_date=None,#终止时间freq=’D’,#数据频率,支持年Yasset=’E’,#金融工具类型代码,E是股票和交易所基金,INDEX是沪深指数,X是期货/期权/港股/中概美国/中证指数/国际指数adj=None,#复权类型factors=[],#自定义换手率(tor)和量比(vr)指标retry_count=3)#与爬取技术相关的参数
我们先说bar()函数与get_k_data()的区别:
需要明确接口ts.get_apis()
起止时间支持两种日期输入方式
数据频率支持“年”,参数名改为freq
默认返回不复权的行情
返回的指标更多
支持多种金融工具,并用asset参数区分
(重要)索引是时间
所以你看,福祸相依,喜忧参半:
#股票ts.bar(‘600000’,conn=ts.get_apis())#期货ts.bar(‘AU’,conn=ts.get_apis(),asset=’X’)#X是其他金融工具的统称,我们也可以明确一下黄金期货是上海商品期货交易所的,于是asset=’OS’,但没有必要啦#不难看出,不同金融工具的返回指标存在差异
与bar()函数类似,1.0.2版改动中还增加一个tick()函数,用于获取每笔交易。我们用得比较少,感兴趣的同学可以通过help(ts.tick)来查看。
1.3 tushare pro
8月10日,tushare给我推送了一个重磅消息。它们要放弃原先的数据获取方式了——突然间我好惶恐,我××刚学会呀!取而代之的是,一种被称为tushare pro的东西。两者的差别在于:
tushare是给出方法,我们自己从相应数据网站爬取数据
tushare pro是它先把数据都爬下来放自己家里,你如果需要数据,就用它发布的Token去连接它自己家的接口
好处很明显,获取数据会更稳定,有就是有,没有就是没有;坏处是,指不定哪天TOKEN就收费了(或者用积分换流量)。我找到它的网站,先注册了一个账号。
注册获取TOKEN:
https://tushare.pro/register?reg=126291
我大概瞅了一眼手册,语法全都变了,这是要朕去死啊!没关系,先看看能不能获取K线所需的数据,保障我下学期的课能开起来才是最重要的。
Step1:初始化常规操作
importtushareasts#引入前先升级ts.set_token(‘bd********f2’)#设置TOKENpro=ts.pro_api()#初始化接口
Step2:获取股票行情
#daily函数:pro.daily(ts_code=None,#股票代码(必须有交易所标记)trade_date=None,#交易日期(与代码二选一)start_date=None,#起始日期end_date=None)#终止日期#query函数:pro.query(‘daily’,#日行情<其他参数相同>)
我个人感觉这一串代码很繁琐,如果不是为了收钱,真的没有必要这么做。你看初始化就要三行,daily函数那些参数的名字又臭又长。而且截至发稿,还没有期货数据,手册也没说怎么调整采样频率和复权。
2 pandas-datareader库2.1 获取美股行情
pandas-datareader一直都能获取美股数据。0.6.0版说Google源不稳定,这根本对国内没有影响,毕竟从来没有连上过。从手册上我们可以看到不少pandas-datareader的数据源,比如IEX、Robinhood、Morningstar等等,用法非常相似,只是改动了数据源的参数。以拼多多的股票为例(PDD):
importpandas_datareaderaspdr#注意是下划线pdd=pdr.data.DataReader(‘PDD’,’iex’,’2018-01-01′,’2018-08-20′)#用IEX源
从结果上看,IEX源的数据返回最简洁,索引是时间(注意不是时间戳,而是字符串),一切都很完美。很遗憾,我不知道怎么才能下载A股行情,尤其是Morningstar源,明明网站是能查到A股行情的……
2.2 用fix_yahoo_finance库修正雅虎源
雅虎(Yahoo!)近些年的发展就像一位长者,它明明改变了世界,可人们关心的却永远都是它还在不在了。人心不古啊!pandas-datareader库从2018年2月开始就放弃雅虎源了,后来有人写了一个fix_yahoo_finance库,修正了这个问题。不知道这种组合能坚持多久——且用且珍惜吧!
安装pandas-datareader库和fix_yahoo_finance库都可以用pip3 install,非常方便。安装好之后,执行修正命令:
importpandas_datareaderaspdrimportfix_yahoo_financeasfyffyf.pdr_override()#修正命令
然后,就正常使用get_data_yahoo函数来获取行情数据:
df=pdr.data.get_data_yahoo(‘000001.SZ’,start=’2017-01-01′,end=’2018-12-12′)
一切都仿佛回到了2017年我备课那会儿,我的PPT也不用改了,感恩!参数只有两个小点需要注意下:
所有pdr中的start和end都接受’YYYY-MM-DD’字符串和datetime类型,这是我觉得最方便的地方,不同的场景下,两种时间都可能被用到
get_yahoo_data接受actions参数,可选inline,同时获取配股数据,用来手工计算复权
3 量化平台
从量化平台里取数据出来有点本末倒置,但考虑到我们有1/4学期都在量化平台上折腾,不少同学已经很熟悉平台定制的函数,甚至自己都不安装python在本地而使用平台提供的notebook,这不失为一种灵活的方法。
我们以聚宽量化平台的黄金期货数据为例:
网址:
https://www.joinquant.com/research
importjqdata#载入平台专用函数库importpandasaspd#用到pandas中的to_excel方法hj=attribute_history(‘AU9999.XSGE’,count=300)hj.to_excel(r’hjdata.xlsx’)#这时notebook根目录里面就会出现一个名叫hjdata的Excel文件#将其下载下来,放在D盘根目录下df=pd.read_excel(r’d:\hjdata.xlsx’)
有几点需要说明:
attribute_history只是行情获取函数之一,如果你觉得返回值的种类太少,可以尝试其他函数
count=300表示过去300个交易日,我们习惯了用起止日期来限定数据范围,但如果你接受count的设定,就会觉得很方便
to_excel比to_csv好用,因为存入Excel中的数据能保持索引为时间戳
量化平台的数据是不能直接存到本地的,只能先保存到notebook中,再下载
当然,如果我们使用量化平台来回测,就真的没有必要下载数据在本机处理。平台回测可以动态复权、自动调整滑点,而很多常用的库都已经内嵌在平台中,甚至能用tushare,确实很方便。(缺点就是如果真有一个库没安装,比如tensorflow、CNTK、keras、pyecharts,就直接瞎了。)
已经内嵌的库:(加粗表示课上用过)anyjson;arch;beautifulsoup4;cvxopt;gensim;graphviz;hmm;hmmlearn;jieba;Lasagne;matplotlib;mpl_toolkits;numpy;openpyxl;pandas;pillow;prettytable;pybrain;PyBrain;pycrypto;pymc;pywt;requests;scipy;seaborn;sklearn;snownlp;statsmodels;tables;talib;theano;tushare;xlrd;xlwt;zipline
4 API接口
我们还有一招:找一个提供行情数据的网站,直接下载,比如雅虎。但既然写出来当作一个方法,肯定不能这么全手动。我们发现,一些网站给出了股票行情的API接口。
4.1 雪球接口(推荐)
我个人最喜欢雪球网的数据,响应速度快,返回值种类多,整理起来也方便。
https://xueqiu.com/stock/forchartk/stocklist.json?symbol=<股票代码>&period=<采样频率>&type=<复权类型>&begin=<起始时间戳>&end=<终止时间戳>
股票代码前要加SZ或SH
采样频率默认为1day,也可以设置1month或1year
复权可填before(前复权)和after(后复权)
返回数据种类多,有常用均线和macd的两线一柱
(重点)必须先刷一次雪球主页获取cookies,再按以上网址获取行情,否则返回
{“error_description”:”遇到错误,请刷新页面或者重新登录帐号后再试”,”error_uri”:”/stock/forchartk/stocklist.json”,”error_code”:”400016″}
这也就导致爬虫不能简单使用requests.get(url)的形式,而要利用访问主页的cookies来访问目标链接。在这里给出核心的代码,以防同学们哪天用到:
#importrequests;importjson;importpandasaspd#简单说,Session是保持浏览方式的类,后续每一个get方法都沿用相同的请求头、cookies、连接方式session=requests.Session()#伪造请求头才能登录首页session.headers={‘User-Agent’:’Mozilla/5.0′}#登录首页,目的是记录连接方式(包括cookies)session.get(‘https://xueqiu.com/’)#这时再get包含行情信息的网页就能成功了r=session.get(‘https://xueqiu.com/stock/forchartk/stocklist.json?symbol=SH600000&period=1day&type=before’)#最后把数据保存为数据框df=pd.DataFrame(json.loads(r.text)[‘chartlist’])
缺点是起止时间(用start、end和_设置)均使用时间戳对应的数字表示,用不惯。多说一句,我们不一定非要用Session类,也可以用浏览器找到名称为aliyungf_tc、域名为xueqiu.com的cookies,人工保持连接。
4.2 网易接口
http://quotes.money.163.com/service/chddata.html?code=<股票代码>&start=<起始时间>&end=<终止时间>&fields=<指标用分号分隔>
股票代码前加0(沪市)或1(深市)
直接返回csv
csv表格的列标签是中文的
起止时间用YYYYMMDD表示
举例:
http://quotes.money.163.com/service/chddata.html?code=0600000&start=20170101&end=20180820
4.3 和讯接口
和讯接口的特点是可以控制采样的数量和方向。
http://webstock.quote.hermes.hexun.com/a/kline?code=<股票代码>&start=<时间定位点>&number=<采样数量和方向>&type=<采样频率代码>
股票代码要添加小写的交易所代码,如sse600000、szse000001
时间定位点是YYYYMMDDhhmmss表示的,如果取日数据,hhmmss就不重要
采样数量和方向用±整数表示,正数表示从时间定位点开始,沿时间轴取若干组数据,负数表示沿时间轴的反方向取若干组数据
采样频率代码可取5(日K)、0(2分钟)、1(5分钟)、2(15分钟)、3(30分钟)、4(60分钟)、6(周)、9(月)、12(季度)、15(年)(其实可以取0-18,注意没有1分钟)
数据都在Data键内,可以直接提取
把网址中的kline换成minute可得分钟数据,用于绘制分时图
和讯还提供了更简单的接口:
全部日线行情:
http://flashquote.stock.hexun.com/Quotejs/DA/<交易所标记>_<股票代码>_DA.html
全部分时行情:
http://flashquote.stock.hexun.com/Quotejs/MA/<交易所标记>_<股票代码>_MA.html
交易所标记中的1表示沪市,2表示深市
接口差别在D和M,注意有两处
4.4 腾讯接口
http://data.gtimg.cn/flashdata/hushen/latest/<采样频率>/<股票代码>.js
采样频率可以是daily、weekly或monthly
返回100条数据
将latest/<采样频率>整体替换为minute,得当日分时图数据
将latest/<采样频率>整体替换为4day,<股票代码>用<交易所>/<股票代码>表示,可得5日分时图数据
将latest/daily整体替换为daily/<年份后两位>,可得特定年份的行情,不支持其他频率和2000年前
举例:
http://data.gtimg.cn/flashdata/hushen/latest/daily/sh600000.js
http://data.gtimg.cn/flashdata/hushen/minute/sh600000.js
http://data.gtimg.cn/flashdata/hushen/4day/sh/sh600000.js
http://data.gtimg.cn/flashdata/hushen/daily/18/sh600000.js
腾讯接口的好处是它直接返回js,如果我们用Echarts画图,可以直接导入。
4.5 新浪接口
关于新浪财经接口的介绍文章很多,但我很少用,不太清楚现在有没有历史数据接口了。
http://hq.sinajs.cn/list=<股票代码>
股票代码由交易所标记和代码组成,比如sh600000、sz000001
返回值很多,常用的包括股票名称0、今日开盘价1、昨日收盘价2、当前价格3、今日最高价4、今日最低价5、成交量8、日期-2和时间-1,可见这是一个实时行情数据接口
有趣的是,新浪提供了一个行情图接口,返回png格式的图片:
http://image.sinajs.cn/newchart/<采样频率>/n/<股票代码>.png
股票代码同样是交易所标记与代码的组合,如sh600000
采样频率可选填daily、weekly、monthly(三种K线图)或min(分时图)
K线图除了蜡烛外,还提供交易量和5、10、30个采样周期的均线
图片背景是透明的,可以直接插PPT里,就是配色比较丑
5 直接爬取行情数据
我自己是最不愿意写爬虫的,能用轮子就绝不自己写,能Ctrl C就绝不打字;但考虑到上述方案随时都可能消失或收费,我们总得给自己留一条后路。简单说一下我的备用方案:
5.1 从雅虎财经上爬
https://finance.yahoo.com/quote/<股票代码.交易所>/history
我们以600000为例:
#用requests库获取网页源代码r=requests.get(r’https://finance.yahoo.com/quote/600000.SS/history’)#用re库获取源代码中的行情数据部分#发现源代码中居然有用json保存的数据(笑出声)pat=re.compile(r'”HistoricalPriceStore”:\{“prices”:(\[.*?\])’)json_data=pat.search(r.text).group(1)#将json转换为数据框df=pd.read_json(json_data)#将date放入索引df.set_index(df.date,inplace=True)
如果我们不以json(而是以字典)的形式转化数据框,就会发现雅虎保存日期是用时间戳对应的数字,我这个遇到数据类型就晕菜的人只能写一长段代码转来转去,所幸最后转出来了。
#与上一段代码的作用相同#lst=pat.search(r.text).group(1)df=pd.DataFrame(columns=[‘date’,’open’,’high’,’low’,’close’,’adjclose’,’volume’,’amount’,’type’,’data’])fordailyineval(lst):df=pd.concat([df,pd.DataFrame(pd.Series(daily)).T],ignore_index=True)defchange(num):returnpd.Timestamp(time.strftime(“%Y-%m-%d”,time.localtime(int(num))))df.set_index(pd.to_datetime(list(map(change,list(df.date)))),inplace=True)
5.2 爱搞搞
毕竟A股数据遍地都是,我们没必要盯着一家,比如我随手搜了一个叫爱搞搞的网站:
http://www.aigaogao.com/tools/history.html?s=<股票代码>
源代码是乱了一些,但还是很好爬的:
#用requests库获取网页源代码r=requests.get(r’http://www.aigaogao.com/tools/history.html?s=600000′)#把日期和高开低收行情挑出来pat=re.compile(r'<aname=”([\d/]{10})”>\1</a><\/td><td.*?>([\d\.] ?)</td><td.*?>([\d\.] ?)</td><td.*?>([\d\.] ?)</td><td.*?>([\d\.] ?)</td>’)res=pat.findall(r.text)#转成数据框df=pd.DataFrame(res,columns=[‘Date’,’Open’,’High’,’Low’,’Close’])#把索引设置为时间戳df.set_index(pd.to_datetime(a.Date),inplace=True)#丢弃Date列df.drop(‘Date’,axis=1,inplace=True)
当然了,再说一下原则,能站着获取数据,就坚决不爬。
第4和第5种方法没有本质不同,都是把网页上的行情信息获取下来整理备用,只不过API接口数据更整齐,网站往往会将其直接导入js代码区域,因此有的API数据干脆就是var赋值语句;而从普通网站上爬下来的源代码往往较乱,特别是一些不怎么讲究的网站,可能需要更多地使用正则表达式,而不是对标签的解析(xpath或bs4)。
6 小结
好了,到此为止我们讲完了获取股票行情数据的5种方法。当然,我们还能想到很多其他的办法,比如去数据库下载、上淘宝买、找程老师要……足够我们在各种场合下愉快地玩耍了。
总结一下:tushare和pandas-datareader库使用最方便,一次调试反复调用,记住个别函数就够了;量化平台也还行,特别是我们决定在平台自己的notebook上写代码的时候,这可能是最好的办法;API接口和网络爬虫都有点费事,但非常灵活,可以得到一些其他途径得不到的数据,实时监测更是不可或缺。