Pythonでポートフォリオバックテスト
いつもバックテストするときは便利なのでRを使っているのですが、Pythonの勉強ついでにRの’quantmod’や’performanceAnalytics’のようなパッケージの中で最も良く使う機能をPythonで簡単に使えるように関数を定義してみました。
本記事はPython ver 3xを少し触ったことがある方向けです。jupyter notebookが便利なのでjupyterを使用する前提で書かれています。
テスト環境は、以下の通り。
- anaconda3-5.2.0
- Python 3.6.5
使用したパッケージは、以下で、インストールが必要になります。
- pandas == 0.23.0
- fix_yahoo_finance == 0.0.22
- numpy == 1.14.3
- matplotlib == 2.2.2
Python3がインストールされている環境であれば、コマンドラインからパッケージをインストールしてください。(ほとんどのパッケージはanaconda3にはインストールされています。)
$ pip install pandas == 0.23.0 $ pip install fix_yahoo_finance == 0.0.22 $ pip install numpy == 1.14.3 $ pip install matplotlib == 2.2.2 $ pip install jupyter
元々Pythonではpandas_datareaderというパッケージを使用してデータを取得することが多かったのですが、8月頃から調子が悪くなっていたので、異なるパッケージを使用してデータを取得しています。
ただ、動作が時折不安定なときもあるので、pandas_datareaderも一応インストールしたほうが良いかも知れません。
早速コードを見ていきましょう
パッケージの準備
使用するパッケージを読み込んでいきます。今はpandas_datareaderでのデータの取得がうまくできないため、fix_yahoo_financeというパッケージを使って読み込んでいきます。
使用する関数を定義
割と頻繁に使いそうな操作を関数にしました。関数の名前はRの’quantmod’や’performanceAnalytics’のイメージでつけているので操作したことがある方はイメージが付きやすいかも知れません。
備忘録用に各関数に注釈をつけているので、
> help(getSymbols)
などのようにして注釈を読んでください。あまり拡張した使用をイメージしていないため、今の所応用的な使用には耐えません。yahoo financeから外国株あるいはETFのデータを取ってくるくらいが想定の使用範囲です。アップデートしたら本記事に反映しておきます。関数の使用方法は以下の例を見ながら解説します。
条件の入力
簡単にするために入力はごく簡単にしています。
tickers: 銘柄やETFのシンボル。[ ]で括ってリストにします。
w: ポートフォリオを組む時のウェイト。これもリストにします。
start, end: データ取得の初めと最後。” “で括ってyyyy-mm-ddの形式で日付を入力。
最新のデータを取得できるように上の例では今日の日付まで取得しています。
データを取得して変換する
getSymbolsは株価データを取得します。株価データを取得して配当調整後の価格に変換したものを出力しています。こんな感じで、日付と価格のデータが取得されます。ticker, start, endを入力してpandas.DataFrameを返します。
returnCalculateは先程のgetSymbolsで取得したデータを引数にとり、日次のリターンを計算します。株式の変動(%)を計算しています。これも出力はDataFrame。
returnCumulativeは、リターンのデータから、累積リターンを計算します。これもDataFrameの形で出力しますが、出力を使うとプロットがかけます。
さっき取得した5種類のETFのデータがプロットできましたね。新しく設定されたETFなどではReturnがずっと1.0のになるようになっています。
periodReturnは特に今は使わないですが、日次リターンなどのデータを月次とか年次に集計する機能です。今後他のデータと組み合わせるときに使おうかと思っています。これもDataFrameを出力します。
ポートフォリオのシミュレーション
先程の基本的な機能でデータの取得ができました。ポートフォリオを実際にモンテカルロ・シミュレーションしていきます。
portfolioReturnは先程の日次リターンを使って、ポートフォリオのウェイトに従って投資したときのリターンを計算します。rebalance_onでリバランスの頻度を設定(デフォルトは四半期に1回)、nameでポートフォリオに名前がつけられます。これも出力はDataFrameになります。上のサンプルでは5つの銘柄を使ってオールウェザーポートフォリオをシミュレーションしています。
plotChartは先程も登場しましたが、リターンからポートフォリオ(あるいは銘柄)の累積リターンのチャートを描画します。リターンを表すDataFrameを入れるだけで簡易チャートを描きます。関数の設定として、リターンが極端に大きいときはy軸を対数表示にします。(50倍以上の時のみにしているので、好みでいじってください。)
もちろん、シミュレーションしたポートフォリオは他のリターンとマージして容易にプロットできます。
さっきは5つだった線が6つに増えましたね?pd.concatはpandasの関数を使用しています。
ポートフォリオの成績の集計
シミュレーションした集計結果を返却する関数です。Annualized Return (= CAGR)、StdDev、Sharpe Ratio、maxDDをそれぞれ計算する関数です。上の4つの関数にはポートフォリオのリターンを入れます。
returnAnnualizedではデフォルトでは幾何リターンを返すようにしています。算術リターンがほしければgeometric = Falseと入れてください。scaleは引数のリターンの間隔です。日次リターンならscale = ‘D’ですが、月次リターンならば’M’などのようにして入れます。
pfSummaryでこれらの指標を一括して表示できます。
せっかくシミュレーションしたポートフォリオなので、以下のようにして保存しておきましょう。
>allweather = pfReturn.copy()
もう一回、1からやってみましょう
実際にシミュレーションするときは僅かな行数です。tickersとwでポートフォリオを設定、getSymbolsで取得、returnCalculateでリターンを計算、portfolioReturnでモンテカルロ・シミュレーションを実行。plotChartでチャート描画、pfSummaryでサマリ出力です。
ポートフォリオを組み合わせる
保存したポートフォリオ同士を比べる時も、pd.concatでマージすると一発ですね。
※このリターンの計算、2001年ごろの時点ではGSGなどは設定されていないため、これらのETFが設定されるまで日次リターンは0(現金と同等)として計算されています。
今後の目標?
ここまでのシミュレーションをやったところで割と満足してしまいました。低級なプログラミングをなるべく意識するように関数を書いたのでそこまで遅くは無いはず。
シミュレーションしたい項目があったら少しずつ付け足したり改良していくかもしれません。
しかし、重要なのは、Pythonからデータを取得するのがかなり不安定な事が多く、今回もマイナーなパッケージを使用しています。仕様の変更などで使えなくなる可能性も十分にあるため、気づいたときに変更します。ただ、ご利用の際にはスクリプトをチェックの上、ご自身の責任でお願いします。
コメント