2020年3月現在、 新型コロナウイルスの世界的拡大で株価が急落しています。
せっかくなのでコツコツと積み立てている投資信託の成績をPythonで可視化してみて、どれくらいのインパクトがあったのか調べてみました。私の投資信託の成績をぜひご覧あれ。
データ収集
楽天証券で投資信託を購入しています。
トップメニューから 口座管理 > 投資信託 > 投信あしあと
へと進むと、全解約済みの銘柄を含めた投信全体の推移を表示することができます。
推移だけならこの画面で十分に確認できるのですが、このあとの分析のために生データを取得します。
画面上で右クリックしてページのソースを表示します。
すると下の方で(1649行目以降から)それらしき配列 data1,data2,data3,data4
があるので、これをコピっておきます。
デベロッパーツールからも確認できますので、お好きな方法で取得してください。
Google Colaboratoryでデータの確認
Google Colaboratoryを起動して、とりあえず二次元配列そのままで扱ってみます。
# 実際はもっと長いです。
data1 = [[1520175600000,0.000000],[1520262000000,-2.000000],[1520348400000,-1.000000]]
data2 = [[1520175600000,3758.000000],[1520262000000,3756.000000],[1520348400000,3757.000000]]
data3 = [[1520175600000,3758.000000],[1520262000000,3758.000000],[1520348400000,3758.000000]]
data4 = [[1520175600000,0.000000],[1520262000000,0.000000],[1520348400000,0.000000]]
長さはすべて488でした。(人によって異なります。)
len(data1), len(data2), len(data3), len(data4)
# => (488, 488, 488, 488)
PandasのDataframeにします。
もともとJavaScriptの二次元配列でしたが、Pythonでも配列の形が同じなのでコピペしたものをそのまま Pandas
で読み込めます。ここからはdata1を抜粋して表示します。
import pandas as pd
df1 = pd.DataFrame(data1)
df1
0 | 1 | |
---|---|---|
0 | 1520175600000 | 0.0 |
1 | 1520262000000 | -2.0 |
2 | 1520348400000 | -1.0 |
3 | 1520434800000 | -2.0 |
4 | 1520521200000 | -2.0 |
... | ... | ... |
1列目はタイムスタンプっぽいが小数点がないようです。桁を揃えるために1000で割り、インデックスにしたあとにDatetimeに変換します。
df1[0] = df1[0] // 1000
df1 = df1.set_index([0])
df1.index = pd.to_datetime(df1.index, unit='s')
df1
1 | |
---|---|
0 | |
2018-03-04 15:00:00 | 0.0 |
2018-03-05 15:00:00 | -2.0 |
2018-03-06 15:00:00 | -1.0 |
2018-03-07 15:00:00 | -2.0 |
2018-03-08 15:00:00 | -2.0 |
... | ... |
2020-02-26 15:00:00 | 59802.0 |
2020-02-27 15:00:00 | 19223.0 |
2020-03-01 15:00:00 | -245.0 |
2020-03-02 15:00:00 | 37929.0 |
2020-03-03 15:00:00 | 9971.0 |
直近2年分のデータが表示されました。
全解約済みの銘柄を含めた投信全体の推移
と説明があるので取得できる期間は人によって異なるかもしれません。
とりあえず、このdata1を matplotlib
で可視化してみます。
import matplotlib.pyplot as plt
df1.plot()
plt.show()
楽天証券の 投信あしあと
の画面と比較してみると、どうやらdata1はトータルリターンであることがわかります。
2019年1月頃から積み上げてきた利益約14万円ほどがコロナウイルスによる影響で一瞬にしてなくなっています。
どうやら楽天証券のページのソースから得られたdata1~4はそれぞれ順番に、下記の項目と対応しているようです。
- トータルリターン
- 評価額
- 実質投資額
- 受取分配金
以後、扱いやすいように1つのDataframeにまとめ、カラム名を内容に合わせるように変更しました。(分配金のある投信を購入していないので今回は除外)
また、ほとんど休眠期間だった始めの半年ほどは除外しました。
return | value | invest | |
---|---|---|---|
time | |||
2018-11-01 15:00:00 | -1.0 | 1629.0 | 1630.0 |
2018-11-04 15:00:00 | -1.0 | 0.0 | 1.0 |
2018-11-05 15:00:00 | 0.0 | 20000.0 | 20000.0 |
2018-11-06 15:00:00 | 67.0 | 20067.0 | 20000.0 |
2018-11-07 15:00:00 | 439.0 | 20439.0 | 20000.0 |
... | ... | ... | ... |
Plotlyでそれっぽい可視化をする。
個人的にPlotlyの色合いなど雰囲気が好きなので、Plotlyで可視化をしてみます。
pip install plotly
まずは楽天証券の画面を再現してみます。
トータルリターン
import plotly.graph_objects as go
data = [
go.Scatter(x=df.index, y=df['return'], name='トータルリターン'),
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
type='date',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title = '金額'
),
showlegend=True,
legend=dict(
x=0.025,
y=0.95,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
評価額と実質投資額
コードはほとんど同じなので折りたたみ(クリックして展開)
data = [
go.Scatter(x=df.index, y=df['value'], name='評価額'),
go.Scatter(x=df.index, y=df['invest'], name='投資額')
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
type='date',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title = '金額'
),
legend=dict(
x=0.025,
y=0.95,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
リターンの前営業日差・変化率
df_diff = df.diff()
コードはほとんど同じなので折りたたみ(クリックして展開)
data = [
go.Scatter(x=df_diff.index, y=df_diff['return'], name='リターン 前営業日差', line = dict(color='purple'))
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
type='date',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title = '金額',
),
showlegend=True,
legend=dict(
x=0.025,
y=0.95,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
当たり前ですが、絶対値で比べると投資額が大きくなるにつれて、リターンの変化も大きくなるので、その時の評価額に対する割合で見てみます。
df['return_change_ratio'] = df['return'].diff() / df['value']
df
コードはほとんど同じなので折りたたみ(クリックして展開)
data = [
go.Scatter(x=df.index, y=df['return_change_ratio'], name='リターン 前営業日変化率', line = dict(color='purple'))
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
type='date',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title = '変化率',
tickformat='%'
),
showlegend=True,
legend=dict(
x=0.025,
y=0.05,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
こう見ると、定期的に 1日で資産が±5%ほど変動する日
がやってきていることがわかりますね。
下落割合が大きい順にデータを並べてみました。
df.sort_values('return_change_ratio', ascending=True).head(10)
return_change_ratio | |
---|---|
time | |
2020-02-24 15:00:00 | -0.055890 |
2020-02-27 15:00:00 | -0.051792 |
2018-12-24 15:00:00 | -0.049762 |
2020-03-05 15:00:00 | -0.043918 |
2019-08-25 15:00:00 | -0.039766 |
2019-01-03 15:00:00 | -0.037813 |
2020-02-25 15:00:00 | -0.035507 |
2018-12-04 15:00:00 | -0.034236 |
2020-03-03 15:00:00 | -0.033716 |
2019-08-14 15:00:00 | -0.031938 |
上から1, 2, 4, 7, 9番目がコロナショックによるものと思われ、10日中5日と半数を占める結果となりました。
反対に上昇幅の大きい順に並べてみました。
df.sort_values('return_change_ratio', ascending=False).head(10)
return_change_ratio | |
---|---|
time | |
2020-03-02 15:00:00 | 0.044621 |
2018-12-26 15:00:00 | 0.040878 |
2020-03-04 15:00:00 | 0.038827 |
2019-01-06 15:00:00 | 0.026219 |
2019-08-13 15:00:00 | 0.022233 |
2020-02-04 15:00:00 | 0.021108 |
2019-11-04 15:00:00 | 0.019685 |
2019-06-04 15:00:00 | 0.018888 |
2018-11-07 15:00:00 | 0.018200 |
2019-09-05 15:00:00 | 0.017959 |
1, 3番目がコロナショックの影響と思われます。1日の上昇割合についても私の投資歴の中で最高を更新したようです。
変化割合ヒストグラム
data = [go.Histogram(x=df['return_change_ratio'], xbins=dict(size=0.001))]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title="変化割合",
dtick=0.01,
range=[-0.06, 0.06],
tickformat='%'
),
yaxis = dict(
title="度数"
)
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
右裾にある3個の度数1の外れ値のうち2つ、左裾にある10個ほど続く度数1のうち1, 2, 4, 7, 9番目はコロナショックのものです。
月毎のリターン
OHLCで月毎にリサンプルし、OpenとCloseの差分を取って月間のリターンの変化を調べてみました。(もっと単純なやりかたがある?)
df_month = df['return'].resample('M').ohlc()
df_month['diff'] = df_month['close'] - df_month['open']
df_month
コードはほとんど同じなので折りたたみ(クリックして展開)
data = [
go.Bar(x=df_month.index.strftime('%Y-%m'), y=df_month['diff'], name='月間のリターン', marker_color ='orange')
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title = '金額',
),
showlegend=True,
legend=dict(
x=0.025,
y=0.05,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
Pandasでリサンプルすると、期間内の最終日付がインデックスになってしまいプロットのラベルとずれが出てしまったので、文字列にしています。
月毎の運用利回り
毎日の積み立てに加え、不定期に買い増しや売却をしているので、厳密に月の利回りを計算しようとするとその月の何日にいくら購入・売却をしたかを考慮して計算しないといけないのですが、ここでは簡易的にその月の投資額の平均値を基準にリターンの割合を計算しました。
とは言っても、ほとんどが固定額の積み立て投資ですので、今回の簡易的な結果とあまり大きな違いはないはずです。(今回準備したデータだけで厳密に計算することは可能です。)
import numpy as np
df_month = df.resample('M').agg({'return' : lambda x: x[-1] - x[0], 'invest': np.mean})
df_month['interest'] = df_month['return'] / df_month['invest']
df_month
return | invest | interest | |
---|---|---|---|
time | |||
2018-11-30 | -113.0 | 25081.550000 | -0.004505 |
2018-12-31 | -4160.0 | 39805.473684 | -0.104508 |
2019-01-31 | 6285.0 | 85135.350000 | 0.073824 |
2019-02-28 | 8414.0 | 174170.210526 | 0.048309 |
2019-03-31 | 1161.0 | 238031.650000 | 0.004878 |
2019-04-30 | 5696.0 | 243301.684211 | 0.023411 |
2019-05-31 | -16022.0 | 284249.052632 | -0.056366 |
2019-06-30 | 20011.0 | 320820.857143 | 0.062374 |
2019-07-31 | 3929.0 | 352307.818182 | 0.011152 |
2019-08-31 | -4843.0 | 399468.000000 | -0.012124 |
2019-09-30 | 17063.0 | 460678.400000 | 0.037039 |
2019-10-31 | 19022.0 | 517172.190476 | 0.036781 |
2019-11-30 | 20319.0 | 571007.684211 | 0.035584 |
2019-12-31 | 21238.0 | 645114.238095 | 0.032921 |
2020-01-31 | 15858.0 | 688004.947368 | 0.023049 |
2020-02-29 | -56921.0 | 732071.222222 | -0.077753 |
2020-03-31 | 7343.0 | 817248.000000 | 0.008985 |
(OHLCを使わなくてもagg関数使えば月間リターンの差が計算できました。)
コードはほとんど同じなので折りたたみ(クリックして展開)
data = [
go.Bar(x=df_month.index.strftime('%Y-%m'), y=df_month['interest'], name='月間の利回り', marker_color ='pink')
]
layout = go.Layout(
width=800,
height=450,
xaxis = dict(
title='時間',
tickformat="%Y-%m",
dtick='M1',
tickangle=90,
showgrid=False
),
yaxis = dict(
title='利回り',
range = [-0.15, 0.15],
tickformat='%'
),
showlegend=True,
legend=dict(
x=0.025,
y=0.95,
font=dict(
size=15
),
borderwidth=2
),
)
fig = go.Figure(
data=data,
layout=layout
)
fig.show()
コロナウイルスが世界的に流行しはじめた2020年2月の運用利回りは 約-7.78%
ほどでした。
まだまだ収束の兆しが見えず、経済への影響はこれから本格化してくると思われるので、今後の推移に注目です。
最悪の単月運用利回りは2018年12月の -10.45%
でした。そういえば年末に暴落がありましたね。
おわり
場当たり的に可視化したのでひょっとすると見当違いの見方や間違いをしているかもしれません。その際はこそっとコメントをいただければと思います。
また、このデータがあるのならこういう分析が面白そうなど、ご意見やアドバイスありましたらぜひご提案ください。よろしくお願いします。最後までご覧いただきありがとうございました。