WHAT' CHA GONNA DO FOR ME?

Python、統計、機械学習、R、ファイナンスとか

pandasで日次リターンを月次リターンに変換する①

日次リターンを月次リターンに変換したいときがそれなりにある。
pandasを使えば、これも簡単にできる。

pandas開発者が自ら執筆したこちらのありがたい本によると、

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

以下の2つの方法が紹介されている。

①日次の累積リターンから計算する方法
②日次のリターンから計算する方法

今回は①の方法で、日次リターンを月次リターンに変換する。

ちなみに日次リターンから日次の累積リターンを作るのは、↓でやった。

いま、cum_ret_dという変数に日次の累積リターンが入っているとする。

cum_ret_d.plot()
Out[8]: <matplotlib.axes._subplots.AxesSubplot at 0xb407b30>

f:id:lofas:20150219154937p:plain

これを使って日次リターンを月次リターンに変換するが、コードは次のようにするだけでOK。

ret_m=cum_ret_d.resample("BM",how="last").pct_change()
ret_m.plot()

Out[6]: <matplotlib.axes._subplots.AxesSubplot at 0x14b86a50>

f:id:lofas:20150219154948p:plain

BMは、business month end frequencyのことで、これでデータの頻度を日次から月末最終営業日に変換する。
lastは、サマリー対象のレコードのうち最後の値を使う、という意味なので、ここまでで月末最終営業日時点の累積リターン系列ができることとなる。
次に、月末最終営業日間の累積リターンの変化分をpct_change()で計算するため、晴れて月次リターンが得られるという流れ。

コード全体はこちら。

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd
import pandas.io.data as web
import datetime

start = datetime.datetime(2000, 1, 1)
end = datetime.datetime(2014, 12, 31)

#データ取得
all_data = {}
for ticker in ['AAPL', 'IBM', 'MSFT', 'YHOO']:
    all_data[ticker] = web.DataReader(ticker,'yahoo',start,end) 

#データ結合(concatでもOK)
#stock_price=all_data['AAPL'].ix[:,['Adj Close']]
#for ticker in ['IBM', 'MSFT', 'YHOO']:
#    stock_price = pd.concat((stock_price,all_data[ticker].ix[:,['Adj Close']]),axis=1)

#データ結合
stock_price=all_data['AAPL'].ix[:,['Adj Close']]
for ticker in ['IBM', 'MSFT', 'YHOO']:
    stock_price = pd.merge(stock_price,all_data[ticker].ix[:,['Adj Close']],
                           left_index=True, right_index=True)
#列名変更
stock_price.columns=['AAPL', 'IBM', 'MSFT', 'YHOO']
stock_price.plot()

#リターンデータに変換
stock_ret=stock_price.pct_change()
stock_ret.plot()

#累積リターンに変換
cum_ret_d=(1+stock_ret).cumprod()
cum_ret_d.ix[0]=1
cum_ret_d.plot()

#月次リターンに変換
ret_m=cum_ret_d.resample("BM",how="last").pct_change()
ret_m.plot()

pandasで累積リターンを計算

前回は株価から直接累積リターンを計算したが、

実際によくあるのは、例えば日次や月次のリターンから、累積リターンを計算するケースだ。

いま、stock_retという変数に日次リターンが既に入っているとして、

stock_ret.plot()
Out[99]: <matplotlib.axes._subplots.AxesSubplot at 0x19ce3e30>

f:id:lofas:20150217162402p:plain

ここから累積リターンを計算するのは、次の1行でいける。
最初の行が欠けるため、始点として一律1を入れるのを忘れないようにする。

cum_ret_d=(1+stock_ret).cumprod()
cum_ret_d.ix[0]=1
cum_ret_d.plot()

Out[100]: <matplotlib.axes._subplots.AxesSubplot at 0x19da6ab0>


f:id:lofas:20150217162540p:plain

コード全体はこちら。

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd
import pandas.io.data as web
import datetime

start = datetime.datetime(2000, 1, 1)
end = datetime.datetime(2014, 12, 31)

#データ取得
all_data = {}
for ticker in ['AAPL', 'IBM', 'MSFT', 'YHOO']:
    all_data[ticker] = web.DataReader(ticker,'yahoo',start,end) 

#データ結合(concatでもOK)
#stock_price=all_data['AAPL'].ix[:,['Adj Close']]
#for ticker in ['IBM', 'MSFT', 'YHOO']:
#    stock_price = pd.concat((stock_price,all_data[ticker].ix[:,['Adj Close']]),axis=1)

#データ結合
stock_price=all_data['AAPL'].ix[:,['Adj Close']]
for ticker in ['IBM', 'MSFT', 'YHOO']:
    stock_price = pd.merge(stock_price,all_data[ticker].ix[:,['Adj Close']],
                           left_index=True, right_index=True)
#列名変更
stock_price.columns=['AAPL', 'IBM', 'MSFT', 'YHOO']
stock_price.plot()

#リターンデータに変換
stock_ret=stock_price.pct_change()
stock_ret.plot()

#累積リターンに変換
cum_ret_d=(1+stock_ret).cumprod()
cum_ret_d.ix[0]=1
cum_ret_d.plot()

pandasで株価を取得

pandasの公式ドキュメント(Remote Data Access — pandas 0.15.2 documentation)によると、今のところ下記からデータをダウンロードすることが可能らしい。これは本当に便利。

今回は、Yahoo! Financeから株価を落としてみようと思う。

スタートとエンドの日付を決めて、all_dataというディクショナリ型の箱を用意する。そこにtickerでループさせて株価を突っ込んでいくという寸法。

In [45]: start = datetime.datetime(2000, 1, 1)
    ...: end = datetime.datetime(2014, 12, 31)
    ...: 
    ...: #データ取得
    ...: all_data = {}
    ...: for ticker in ['AAPL', 'IBM', 'MSFT', 'YHOO']:
    ...:     all_data[ticker] = web.DataReader(ticker,'yahoo',start,end) 

各社について、Open、High、Low、Close、Volume、Adj Closeが取れる。Adj Closeは、配当や株式分割等のコーポレートアクション考慮済みの株価と思われるため、以降はこれを使うこととする。
扱いやすいように各社のAdj Closeだけのデータフレームをmerge関数を使って作る。その際、日付で内部結合する。そのうえで、株価の時系列プロットを見てみる。

In [49]: stock_price=all_data['AAPL'].ix[:,['Adj Close']]
    ...: for ticker in ['IBM', 'MSFT', 'YHOO']:
    ...:     stock_price = pd.merge(stock_price,all_data[ticker].ix[:,['Adj Close']],
    ...:                            left_index=True, right_index=True)
    ...: 
    ...: #列名変更
    ...: stock_price.columns=['AAPL', 'IBM', 'MSFT', 'YHOO']
    ...: stock_price.plot()
Out[49]: <matplotlib.axes._subplots.AxesSubplot at 0x1975e358>

f:id:lofas:20150215222401p:plain

予想通りAAPLの上昇がすさまじい感じだが、それぞれの株価の始点が違っていていまいちわかりづらいので、累積リターンを計算する。

In [51]: stock_cum_ret=stock_price.ix[:stock_price.shape[0]]/stock_price.ix[0]-1
    ...: stock_cum_ret.plot()
Out[51]: <matplotlib.axes._subplots.AxesSubplot at 0x19684518>

f:id:lofas:20150215222410p:plain

AAPLすごすぎワロタ

株価や累積リターンのままだと分析がやりづらいため(時系列的に言えば単位根過程であるため)、リターンに変換する。
pandasだと、pct_change()関数一発でこれができる。pandasすごいよpandas
で、あとはいつも通り統計量や時系列プロット、スキャッターマトリックスや相関行列を見て、大体の特徴をつかむ。

In [53]: stock_ret=stock_price.pct_change()

In [54]: stock_ret.describe()
Out[54]: 

	AAPL	IBM	MSFT	YHOO
count	3772	3772	3772	3772
mean	0.001306	0.000284	0.000232	0.000336
std	0.027962	0.017036	0.020219	0.033706
min	-0.519444	-0.155476	-0.155941	-0.218362
25%	-0.012166	-0.007431	-0.00882	-0.014154
50%	0	0.000151	0	0
75%	0.014707	0.007959	0.009221	0.014288
max	0.139571	0.120228	0.195793	0.479666


In [55]: stock_ret.plot()

In [56]: pd.scatter_matrix(stock_ret,diagonal='kde')
Out[56]: 

In [57]: stock_ret.corr()
Out[57]: 

	AAPL	IBM	MSFT	YHOO
AAPL	1	0.403815	0.411938	0.371237
IBM	0.403815	1	0.496384	0.380836
MSFT	0.411938	0.496384	1	0.389721
YHOO	0.371237	0.380836	0.389721	1

f:id:lofas:20150215223106p:plain

f:id:lofas:20150215223114p:plain

コード全体はこちら。

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd
import pandas.io.data as web
import datetime

start = datetime.datetime(2000, 1, 1)
end = datetime.datetime(2014, 12, 31)

#データ取得
all_data = {}
for ticker in ['AAPL', 'IBM', 'MSFT', 'YHOO']:
    all_data[ticker] = web.DataReader(ticker,'yahoo',start,end) 

#データ結合(concatでもOK)
#stock_price=all_data['AAPL'].ix[:,['Adj Close']]
#for ticker in ['IBM', 'MSFT', 'YHOO']:
#    stock_price = pd.concat((stock_price,all_data[ticker].ix[:,['Adj Close']]),axis=1)

#データ結合
stock_price=all_data['AAPL'].ix[:,['Adj Close']]
for ticker in ['IBM', 'MSFT', 'YHOO']:
    stock_price = pd.merge(stock_price,all_data[ticker].ix[:,['Adj Close']],
                           left_index=True, right_index=True)
#列名変更
stock_price.columns=['AAPL', 'IBM', 'MSFT', 'YHOO']
stock_price.plot()

#累積リターン
stock_cum_ret=stock_price.ix[:stock_price.shape[0]]/stock_price.ix[0]-1
stock_cum_ret.plot()

#リターンデータに変換
stock_ret=stock_price.pct_change()
stock_ret.describe()
stock_ret.plot()
pd.scatter_matrix(stock_ret,diagonal='kde')
stock_ret.corr()

pandasでFF+C4ファクターを作ってみた②

これのちょっとした続き。

pandasのscatter_matrixを使うと、列のヒストグラムや分布、列間の散布図がまとめて出せると聞いたのでやってみる。
4ファクターを作るところまでは前回と一緒なので割愛。
このコードでスキャッタープロットが出る。対角部分は今回はカーネル分布にしておく。

pd.scatter_matrix(ffc4,diagonal='kde')

f:id:lofas:20150214234811p:plain

いいね!
散布図を見る限りファクター間の相関はあまり高くなさそうだね、ということで、一応相関を計算してみる。
pandasで相関行列を計算するのは前にやったので、そのことを思い出す。

加えて、どれも正規分布にはいまいち従ってなさそうなので、歪度と尖度も一応計算しておく。

In [32]: ffc4.corr()
Out[32]: 
	1 Mkt-RF	2 SMB	3 HML	1 Mom
1 Mkt-RF	1	0.326722	0.228785	-0.33814
2 SMB	0.326722	1	0.114047	-0.148994
3 HML	0.228785	0.114047	1	-0.404134
1 Mom	-0.33814	-0.148994	-0.404134	1

ffc4.skew()
Out[33]: 
1 Mkt-RF    0.193809
2 SMB       1.966844
3 HML       2.144194
1 Mom      -3.116315
dtype: float64

ffc4.kurt()
Out[34]: 
1 Mkt-RF     7.734848
2 SMB       19.382973
3 HML       18.916469
1 Mom       28.186152
dtype: float64

これで計算される尖度は、通常の尖度から3を引いた超過尖度である点に注意すること。

相関はそこまで高いものはないので、OLSの説明変数として使う分には多重共線性の問題は起きなさそう。
あと、グラフで見た通り、いずれのファクターも歪度、超過尖度ともに0からかなり離れてるため、正規分布とは言えませんね、なかでもモメンタムファクターは暴れてますね、という結果に。

コード全体はこんな感じで、前回とほとんど変わらず。

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd

ip=web.DataReader("F-F_Momentum_Factor", "famafrench")
mom=ip[1].replace(-99.99,np.NaN).dropna()

ip=web.DataReader("F-F_Research_Data_Factors", "famafrench")
ff3=ip[0].replace(-99.99,np.NaN).dropna().ix[:,0:3]

ffc4=pd.merge(ff3,mom,left_index=True, right_index=True)

pd.scatter_matrix(ffc4,diagonal='kde')
ffc4.corr()
ffc4.skew()
ffc4.kurt()

pandasでFF+C4ファクターを作ってみた

pandasでFF3ファクターが取れたので、ここにモメンタムファクターを加えて、Carhartの4ファクターを作ってみたという話。
モメンタムファクターについては以下の通り。

MOM:過去1年間(直近1ヶ月は除く)の勝ち組株をロング、負け組株をショートしたロングショートポート

Carhart(1997)は、この4ファクターを使って米国の投資信託の性質の分析をしていて、これも多分かなり有名。
https://stuwww.uvt.nl/fat/files/library/Carhart,%20Mark%20M.%20-%20On%20Persistence%20in%20Mutual%20Fund%20Performance%20%281997%29.pdf

モメンタムファクターは、フレンチ教授のデータライブラリーでは単体で提供されており、以下で取れる。
欠損値は例によって行ごと削除。

ip=web.DataReader("F-F_Momentum_Factor", "famafrench")
mom=ip[1].replace(-99.99,np.NaN).dropna()

このMOMファクターとFF3ファクターを結合したい。
ただ、両データの日付が微妙に違うため、両方にある日付だけを対象に結合したい。
要はSQLでいうところのINNER JOINをやりたいので、pandasのmerge関数を使う。オプションでインデックスをキーにする。

ffc4=pd.merge(ff3,mom,left_index=True, right_index=True)

要約統計量と時系列プロットは前回見たので、今回はヒストグラムとボックスプロットを見てみる。
f:id:lofas:20150213153812p:plain
f:id:lofas:20150213153822p:plain

よく言われているように、モメンタムファクターの負への歪みが強烈。

コード全体はこんな感じ。

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd

ip=web.DataReader("F-F_Momentum_Factor", "famafrench")
mom=ip[1].replace(-99.99,np.NaN).dropna()

ip=web.DataReader("F-F_Research_Data_Factors", "famafrench")
ff3=ip[0].replace(-99.99,np.NaN).dropna().ix[:,0:3]

ffc4=pd.merge(ff3,mom,left_index=True, right_index=True)
ffc4.hist(bins=50,normed=True)
ffc4.boxplot()

pandasでFama-Frenchの3ファクターを取ってみた

pandasで、Franch教授のデータライブラリーからデータがインポートできるときいたので、実際にやってみた。

ファイナンスでは、Fama-Frenchの3ファクターという有名なファクターがある。
それぞれのファクターのざっくりとした説明はこちら。

Mkt-RF:リスクフリーレートで借り入れて時価加重のマーケットポートをロングするロングショートポート
SMB:小型株をロングして大型株をショートするロングショートポート
HML:バリュー株をロングしてグロース株をショートするロングショートポート

詳細は、French教授のHPを読みましょう。
Kenneth R. French - Data Library

もしくは超有名な原論文を読みましょう。
FF(1992)
http://www.bengrahaminvesting.ca/Research/Papers/French/The_Cross-Section_of_Expected_Stock_Returns.pdf
FF(1993)
http://home.business.utah.edu/finmll/fin787/papers/FF1993.pdf

↓の1行で取れると聞いて半信半疑でやってみたら、

In [144]: ip=web.DataReader("F-F_Research_Data_Factors", "famafrench")

本当に取れた。たまげたなあ。
今までHPからzip落として解凍して、テキストファイルからcsv作ってRで読み込んでたのは何だったんや!

ディクショナリー型で取れて、key:0が月次リターン、key:1が年次リターンのようなので、ここでは月次を使う。
\-99.99は欠損を表すのでNaNに変換してレコードごと削除したうえで、3ファクターだけを抜き出す。
これが1行でさらっと書けるのはすばらしい。

In [145]: ff3=ip[0].replace(-99.99,np.NaN).dropna().ix[:,0:3]

要約統計量と時系列プロットを見てみる。これも簡単に書ける。

In [149]: ff3.describe()
Out[149]: 
          1 Mkt-RF        2 SMB        3 HML
count  1062.000000  1062.000000  1062.000000
mean      0.653927     0.219642     0.396591
std       5.404478     3.224314     3.536978
min     -29.130000   -16.400000   -13.280000
25%      -2.015000    -1.560000    -1.277500
50%       1.025000     0.060000     0.230000
75%       3.657500     1.780000     1.747500
max      38.850000    36.700000    35.460000

In [150]: ff3.plot()
Out[150]: <matplotlib.axes._subplots.AxesSubplot at 0x1b4937d0>

f:id:lofas:20150213131247p:plain

コード全体はこちら。
pandasかわいいよpandas

# -*- coding: utf-8 -*-
import pandas.io.data as web
import numpy as np
import seaborn as sns
import pandas as pd

ip=web.DataReader("F-F_Research_Data_Factors", "famafrench")
ff3=ip[0].replace(-99.99,np.NaN).dropna().ix[:,0:3]
ff3.describe()
ff3.plot()

OLS④

OLSシリーズ第4弾。
OLSは本当に重要なのでしつこくやる。

今回は、scipyのstats.linregressを使う。

いつも通り下記の回帰式を推定する。

{ 
y=2+1.5x+\epsilon\\
x \sim\ \mathcal{N}(1,1)\\
\epsilon \sim\ \mathcal{N}(0,1)\\
}

コードは以下の通り。

# -*- coding: utf-8 -*-
import numpy as np
from scipy import stats

#サンプリング数
N=1000

#データ生成
np.random.seed(0)
x=np.random.normal(1,1,N)
eps=np.random.normal(0,1,N)
y=2+1.5*x+eps

#OLS(stats.linregress)
beta, intercept, r_value, p_value, std_err = stats.linregress(x,y)

beta
intercept
r_value
p_value
std_err

結果はいつも通り。係数、定数項、相関係数、係数のp値、係数の標準誤差を返してくれる模様。どうでもいいけど、この変数を並べるMatlab風の書き方はどうも慣れない…

In [132]: beta, intercept, r_value, p_value, std_err = stats.linregress(x,y)


In [133]: beta
Out[133]: 1.4690941983978636

In [134]: intercept
Out[134]: 2.0431240470954477

In [135]: r_value
Out[135]: 0.8317899971487972

In [136]: p_value
Out[136]: 2.275528428788794e-257

In [137]: std_err
Out[137]: 0.031033762367292484

ファイナンスでは、例えばCAPMなんかを推定したときには、定数項であるαの標準誤差とt値を確認したくなるので、その意味ではこれもなかなか使いづらそう。

これでPythonのOLS推定関数も出尽くした感があるので(少なくとも頻度論に基づくいわゆる最小二乗法的なものに限っては)、次はMCMCでもやるか