Python金融分析(四):开放数据源

前言

自从我们的社会进入所谓的“信息时代”以来,我们已经被大量的信息或数据所吞没。出于这个原因,对拥有数据处理技能的人员,如商业分析师和数据科学家的需求大大增加。

Kane(2006) 提出了“开源金融”的概念。主要有三个部分组成:

  • 使用开源软件测试假设和实现投资策略;
  • 低廉的金融数据获取权限;
  • 可复现已发布的研究成果。

那么,在《Python for Finance》(Yan, 2017)一书中将这三个部分归纳为 开放软件、开放数据和开放代码 。 而事实上,如何获取开放的数据,尤其是金融数据,可能是困扰许多人的一个难题。接下来,我们将介绍“开放数据”的一些来源。

金融数据之free lunch

本文的重点是如何检索经济,财务和会计相关数据,尤其是公共数据。 例如,Yahoo Finance提供丰富的数据,如历史交易价格,当前价格,期权数据,年度和季度财务报表以及债券数据。

此类公开数据可用于估算β(市场风险),波动率(总风险),夏普比率,詹森阿尔法,特雷诺比率,流动性,交易成本以及进行财务报表分析(比率分析)和绩效评估,对于金融分析十分有用。未来我们将会讨论这些,但是先让我们回到如何获得数据。

关于各种类型的经济金融开放数据获取,https://www.economicsnetwork.ac.uk/links/data_free 提供了一个比较全面的介绍,现在免费的午餐越来越难找,如果想找到能够提供股票数据的,大概可以从以下几个中筛选:

  • Quandl (首选)
  • Yahoo Finance (2017年修改API格式,pandas_reader 0.7.0可用)
  • Google Finance (不稳定)
  • Alpha Vantage (免费版有限制)
  • World Trading Data (免费版有限制)

除此以外,pandas_datareader还提供了更多可供选择的数据源,但是能不能用、好不好用,要亲自测试过才知道。

此外,还有一些其他可能会用到的数据,可以从下表的数据源获得:

Name Data types
Federal Reserve Economic Data Interest rates, rates for AAA, AA rated bonds
Prof. French’s Data Library Fama-French factor time series, market index returns, risk-free rate, industry classification
Census Bureau Census data
US. Department of Treasury US. Treasure yield
Bureau of Labor Statistics Inflation, employment, unemployment, pay and benefits
Bureau of Economic Analysis Gross Domestic Product (GDP) and so on
National Bureau of Economic Research Business cycles, vital statistics, report of presidents

通常而言,我们有两种方法可以获取数据:

  • 手动从特定地址下载数据,然后编写Python程序进行检索和处理;
  • 使用各种Python包中现成的函数,例如pandas_reader.data模块中的get_data_yahoo()

两种方法都存在有点和缺点:第一种方法的优点在于我们了解数据的来源,而且由于我们自己编写的程序,逻辑会更加清晰和透明,缺点就是麻烦;第二种方法的优点是快速方便,用户不必关心数据的来源和数据集的结构,缺点是接口可能会变化,导致不兼容或失效,例如matplotlib中原来有finance的子模块,但是现在已经独立为mpl_finance模块(实际上已经不再维护)。

数据读取与分析

从Yahoo Finance读取数据

Yahoo finance提供1960年以来的历史市场数据,最近几年的财务报表,当前报价,分析师建议和期权数据。 历史交易数据包括每日,每周,每月和股息。 历史数据中有几个变量:开盘价(Open),实现高价(High),最低价(Low),交易量(Volume),收盘价(Close)和调整后的收盘价(Adjustment)(根据分配和分红调整)。

1
%matplotlib inline
1
2
3
4
5
6
7
8
9
10
11
import math
import numpy as np
import pandas as pd
import numpy.random as npr
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas_datareader.data as pddata

plt.style.use('ggplot')
mpl.rcParams['figure.figsize']= [15, 9]

1
2
3
4
5
stock_ibm = pddata.DataReader(name='IBM', data_source='yahoo',
start='2015-01-01', end='2015-12-31',
retry_count=3, pause=0.1,
session=None, access_key=None)
stock_ibm.head()
High Low Open Close Volume Adj Close
Date
2014-12-31 161.500000 160.380005 160.410004 160.440002 4011900.0 133.926178
2015-01-02 163.309998 161.000000 161.309998 162.059998 5525500.0 135.278397
2015-01-05 161.270004 159.190002 161.270004 159.509995 4880400.0 133.149841
2015-01-06 159.960007 155.169998 159.669998 156.070007 6146700.0 130.278320
2015-01-07 157.199997 154.029999 157.199997 155.050003 4701800.0 129.426880
1
2
3
4
5
6
7
8
9
10
def get(tickers, startdate, enddate):
def data(ticker):
return pddata.get_data_yahoo(ticker, start=startdate, end=enddate)
datas = map(data, tickers)
return(pd.concat(datas, keys=tickers, names=['Ticker', 'Date']))


tickers = ['AAPL', 'MSFT', 'IBM', 'GOOG']
all_data = get(tickers, '20060101', '20161231')
all_data.head()
High Low Open Close Volume Adj Close
Ticker Date
AAPL 2006-01-03 10.678572 10.321428 10.340000 10.678572 201808600.0 7.120505
2006-01-04 10.854285 10.642858 10.732857 10.710000 154900900.0 7.141460
2006-01-05 10.700000 10.535714 10.690000 10.625714 112355600.0 7.085258
2006-01-06 10.957143 10.650000 10.750000 10.900000 176114400.0 7.268152
2006-01-09 11.028571 10.820000 10.961429 10.864285 168760200.0 7.244339
1
2
3
4
5
6
7
8
9
10
11
# Isolate the `Adj Close` values and transform the DataFrame
daily_close_px = all_data[['Adj Close']].reset_index().pivot('Date', 'Ticker', 'Adj Close')

# Calculate the daily percentage change for `daily_close_px`
daily_pct_change = daily_close_px.pct_change()

# Plot the distributions
daily_pct_change.hist(bins=50, sharex=True, figsize=(12,8))

# Show the resulting plot
plt.show()

output_12_0

1
2
3
4
5
# Plot a scatter matrix with the `daily_pct_change` data
pd.plotting.scatter_matrix(daily_pct_change, diagonal='kde', alpha=0.1,figsize=(12,12))

# Show the plot
plt.show()

output_13_0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from scipy.stats import norm

ret = (stock_ibm['Adj Close'] - stock_ibm['Adj Close'].shift(1))/stock_ibm['Adj Close'].shift(1)
print(ret.head())
ret.plot.hist(bins=50, label='hist')
ret.plot.kde(label='kde')
mu = np.mean(ret)
sigma = np.std(ret)
x = np.linspace(np.min(ret), np.max(ret), 100)
norm_samp = norm.pdf(x, mu, sigma)
plt.plot(x, norm_samp, label='normal')
plt.title("IBM return distribution")
plt.xlabel("Returns")
plt.ylabel("Frequency")
plt.legend()
plt.show()
Date
2014-12-31         NaN
2015-01-02    0.010097
2015-01-05   -0.015735
2015-01-06   -0.021566
2015-01-07   -0.006536
Name: Adj Close, dtype: float64

output_14_1

需要稍微注意的是,手动下载的数据和通过API读取的数据在排序上可能会有所差别:前者是按照由新到旧,后者是由旧到新,在使用数据前注意检查一下。

我们可以用numpy.shift(),然后计算股票的对数收益率。对数收益率具有可加性,也就是说月度对数收益率等于日对数收益率之和,相应地可以计算季和年的对数收益率:

Rannuallog=ΣRmonthlylog=ΣRdailylogR^{log}_{annual}=\Sigma R^{log}_{monthly}=\Sigma R^{log}_{daily}

通过具体示例说明:

1
2
3
4
5
6
7
log_ret = np.log(stock_ibm['Adj Close']/
stock_ibm['Adj Close'].shift(1)).dropna()
ret_monthly = np.exp(log_ret.resample('M', label='right').sum())-1
print(ret_monthly)
ret_yearly = np.exp(log_ret.resample('A', label='right').sum())-1
print(ret_yearly)
print(stock_ibm['Adj Close'][-1]/stock_ibm['Adj Close'][0]-1)
Date
2015-01-31   -0.044440
2015-02-28    0.063701
2015-03-31   -0.008893
2015-04-30    0.067228
2015-05-31    0.005608
2015-06-30   -0.041202
2015-07-31   -0.004119
2015-08-31   -0.079464
2015-09-30   -0.019744
2015-10-31   -0.033731
2015-11-30    0.004625
2015-12-31   -0.012911
Freq: M, Name: Adj Close, dtype: float64
Date
2015-12-31   -0.1074
Freq: A-DEC, Name: Adj Close, dtype: float64
-0.10739973836120964

从FRED获取数据

美联储(the Federal Reserve)有许多与当前经济和历史时间序列相关的数据集。 例如,他们有与利率相关的数据,例如欧元存款利率。我们可以前往美联储的网站(https://www.federalreserve.gov/econresdata/default.html )手动下载,也可以利用pandas_datarader模块下载。

VIX全名是芝加哥期权交易所波动率指数(Chicago Board Options Exchange Volatility Index),用以衡量S&P 500指数未来30日的预期年化波动率,通常可使用S&P 500指数的近月及邻月认购/认沽期权价格计算得出。

VIX基于期权的隐含波动率编制而成,在期权交易中,隐含波动率通常代表着对市场未来风险程度的预期,因此VIX往往被看作是领先市场的风向标。一个高VIX的市场往往意味着恐慌情绪的蔓延,故在海外VIX也被戏称为“恐慌性指数”。

1
2
vix = pddata.DataReader('VIXCLS', 'fred')
vix.head()
VIXCLS
DATE
2010-01-01 NaN
2010-01-04 20.04
2010-01-05 19.35
2010-01-06 19.16
2010-01-07 19.06

肯尼斯·佛伦奇数据库

肯尼斯·佛伦奇与尤金·法马(2013年诺贝尔经济学奖获得者)共同提出了法马-佛伦奇三因子模型(Fama-French three-factor model),一个资本资产定价模型的改进理论。该模型的提出是基于美国股市历史回报率的实证研究结果,目的在于解释股票市场的平均回报率受到哪些风险溢价因素的影响。

肯尼斯·佛伦奇维护了一个数据库——Prof. French’s data library,它包含每日,每周和每月的Fama-French因子和其他有用的数据(http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html )。我们可以从网上直接下载因子的数据集,也可以利用pandas_datarader模块下载:

1
2
3
4
5
ff2 = pddata.DataReader("F-F_Research_Data_Factors_weekly", "famafrench")
ff3 = pddata.DataReader("6_Portfolios_2x3", "famafrench")
ff4 = pddata.DataReader("F-F_ST_Reversal_Factor", "famafrench")
ff2[0].plot()
plt.show()

output_22_0

从美国人口普查局、财政部和劳工统计局获取数据

不得不说,美国政府的数据管理做得还是很好地。我们可以从美国人口普查局的网站上检索数据的相关信息https://www.census.gov/rdo/data/ 并选择下载需要的数据(似乎中国的IP地址不能访问)。此外,在美国财政部、劳工统计局(https://download.bls.gov/ ) 的网站上都可以检索数据。

treasury

小结

我们讨论了经济、金融和会计的各种公共数据来源。对于经济领域,我们可以用美联储、佛伦奇教授的数据库来检索许多有用的时间序列。 对于金融,我们可以使用Yahoo!Finance和quandl来下载历史价格数据。 对于会计信息,例如最近几年的资产负债表和损益表,我们可以使用Yahoo!Finance、各个公司的网站和SEC查找相关信息。

参考文献

  1. https://blog.quandl.com/api-for-stock-data
  2. https://ntguardian.wordpress.com/2018/07/17/stock-data-analysis-python-v2/
  3. http://papers.ssrn.com/sol3/papers.cfm?abstract_id=966354