0 准备工作
首先,使用pip方法安装pandas和pandas-datareader两个功能包. 安装的方法十分简单,以管理员身份运行cmd. 输入以下命令。
$ pip install pandas
$ pip install pandas-datareader
需要注意的是,安装pandas时将自动安装numpy等功能包,因此可以使用pandas即代表安装了numpy功能包。今后,在安装所有Python所需功能包时,皆可使用以上方法。
在Yahoo Finance对API进行升级后,你可能需要安装最新开发版本(latest development version)的pandas-datareader(目前是0.5.0版本),才能对雅虎财经的部分数据进行访问,安装方法可以参考原git。
pandas-datareader包中的pandas_datareader.data.DataReader函数可以根据输入的证券Ticker,起始日期和终止日期来返回包含所有历史日价格的数据,其数据类型是DataFrame,这是pandas包引入的一个数据类型。在这里假设需要苹果公司(Ticker: AAPL)从2016年初到今天(2017年4月6日)的历史日价格。
到这里,打开你的Python代码编辑器,你便完成了所有的准备工作。
1 获取股价数据
此处需使用的包是datetime,pandas,和pandas-datareader. 导入datetime的原因是,我们要使用datetime包中的datetime.datetime.today()函数来调用今天的日期。
import datetime
import pandas as pd
import pandas_datareader.data as web
## !!! here it is 'pandas_datareader' rather than 'pandas-datareader'
在以上代码中,import A as B 的作用是导入A,并给A起一个别名叫做B. 此例中,pandas_datareader.data这个名称显然过长,因此给它起一个别名叫做web,这样在后文中使用pandas_datareader.data.DataReader函数时,直接使用web.DataReader即可。一定要注意的是,这里的pandas_datareader中使用的是下划线'_',而非在pip安装时使用的连接符‘-’.
接下来,设置起始日期和终止日期。使用datetime.datetime函数指向给定日期,使用datetime.date.today函数指向今天的日期。运行DataReader函数并将其保存到一个名为prices的变量中。
start = datetime.datetime(2016, 1, 1) # or start = '1/1/2016'
end = datetime.date.today()
prices = web.DataReader('AAPL', 'yahoo', start, end)
print prices.head() # print first rows of the prices data
注意DataReader函数中第二个参数代表数据来源,DataReader支持包括雅虎、谷歌在内的十数种数据来源,本篇笔记只关注来源为雅虎财经的数据。
观察一下获得的prices数据的前六行(含列名),
Open High Low Close Volume \
Date
2016-01-04 102.610001 105.370003 102.000000 105.349998 67649400
2016-01-05 105.750000 105.849998 102.410004 102.709999 55791000
2016-01-06 100.559998 102.370003 99.870003 100.699997 68457400
2016-01-07 98.680000 100.129997 96.430000 96.449997 81094400
2016-01-08 98.550003 99.110001 96.760002 96.959999 70798000
Adj Close
Date
2016-01-04 102.612183
2016-01-05 100.040792
2016-01-06 98.083025
2016-01-07 93.943473
2016-01-08 94.440222
这个DataFrame的index是日期,总共有六列数据,通常情况下我们只关注最后一列Adjusted Closing Price 并使用它计算收益率。Adj Close的好处是已将所有的权重、分割和股利分发等因素考虑在了价格中进行调整。
2 获取股利数据
pandas-datareader包也可以用来获取股利或股票分割等数据,只要将DataReader函数中的数据源参数修改为'yahoo-actions'即可。
actions = web.DataReader('AAPL', 'yahoo-actions', start, end)
print actions.head()
这样返回的actions仍是一个DataFrame类型的变量,其index为日期。观察前六行的数据。
action value
2017-02-09 DIVIDEND 0.57
2016-11-03 DIVIDEND 0.57
2016-08-04 DIVIDEND 0.57
2016-05-05 DIVIDEND 0.57
2016-02-04 DIVIDEND 0.52
此处的action表示证券所进行的操作,如派发股利,或股票分割等等,而value则表示操作值。我们可以看到,苹果公司最近一笔股利发放是在2017年2月9日,每股发放了0.57美元的股利。
值得一提的是,如果在给定日期内,该证券并没有操作活动,DataReader函数将返回一个空的DataFrame,既没有index,也没有列名。
print web.DataReader('AAPL', 'yahoo-actions', datetime.datetime(2017, 4, 1), datetime.date.today())
输出结果为,
Empty DataFrame
Columns: []
Index: []
3 合并股利和股价数据
因为actions的index是prices的index的一个子集,所以我们可以直接将actions的各列添加到prices后面。下面介绍两种合并股利和股价的方法。
第一种方法思路比较简单,直接将actions的每一列提取出来,添加到prices后面。
一个DataFrame变量的某一列单独提取出来,是一个Series变量,这也是pandas包中独有的一个数据类型。通常,在列名不含空格和连接符的情况下,可以直接将列名作为DataFrame变量的Attribute进行调用。如actions.action即为actions中action这一列,它的类型是Series. 而在prices变量中,‘Adj Close’列,因为列名中含有空格,只能使用prices['Adj Close']来调用。
Attribute可以理解为一个对象(如prices,actions,甚至start,end等等)所包含的一系列属性、方法、函数等等,如在之前使用的prices.head()即是在调用prices的head这一Attribute.
将actions的列调用出来后,即可对prices的新列进行赋值。
prices['action'], prices['value'] = actions.action, actions.value
print prices
观察合并后的数据中间的某几行。
Open High Low Close Volume \
Date
...
2016-02-01 96.470001 96.709999 95.400002 96.430000 40943500
2016-02-02 95.419998 96.040001 94.279999 94.480003 37357200
2016-02-03 95.000000 96.839996 94.080002 96.349998 45964300
2016-02-04 95.860001 97.330002 95.190002 96.599998 46471700
2016-02-05 96.519997 96.919998 93.690002 94.019997 46418100
2016-02-08 93.129997 95.699997 93.040001 95.010002 54021400
...
Adj Close action value
Date
...
2016-02-01 93.923996 NaN NaN
2016-02-02 92.024676 NaN NaN
2016-02-03 93.846074 NaN NaN
2016-02-04 94.600127 DIVIDEND 0.52
2016-02-05 92.073538 NaN NaN
2016-02-08 93.043048 NaN NaN
...
可以发现,所有没有action和value的日期,该处数值将为NaN(Not a Number),而有效的股利数据被加入了相应行中。
第二种方法是使用pandas包中的merge函数,它可以根据一定规则将两个DataFrame变量合并。
prices = pd.merge(prices, actions, how='outer', left_index=True, right_index=True)
print prices
以上代码,可以得到与第一种方法同样的结果。其中how='outer'是说最后合并结果的长度将是prices和actions中较长的长度,因此才会有NaN的出现。left_index和right_index都设为True,即当左右DataFrame的index相符时才合并。
4 输出为CSV文件
DataFrame类型有一个Attribute可以直接将数据导出为CSV文件。如将合并好的股价和股利数据保存到根目录out文件夹中,并起名为AAPL.csv.
prices.to_csv('.\\out\\AAPL.csv)
其中.to_csv()方法需要一个字符型参数,即所需保存路径。在该字符串中,'.'表示working directory,使用PyCharm等编译器的话,一般都是代码文件所在的目录。而两个反斜杠表示文件夹的层级关系,之所以用两个,是因为''在字符串中有转义作用。
需要注意的一点是,此例中,'.\out'文件夹应事先建好。直观的方法是直接新建文件夹,并命名为'out'. 使用Python的os包也可以实现。
import os
os.mkdir('.\\out')
将prices导出为CSV文件后,可以打开查看。也可以使用EXCEL进行操作。
Date,Open,High,Low,Close,Volume,Adj Close,action,value
...
2016-02-01,96.470001,96.709999,95.400002,96.43,40943500,93.923996,,
2016-02-02,95.419998,96.040001,94.279999,94.480003,37357200,92.024676,,
2016-02-03,95.0,96.839996,94.080002,96.349998,45964300,93.846074,,
2016-02-04,95.860001,97.330002,95.190002,96.599998,46471700,94.600127,DIVIDEND,0.52
2016-02-05,96.519997,96.919998,93.690002,94.019997,46418100,92.073538,,
2016-02-08,93.129997,95.699997,93.040001,95.010002,54021400,93.043048,,
...
Python可以很方便地对金融数据进行相关操作,我将在未来的笔记中讨论具体的做法。如果您发现任何问题或有任何疑问,欢迎指正或讨论。
by JohnnyMOON, COB @UIUC
这只是做Finance作业的学习笔记