pandasで日次リターンを月次リターンに変換する①
日次リターンを月次リターンに変換したいときがそれなりにある。
pandasを使えば、これも簡単にできる。
pandas開発者が自ら執筆したこちらのありがたい本によると、
Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (7件) を見る
以下の2つの方法が紹介されている。
①日次の累積リターンから計算する方法
②日次のリターンから計算する方法
今回は①の方法で、日次リターンを月次リターンに変換する。
ちなみに日次リターンから日次の累積リターンを作るのは、↓でやった。
いま、cum_ret_dという変数に日次の累積リターンが入っているとする。
cum_ret_d.plot() Out[8]: <matplotlib.axes._subplots.AxesSubplot at 0xb407b30>
これを使って日次リターンを月次リターンに変換するが、コードは次のようにするだけでOK。
ret_m=cum_ret_d.resample("BM",how="last").pct_change() ret_m.plot() Out[6]: <matplotlib.axes._subplots.AxesSubplot at 0x14b86a50>
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>
ここから累積リターンを計算するのは、次の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>
コード全体はこちら。
# -*- 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
- Google Finance
- St.Louis FED (FRED)
- Kenneth French’s data library
- World Bank
- Google Analytics
今回は、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>
予想通り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>
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
コード全体はこちら。
# -*- 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')
いいね!
散布図を見る限りファクター間の相関はあまり高くなさそうだね、ということで、一応相関を計算してみる。
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)
要約統計量と時系列プロットは前回見たので、今回はヒストグラムとボックスプロットを見てみる。
よく言われているように、モメンタムファクターの負への歪みが強烈。
コード全体はこんな感じ。
# -*- 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>
コード全体はこちら。
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を使う。
いつも通り下記の回帰式を推定する。
コードは以下の通り。
# -*- 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でもやるか