pandasを使用して株価の日次リターン、累積リターン、リターン分布、正規Q-Qプロットを行う

以前、Rを使って試したことと同様のことをpythonで試す。

・「urcaパッケージを使用した定常性検定
・「urcaパッケージを使用した定常性検定 〜 続編

主なお題は次。

・日次リターンの計算
・日次リターンから累積リターンを求める
・リターン分布を描く
・正規Q-Qプロットを行う

まずは前回まで使用したのと同じコードで株価データを取得。

#coding:utf-8
import pandas as pd
import datetime as dt
import pandas_datareader.data as pdd
import matplotlib.pyplot as plt

######## Define Method ########
def getMultiStockData(tickerList, date_from, date_to):
    def getStockData(ticker):
        stockData = pdd.DataReader(ticker, 'yahoo', date_from, date_to)
        return stockData
    datas = map(getStockData, tickerList)
    return pd.concat(datas, keys=tickerList, names=['Ticker', 'Date'])

######### 処理#########
# 期間設定
date_from = dt.date(2016, 1, 24)
date_to = dt.date(2016, 10, 6)

# 株価取得
tickerList = ['AAPL', 'MSFT', 'IBM']
multiStockData  = getMultiStockData(tickerList, date_from, date_to)

# 終値を取得
adjClosingPrice = multiStockData[['Adj Close']]
adjClosingPrice = adjClosingPrice.reset_index()

# pandas.DataFrameをpivotingして終値データをticker毎に纏める
adjClosingPriceTable = adjClosingPrice.pivot(
    index='Date',
    columns='Ticker',
    values='Adj Close'
)

これを前提に話を進めていく。

・日次リターンの計算
これは以前「pandasで時系列データを扱う(pandas.Seriesとpandas.DataFrameを使って時系列データを扱う)2」でも触れたようにlagを取って計算するだけ。
前回は、「pandas.DataFrame.shift」を使用したが、今回は「pandas.DataFrame.pct_change」を使用する。
具体的にはこんな感じ。

daily_pct_change =  adjClosingPriceTable.pct_change()   # 日次リターンの計算

・日次リターンから累積リターンを求める
また、日次リターンの累積リターンを求める。
これも以前と同様に次のように実施すると良い。

# 日次リターンの累積を求める
cum_daily_return = (1 + daily_pct_change).cumprod()

可視化までのソースは次。

#coding:utf-8
import pandas as pd
import datetime as dt
import pandas_datareader.data as pdd
import matplotlib.pyplot as plt

######## Define Method ########
def getMultiStockData(tickerList, date_from, date_to):
    def getStockData(ticker):
        stockData = pdd.DataReader(ticker, 'yahoo', date_from, date_to)
        return stockData
    datas = map(getStockData, tickerList)
    return pd.concat(datas, keys=tickerList, names=['Ticker', 'Date'])

######### 処理#########
# 期間設定
date_from = dt.date(2016, 1, 24)
date_to = dt.date(2016, 10, 6)

# 株価取得
tickerList = ['AAPL', 'MSFT', 'IBM']
multiStockData  = getMultiStockData(tickerList, date_from, date_to)

# 終値を取得
adjClosingPrice = multiStockData[['Adj Close']]
adjClosingPrice = adjClosingPrice.reset_index()

# pandas.DataFrameをpivotingして終値データをticker毎に纏める
adjClosingPriceTable = adjClosingPrice.pivot(
    index='Date',
    columns='Ticker',
    values='Adj Close'
)

# 日次リターンを求める
daily_pct_change =  adjClosingPriceTable.pct_change()   # 日次リターンの計算
daily_pct_change.fillna(0, inplace=True)                # Nanを0で埋める
#daily_pct_change2 =  adjClosingPriceTable/adjClosingPriceTable.shift(1) - 1

# 日次リターンの累積を求める
cum_daily_return = (1 + daily_pct_change).cumprod()

# グラフ
cum_daily_return.plot(figsize=(12,8))
plt.legend(loc='Best')

結果は次。

・リターン分布を描く
日次リターンの値は既に求めてあるので、それをヒストグラムにするだけ。
日次リターンをヒストグラムにする部分のソースだけをピックアップしたものは次。

### 日次リターンの分布をヒストグラムにする
# AAPLの日次リターン
return_aapl = daily_pct_change['AAPL']

# AAPLの日次リターンヒストグラム
return_aapl.hist(bins=50, figsize=(12,8));

# 全ての株価の日次リターンヒストグラム
daily_pct_change.hist(bins=50, sharex=True, figsize=(12,8));

AAPLの日次リターンは次のようになる。

全ての銘柄の日次リターンは次のようになる。

グラフを見ると、次のように見える。

・日次リターンは0近辺に集まりがち
・若干偏りは見られるが、正規分布のように見える

これを数値で確認するには、describeメソッドを使用する。

# describe
print return_aapl.describe()

結果は次。

count 179.000000
mean 0.000965
std 0.014991
min -0.065707
25% -0.005662
50% 0.000901
75% 0.008158
max 0.064963
Name: AAPL, dtype: float64

・正規Q-Qプロットを行う
「scipy.stats」を使用する。
最後なので、全てコードを載せておく。

#coding:utf-8
import pandas as pd
import numpy as np
import datetime as dt
import pandas_datareader.data as pdd
import matplotlib.pyplot as plt
import scipy.stats as stats

######## Define Method ########
def getMultiStockData(tickerList, date_from, date_to):
    def getStockData(ticker):
        stockData = pdd.DataReader(ticker, 'yahoo', date_from, date_to)
        return stockData
    datas = map(getStockData, tickerList)
    return pd.concat(datas, keys=tickerList, names=['Ticker', 'Date'])

######### 処理#########
# 期間設定
date_from = dt.date(2016, 1, 24)
date_to = dt.date(2016, 10, 6)

# 株価取得
tickerList = ['AAPL', 'MSFT', 'IBM']
multiStockData  = getMultiStockData(tickerList, date_from, date_to)

# 終値を取得
adjClosingPrice = multiStockData[['Adj Close']]
adjClosingPrice = adjClosingPrice.reset_index()

# pandas.DataFrameをpivotingして終値データをticker毎に纏める
adjClosingPriceTable = adjClosingPrice.pivot(
    index='Date',
    columns='Ticker',
    values='Adj Close'
)

# 日次リターンを求める
daily_pct_change =  adjClosingPriceTable.pct_change()   # 日次リターンの計算
daily_pct_change.fillna(0, inplace=True)                # Nanを0で埋める

# AAPLの日次リターン
return_aapl = daily_pct_change['AAPL']

# プロット
f = plt.figure(figsize=(12, 8))
ax = f.add_subplot(111)
ax.set_title("Probplot")
stats.probplot(return_aapl, dist='norm', plot=ax)
plt.show()

結果は次。

※)グラフを描画した直後に下記エラーが発生する。

line 313, in probplot
plot.title('Probability Plot')
TypeError: 'Text' object is not callable

発生箇所は、morestats.pyの次の箇所。

line 313, in probplot
    plot.title('Probability Plot')
TypeError: 'Text' object is not callable

原因は調べられていないが、フレーム内部なのでバグかもしれない。あとで調べておく。

最後に、scipyでShapiro Testを行う。

# Shapiro Test
print stats.shapiro(return_aapl)

結果は・・・。
あれ、これはp値が0.05未満なのでデータが正規分布に従っているという帰無仮説が棄却される?

(0.9281759858131409, 9.659175503884398e-08)