Pythonで国内株式インデックスETFのパフォーマンスを比較してみた

つみたてNISA、iDeCoなど活用して、インデックス投資をする上で、国内株式インデックスとして、「日経平均」「TOPIX」「JPX日経400」どれを選ぶといいか、過去の時系列データを比較してみた。

データ取得

つみたてNISAの対象になっている3本のETFを用いる。

  • ダイワ上場投信 – 日経225(2010/10/29~)
  • ダイワ上場投信 – トピックス(2006/1/4~)
  • ダイワ上場投信 – JPX日経400(2014/3/25~)
In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas_datareader.data as web
import datetime
from scipy import stats
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
In [2]:
# 期間設定
start = datetime.datetime(2014,3,31)
end = datetime.datetime(2020,12,31)

# データ取得
df = web.DataReader(['1320.T','1305.T','1599.T'],'yahoo',start,end)['Adj Close']

# 系列名変更
df.columns = ['N225','TOPIX','JPX400']

原系列比較

In [3]:
# 2014年3月末を100として指数化
df_index = df / df.iloc[0]*100
In [4]:
sns.set_style('whitegrid')
df_index.plot(figsize=(10,5))
Out[4]:
<AxesSubplot:xlabel='Date'>

2014年3月31日に購入し、2021年12月31日に売却するとすると、パフォーマンスは日経平均が一番いいことがわかる。 TOPIXとJPX400が、相対的に似た傾向がある。

In [5]:
df_index.describe()
Out[5]:
N225 TOPIX JPX400
count 1667.000000 1667.000000 1667.000000
mean 137.489403 131.318414 129.460083
std 21.343853 16.096415 14.590108
min 94.021028 94.094170 94.829723
25% 119.185292 117.877097 117.941215
50% 137.385025 134.075182 131.826730
75% 154.450013 143.740461 140.841861
max 196.295491 161.798119 160.744910

日経平均が平均値、標準偏差が高いことがわかる。

月次収益率比較

In [8]:
# 日次データから月次データに変換(月末値)
df_index_m = df_index.resample('M').last()

# 収益率に変換
df_r_m = df_index_m.pct_change().dropna()

# 月次収益率をプロット
df_r_m.plot(figsize=(10,5))
Out[8]:
<AxesSubplot:xlabel='Date'>
In [75]:
df_r_m.describe()
Out[75]:
N225 TOPIX JPX400
count 81.000000 81.000000 81.000000
mean 0.009516 0.006873 0.006817
std 0.049087 0.045877 0.045576
min -0.105836 -0.101607 -0.100196
25% -0.013854 -0.010457 -0.017131
50% 0.015398 0.011876 0.012361
75% 0.041937 0.034754 0.034419
max 0.149810 0.111044 0.112329

日経平均の平均月次収益率0.95%、標準偏差4.90%で、TOPIX、JPX400より大きい。

In [14]:
df_r_m.corr()
Out[14]:
N225 TOPIX JPX400
N225 1.00000 0.96725 0.96985
TOPIX 0.96725 1.00000 0.99094
JPX400 0.96985 0.99094 1.00000

各系列とも相関は95%以上で、ほぼ同じ動きをしていることも数値で確認。

In [24]:
# 年率換算
r_y_mean = (1+df_r_m.mean())**12 -1
r_y_sd = np.sqrt(12) * df_r_m.std()

# 平均・標準偏差(年率換算値)プロット
sns.set_style('whitegrid')
plt.figure(figsize=(5,4))
for (i,j,k) in zip(r_y_sd,r_y_mean, df_r_m.columns):
    plt.scatter(i,j)
    plt.annotate(k, xy=(i, j))

plt.xlabel("Standard Deviation (annualized basis)")
plt.ylabel("Mean Return (annualized basis)")
plt.show()
In [15]:
pd.DataFrame({'Mean':r_y_mean,
              'Sd':r_y_sd,
              'Mean/Sd': r_y_mean / r_y_sd})
Out[15]:
Mean Sd Mean/Sd
N225 0.120358 0.170042 0.707813
TOPIX 0.085666 0.158922 0.539046
JPX400 0.084942 0.157881 0.538013

日経平均が平均収益率(年率)12.0%・標準偏差17.0%、TOPIXが平均収益率8.6%・標準偏差15.9%、JPX400が平均収益率が8.5%・標準偏差が15.7%。
リスクあたりのリターン(Mean/Sd)でみると、日経平均が一番高いことがわかる。

In [17]:
sns.set_style('whitegrid')
f, axs = plt.subplots(1, 3, figsize=(12, 4))
sns.histplot(df_r_m, x='N225', stat='probability', kde=True, ax=axs[0])
sns.histplot(df_r_m, x='TOPIX', stat='probability', kde=True, ax=axs[1], color='orange')
sns.histplot(df_r_m, x='JPX400', stat='probability', kde=True, ax=axs[2], color='green')
f.tight_layout()

3つの指数の月次収益率のヒストグラムを確認すると、ほぼ正規分布で、下落の度数が大きくなっていることがわかる。

日経225とTOPIX比較

In [29]:
sns.histplot(df_r_m, x='N225', stat='probability', kde=True, 
             bins=50, alpha=0.5, label='N225')
sns.histplot(df_r_m, x='TOPIX', stat='probability', kde=True, 
             color='orange', bins=50, alpha=0.5, label='TOPIX')

plt.legend()
plt.xlabel('Monthly Return')
Out[29]:
Text(0.5, 0, 'Monthly Return')
In [21]:
stats.ttest_ind(df_r_m['N225'], df_r_m['TOPIX'])
Out[21]:
Ttest_indResult(statistic=0.35399565478311107, pvalue=0.7238082003465377)

日経平均の平均月次収益率が若干大きく見えるが、t検定をするとp値は0.72で、有意な差があるとは言えない。 この期間では、たまたま日経平均の平均月次収益率が高かったようである。

In [47]:
sns.jointplot(data=df_r_m, x='N225', y='TOPIX', kind='scatter')
Out[47]:
<seaborn.axisgrid.JointGrid at 0x7f8a981178e0>

TOPIXとJPX400比較

In [30]:
sns.histplot(df_r_m, x='TOPIX', stat='probability', kde=True, 
             color='orange', bins=50, alpha=0.5, label='TOPIX')
sns.histplot(df_r_m, x='JPX400', stat='probability', kde=True, 
             color='green', bins=50, alpha=0.5, label='JPX400')

plt.legend()
plt.xlabel('Monthly Return')
Out[30]:
Text(0.5, 0, 'Monthly Return')
In [31]:
stats.ttest_ind(df_r_m['TOPIX'], df_r_m['JPX400'])
Out[31]:
Ttest_indResult(statistic=0.007791966753496181, pvalue=0.9937926797890451)
In [32]:
sns.jointplot(data=df_r_m, x='TOPIX', y='JPX400', kind='scatter')
Out[32]:
<seaborn.axisgrid.JointGrid at 0x7f8aa0605e20>

TOPIXとJPX400はほぼ同じ傾向であることを、視覚的に、数値で確認。

おまけ:時系列予測

Topixデータについて、Prophetを使って予測して簡単に可視化してみる。精度は求めておらず、ざっくり過去と同じと仮定すると2021年はこんな感じ程度の情報です。
Prophetのインストールなど詳細は以下公式ドキュメント参照ください。
https://facebook.github.io/prophet/

In [33]:
# 期間設定
start = datetime.datetime(2014,3,31)
end = datetime.datetime(2020,12,31)

# データ取得
df = web.DataReader('1305.T','yahoo',start,end)
df.head()
Out[33]:
High Low Open Close Volume Adj Close
Date
2014-03-31 1254.0 1237.0 1247.0 1253.0 823390.0 1185.469971
2014-04-01 1256.0 1247.0 1256.0 1253.0 439580.0 1185.469971
2014-04-02 1274.0 1258.0 1259.0 1261.0 1341900.0 1193.038940
2014-04-03 1274.0 1262.0 1264.0 1267.0 421990.0 1198.715454
2014-04-04 1268.0 1262.0 1264.0 1266.0 316900.0 1197.769287
In [44]:
df['ds']=df.index
df['y']=df['Adj Close']
df['y'].plot(figsize=(8,4))
Out[44]:
<AxesSubplot:xlabel='Date'>
In [35]:
df.head()
Out[35]:
High Low Open Close Volume Adj Close ds y
Date
2014-03-31 1254.0 1237.0 1247.0 1253.0 823390.0 1185.469971 2014-03-31 1185.469971
2014-04-01 1256.0 1247.0 1256.0 1253.0 439580.0 1185.469971 2014-04-01 1185.469971
2014-04-02 1274.0 1258.0 1259.0 1261.0 1341900.0 1193.038940 2014-04-02 1193.038940
2014-04-03 1274.0 1262.0 1264.0 1267.0 421990.0 1198.715454 2014-04-03 1198.715454
2014-04-04 1268.0 1262.0 1264.0 1266.0 316900.0 1197.769287 2014-04-04 1197.769287
In [36]:
from fbprophet import Prophet

# モデル作成
model = Prophet()
model.fit(df)

# 365日分予測
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)

# 結果確認
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
Importing plotly failed. Interactive plots will not work.
INFO:fbprophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.
Out[36]:
ds yhat yhat_lower yhat_upper
2027 2021-12-26 1811.216256 1506.567817 2082.276551
2028 2021-12-27 1817.959878 1506.842444 2096.142366
2029 2021-12-28 1817.734781 1504.319855 2076.355147
2030 2021-12-29 1817.243343 1489.218907 2098.179368
2031 2021-12-30 1813.376005 1507.709876 2082.316412
In [39]:
fig1 = model.plot(forecast, figsize=(8,4))

2021年も2014年〜2020年と同じような傾向があるとすると、年初から下落して、年末にかけて上昇しそうである。

In [43]:
fig2 = model.plot_components(forecast, figsize=(8,6))

要素をみてみると、上昇トレンドがあり、曜日は水・火がプラス、月は1月、11月~12月がプラス要因のようである。