Posted at

Jupyter Notebook 上で 日本株 株式投資ファンダメンタル分析 in 2017 -2018


本記事の内容


  • Jupyter Notebook 上で 日本株 株式投資における財務指標の分析を行なってみた結果を書いております

  • 内容自体は、finpy忘年会のLTネタがベースとなっております


自己紹介


  • 入社4年目。本業では Rails でクラウドサービスの使用リソースや販売量の可視化ツール開発をやっていたりします。投資は業務とは無関係で完全に趣味です。業務関連のネタも投稿していきたい。


本記事の背景

💹 2018年は QuantX[1], Quantopian[2] など趣味でシグナルトレード株式投資に出会い始めた1年でした。

😔 ボリンジャーバンド、RSI など、時系列データに対する TA-lib[3] を利用したテクニカル分析を先に学んできたけど、ファンダメンタル分析はあまり意識してなかった・・・

✨ 当然ながら、株価が 下降傾向 の銘柄に対してテクニカル分析によるシグナルトレードをするよりも、上昇傾向 である銘柄に対して適応した方が当然勝率が高そう。

🤔 でも、座学としては様々な観点があると言われているが、実際の結果と照らし合わせてみないと実感がわかない・・・

💪 KABU+[4] で 2017~2018年分の財務指標、株価データを取得したので、実際の 財務指標の値 と現在までの 株価上昇率 に相関があるかを調べることで、注目すべき項目を明らかにしたい!というのが背景です。

(KABU+では記事掲載する際に、分析結果を画像化することが規約となっております)

💡 この記事がみなさまの Jupyter Notebook 上での分析、可視化の参考、株式投資での財務指標分析の参考となれば幸いです。


今回分析する対象の財務指標について

今回関わる財務指標は以下の通りです。詳しい説明は端折ります。

太字 になっている指標に対して比較・分析を行います。


  • 📊 売上高: 会社本来の営業活動によって稼いだお金の総額

  • 💵 総資産: 企業のすべての資産の総額

  • 💰 当期純利益: 事業年度に計上されるすべての収益からすべての費用を差し引いて計算される当期の最終的な利益

  • 💰 営業利益 / 営業利益率: 企業が本業で稼いだ利益のこと、また売上高に対する割合

  • 👨‍👩‍👧 自己資本 / 自己資本比率: 企業が自社内部で調達した資本のこと、また総資本(自己資本 + 他人資本)に対する割合

  • 👨‍👩‍👧 ROE(Return On Equity / 自己資本利益率): 当期純利益 ÷ 自己資本 × 100

  • 💰 ROA(Return On Assets / 総資産利益率): 当期純利益 ÷ 総資産 × 100


Jupyter Notebook で計算

今回 KABU+ より取得するデータは下記の通り

① 財務指標データ: 2017/04/01

② 財務指標変動データ: 2017/04/01 ~ 2018/04/07

③ 株価変動データ(2017年度): 2017/04/03 ~ 2018/03/30

④ 株価変動データ(2018年度): 2018/04/01 ~ 2018/11/30

下記のデータにおける相関、関係性を調査する


  • a. ① 財務指標データ → ③ 株価変動データ(2017年度)

  • b. ② 財務指標変動データ → ④ 株価変動データ(2018年度)


a. ① 財務指標データ → ③ 株価変動データ(2017年度)


2017年度の財務指標の値を取得・整形


python

before_finance_df = pd.read_csv('japan-all-stock-data_20180402.csv',encoding='cp932', index_col=0)

before_finance_df.head(3)


2017, 2018年度の株価データを取得・整形


python

finance_df = pd.read_csv('japan-all-stock-financial-results_20170401.csv',encoding='cp932', index_col=0)

finance_df = finance_df[finance_df.ROE != '-'] # 値がないデータ行を排除
finance_df = finance_df[finance_df.ROA != '-'] # 値がないデータ行を排除
finance_df = finance_df[finance_df['営業利益(百万円)'] != '-'] ## 値がないデータ行を排除
finance_df.head(2)


python

series_before = pd.read_csv('log/japan-all-stock-prices_20170403.csv',encoding='cp932', index_col=0)['株価']

series_before = series_before[series_before != '-']
series_before.head(2)


データを統合した DataFrameの作成


python

series_after = pd.read_csv('log/japan-all-stock-prices_20180330.csv',encoding='cp932', index_col=0)['株価']

series_after = series_after[series_after != '-']
series_after.head(2)


相関係数の算出


python

df = pd.DataFrame(data=0,columns=[], index=finance_df.index)

df['before'] = series_before.astype(np.float64)
df['after'] = series_after.astype(np.float64)
df['ratio'] = (series_after.astype(np.float64) - series_before.astype(np.float64)) / series_before.astype(np.float64)
df['roa'] = finance_df['ROA'].astype(np.float64)
df['roe'] = finance_df['ROE'].astype(np.float64)
df['自己資本比率'] = finance_df['自己資本比率'].astype(np.float64)
df['営業利益率'] = finance_df['営業利益(百万円)'].astype(np.float64) / finance_df['売上高(百万円)'].astype(np.float64) * 100
df.corr()


相関係数のヒートマップ可視化


python

import seaborn as sns

sns.heatmap(df.corr(),annot=True)
plt.show() # 描画


ROA と 株価上昇率のプロット

(※ 見やすさのために外れ値を除外)


python

df[(df.ratio < 15) & (df.roa > -100) & (df.roe > -500) & (df['営業利益率'] > -100)].plot.scatter(x='ratio', y='roe')



ROE と 株価上昇率のプロット

(※ 見やすさのために外れ値を除外)


python

df[(df.ratio < 15) & (df.roa > -100) & (df.roe > -500) & (df['営業利益率'] > -100)].plot.scatter(x='ratio', y='roa')



自己資本比率 と 株価上昇率のプロット

(※ 見やすさのために外れ値を除外)


python

df[(df.ratio < 15) & (df.roa > -100) & (df.roe > -500) & (df['営業利益率'] > -100)].plot.scatter(x='ratio', y='自己資本比率')



営業利益率 と 株価上昇率のプロット

(※ 見やすさのために外れ値を除外)


python

df[(df.ratio < 15) & (df.roa > -100) & (df.roe > -500) & (df['営業利益率'] > -100)].plot.scatter(x='ratio', y='営業利益率')



要約統計量の算出


python

df.describe(include = 'all')



株価上昇率が中央値以上か否かでラベリング


python

df['ratio_label'] = ''

for (sym, val) in df['ratio'].items():
if (df.loc[sym, 'ratio'] > 0.19): # 1.06 = 中央値
df.loc[sym, 'ratio_label'] = 'upper'
else:
df.loc[sym, 'ratio_label'] = 'lower'

df.head()



株価上昇率 上位群、下位群それぞれにおける各指標に対するプロット

青:上位群 ( > 中央値)

橙:下位群 ( <= 中央値)

(※ 見やすさのために外れ値を除外)


python

sns.pairplot(df[['roa', 'roe', '自己資本比率', '営業利益率', 'ratio_label']][(df['roa'] > -50) & (df['roe'] > -50) & (df['営業利益率'] > -20)].dropna(), hue='ratio_label')



株価上昇率 下位群の要約統計量の算出


python

df[['roa', 'roe', '自己資本比率', '営業利益率', 'ratio_label']][df['ratio_label'] == 'lower'].dropna().describe(include = 'all')



株価上昇率 上位群の要約統計量の算出


python

df[['roa', 'roe', '自己資本比率', '営業利益率','ratio_label']][df['ratio_label'] == 'upper'].dropna().describe(include = 'all')


... と、ここまでみる限りは、両群に差はあまり見られないので次に進む


b. ② 財務指標変動データ → ④ 株価変動データ(2018年度)

データの読み込み、整形に関しては a の手順と同様です。


2017, 2018年度の財務指標、株価の値を取得・整形


python

finance_2017_df = pd.read_csv('japan-all-stock-financial-results_20170401.csv',encoding='cp932', index_col=0)

finance_2017_df = finance_2017_df[finance_2017_df.ROE != '-'] # 値がないデータ行を排除
finance_2017_df = finance_2017_df[finance_2017_df.ROA != '-'] # 値がないデータ行を排除
finance_2017_df = finance_2017_df[finance_2017_df['営業利益(百万円)'] != '-'] ## 値がないデータ行を排除
finance_2017_df = finance_2017_df[finance_2017_df['売上高(百万円)'] != '-'] ## 値がないデータ行を排除

finance_2018_df = pd.read_csv('japan-all-stock-financial-results_20180407.csv',encoding='cp932', index_col=0)
finance_2018_df = finance_2018_df[finance_2018_df.ROE != '-'] # 値がないデータ行を排除
finance_2018_df = finance_2018_df[finance_2018_df.ROA != '-'] # 値がないデータ行を排除
finance_2018_df = finance_2018_df[finance_2018_df['営業利益(百万円)'] != '-'] ## 値がないデータ行を排除
finance_2018_df = finance_2018_df[finance_2018_df['売上高(百万円)'] != '-'] ## 値がないデータ行を排除

series_before = pd.read_csv('log/japan-all-stock-prices_20180330.csv',encoding='cp932', index_col=0)['株価']
series_before = series_before[series_before != '-']

series_after = pd.read_csv('log/japan-all-stock-prices_20181130.csv',encoding='cp932', index_col=0)['株価']
series_after = series_after[series_after != '-']



データを統合した DataFrameの作成


python

sales_before = finance_2017_df['営業利益(百万円)'].astype(np.float64) / finance_2017_df['売上高(百万円)'].astype(np.float64) * 100

sales_after = finance_2018_df['営業利益(百万円)'].astype(np.float64) / finance_2018_df['売上高(百万円)'].astype(np.float64) * 100
roa_before = finance_2017_df['ROA'].astype(np.float64)
roa_after = finance_2018_df['ROA'].astype(np.float64)
roe_before = finance_2017_df['ROE'].astype(np.float64)
roe_after = finance_2018_df['ROE'].astype(np.float64)
own_before = finance_2017_df['自己資本比率'].astype(np.float64)
own_after = finance_2018_df['自己資本比率'].astype(np.float64)

df2 = df.copy()

df2['before'] = series_before.astype(np.float64)
df2['after'] = series_after.astype(np.float64)
df2['自己資本比率'] = finance_df['自己資本比率'].astype(np.float64)
df2['営業利益率'] = finance_2018_df['営業利益(百万円)'].astype(np.float64) / finance_2018_df['売上高(百万円)'].astype(np.float64) * 100
df2['roa'] = finance_2018_df['ROA'].astype(np.float64)
df2['roe'] = finance_2018_df['ROE'].astype(np.float64)

df2['営業利益率_diff'] = (sales_after.astype(np.float64) - sales_before.astype(np.float64)) / sales_before.astype(np.float64)
df2['自己資本比率_diff'] = (own_after.astype(np.float64) - own_before.astype(np.float64)) / own_before.astype(np.float64)
df2['roa_diff'] = (roa_after.astype(np.float64) - roa_before.astype(np.float64)) / roa_before.astype(np.float64)
df2['roe_diff'] = (roe_after.astype(np.float64) - roe_before.astype(np.float64)) / roe_before.astype(np.float64)
df2['ratio'] = (series_after.astype(np.float64) - series_before.astype(np.float64)) / series_before.astype(np.float64)
df2.head()



相関係数の算出


python

df.corr()



相関係数のヒートマップを表示


python

import seaborn as sns

sns.heatmap(df2.corr(),annot=True)
plt.show() # 描画


要約統計量の算出


python

df2.describe(include = 'all')



株価上昇率 が中央値以上か否かでラベリング


python

df2['ratio_label'] = ''

for (sym, val) in df2['ratio'].items():
if (df2.loc[sym, 'ratio'] > 0.14):
df2.loc[sym, 'ratio_label'] = 'upper'
else:
df2.loc[sym, 'ratio_label'] = 'lower'

df2.head()



株価上昇率 上位群、下位群それぞれにおける各指標に対するプロット


python

sns.pairplot(df2[['ratio', 'roa', 'roe', '自己資本比率', '営業利益率', 'roa_label','ratio_label']][df2['営業利益率'] > -1].dropna(), hue='ratio_label')


橙:上位群 ( > 中央値)

青:下位群 ( <= 中央値)

(※ 見やすさのために外れ値を除外)

... 先ほどと同様に両群の差はほとんど見られなさそう


指標に対する 株価上昇率 下位群の要約統計量の算出


指標に対する 株価上昇率 上位群の要約統計量の算出


python

df3 = df2[['roa_diff', 'roe_diff', '自己資本比率_diff', '営業利益率_diff', 'ratio_label']][(df2['営業利益率_diff'] > -1.5) & (df2['自己資本比率_diff'] < 1) & (df2['営業利益率_diff'] < 5) & (df2['roa_diff'] > -20) & (df2['roa_diff'] < 20)]

sns.pairplot(df3.dropna(), hue='ratio_label')

橙:上位群 ( > 中央値)

青:下位群 ( <= 中央値)

(※ 見やすさのために外れ値を除外)

... 上位群の方が若干変動率が 0 に寄っている・・・?

ROE, ROA が前年度から 10% 以上減少していると、株価が上昇しにくい傾向・・・?


指標変動率に対する 株価上昇率 下位群の要約統計量の算出


指標変動率に対する 株価上昇率 上位群の要約統計量の算出


考察

❗️ ROE, ROA が前年度から 10% 以上減少していると、株価が上昇しにくい傾向

❗️ 株価が大きく上昇する可能性があるのは営業利益率が25%以下の傾向(小型株の方が上昇率が高いだけかも)

😔 しかしながら、1, 2年分の財務指標からの値単体では将来の株価の上昇を予測することは難しそう

💡 数年分の結果 から分析すれば別の結果が得られるかもしれない

💡 中長期的な財務指標の変動と中長期的な株価の上昇を検討すればまた別の結果が得られるかもしれない

💡 売上高で企業規模を分けて分析すれば別の結果が得られるかもしれない


結論

❗️ 財務指標の値の変動が大きければ株価は比較的に上がりにくい傾向(結局分かったのは1年単位で投資する場合に、投資すべきでない銘柄についてだけであった・・・)

😔 1, 2年分の財務指標からの値単体では将来の株価の上昇を予測することは難しそう

💪 だが、財務指標と株価の上昇について全く相関がないと、考えるには早計なので今後とも分析を深めて自分なりの指標基準というものを確立させていきたい

長くなりましたが、読んでいただきありがとうございました 🙇‍♂️


免責注意事項


  • 今回の記事の情報に基づいて投資し、損害が発生した場合、一切責任を持ちません

  • 投資は自己責任、自己判断にてお願い致します


外部リンク

[1] QuantX

[2] Quantopian

[3] TA-lib

[4] KABU+