top of page
  • 執筆者の写真bookloveru2

pythonで株価分析⑮【コピペでOK! 多層パーセプトロンで株価予測モデルを構築する(機械学習)】

更新日:2021年11月1日


皆様こんにちは。

今回はpythonで株価分析の第15幕です(・ω・)ノ


表題の通り、機械学習を利用して株価予測モデルを構築していきます。


では、早速いってみましょう(・ω・)ノ



 

目次


 


❶ 多層パーセプトロンとは?を30秒で解説


パーセプトロンは、下図の〇(赤丸)のことです。


例:ここに1本のワインがあるとします。

これが「赤ワイン」か「白ワイン」かを判定する為に、

【年数・アルコール度数・色合い】

という3つの情報を利用して結果を判定します。


左側から、入力層、中間層、出力層と層がたくさんある為、多層パーセプトロンと言います。

画像は国内最強の頭脳集団:PFNのchainerチュートリアルより👆


多層パーセプトロンの説明は以上です(`・ω・´)ゞ


❷ 株価予測モデル構築

では、ここからは多層パーセプトロンを利用して株価予測モデルを構築していきます。



ワイン判定の様に

【年数・アルコール度数・色合い】

という情報の、株価バージョンを決めて、株価予測モデルを構築します。


今回の株価バージョンでは、

【金利・原油価格・ダウ平均株価・出来高】

を情報として利用します。


上記4点を基に、多層パーセプトロンで構築した株価予測モデルの結果を下記に示します。



・多層パーセプトロンで構築したモデル:株価の上下予測

・対象銘柄:バンク・オブ・アメリカ【BAC】

・学習期間:2016.6.14~2021.6.13(5年間)

・予測モデルの精度(結果):54.9%


となりました。


これは、【金利・原油価格・ダウ平均株価・出来高】という情報を利用すると約55%の確率で株価の上下予測が可能❕ということを意味します(`・ω・´)ゞ



まず、バンク・オブ・アメリカの5年間の株価を見ていきます。

2016年ちゅばんより上昇傾向。コロナショックで死亡。異次元緩和で再復活という感じです👇



ここで、今回作成した予測モデルの出力結果です👇



ROC曲線は、イマイチ。予測が当たるかは、1/2といった所です。

(´;ω;`)ウッ…

そう簡単には、億万長者になれそうもありません( ノД`)シクシク…👇




❸ 株価予測モデル解説


株価予測モデル

作成した予測モデルは、「翌日の株価が上昇したら、1」を「翌日の株価が下落したら、0」を出力します。

予測の「1と0」の個数を、実際の値「1と0」の個数と比較し、その正解率が54.9%となっております。


対象期間の学習率:学習データ7割、テストデータ3割





さて、予測モデルの結果ですが、下記の通り、1問目から間違っています(´;ω;`)ウゥゥ👇


という計算の結果、正解率は約55%でした(・ω・)ノ



【金利・原油価格・ダウ平均株価・出来高】という情報を利用すると約55%の確率で株価の上下予測が可能❕


ていうか、半分くらいしか当たらんやんけ(*ノωノ)

という感想でした。

ま、選択した情報が大したこと無かったのかなぁ~。。


以上でモデルの解説は終わりです。次は、pythonコードです。






❹ pythonコード解説


#@title 多層パーセプトロンで株価予測
import datetime
import fix_yahoo_finance as yf
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
 
#スタート日を決める
start = "2016-6-14" #@param {type:"string"}

#①株価を取得する
start = start #株価を取得するスタート日を決める
end = datetime.date.today()     #現在までの営業日(今日 =today)を取得する最終日とする
codelist = ["^DJI","^N225","^FTSE","000001.SS","IMOEX.ME","^BSESN","^BVSP", "BTC-USD","^TNX","GC=F","CL=F"]
 #コードリストに取得したい銘柄を入れる アメリカ 日本 イギリス 中国 ロシア インド ブラジル ビットコ 10年金利 金 原油
#②取得した株価の内、終値をdata2に格納する
data2 = yf.download(codelist, start=start, end=end)["Adj Close"] .dropna()
data2.rename(columns={'^DJI':'ダウ',
 '^N225':'日経平均',
 '^FTSE':'イギリス',
 "000001.SS":'中国',
 'IMOEX.ME':'ロシア',
 '^BSESN':'インド',
 '^BVSP':'ブラジル',
 'BTC-USD':'ビットコ',
 '^TNX':'10 Year Treasury',
 "GC=F":"金",
 "CL=F":"原油"},inplace=True)
data2

実行結果👇

スタート日から今日現在までの世界主要株価+指数の終値データが作成されます。


 




# 前処理開始。分析用のXとyを作成。
import pandas_datareader as pdr
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

ticker = "BAC" #@param {type:"string"}
df = pdr.get_data_yahoo(ticker, start, end)
# data2から説明変数追加
df["金利"] = data2['10 Year Treasury']
df["原油"] = data2['原油']
df["ダウ"] = data2['ダウ']
# 前日との差分をDiffへ格納
df["Diff"] = df["Adj Close"].diff()
# 終値×出来高を"出来高"へ格納
df["出来高"] = df["Adj Close"] * df["Volume"]

実行結果👇

説明変数として作成した【金利・原油価格・ダウ平均価格・Diff(前日との差分)・対象銘柄(今回はバンク・オブ・アメリカの)出来高】データがデータフレームに追加されます。




 


# 差分[Diff]を1行、上にずらして(.shift(-1))、xが0より大きい(つまり値上がりしていれば1。)
# xが0より大きくない(つまり値下がりしていれば0。)のラムダ式xを作成。.apply()でラムダ式をdf[Diff]へ格納して、df[y]を作成。
# yは未来の値。yが1なら明日の株価終値は上昇。つまりyは正解ラベル。
df["y"] = df["Diff"].apply(lambda x: 1 if x > 0 else 0).shift(-1)
# OpenとかHighとかをdfから削除.drop(["消すindex"],axis=1で列を指定)。.dropna()で欠損値のある行を削除。
df2 = df.drop(["Open", "High", "Low", "Close", "Volume", "Diff", "Adj Close"],axis=1,).dropna()
print('\033[35m'+"dfを表示\n"+'\033[0m', (df))
print('\033[35m'+"df2を表示\n"+'\033[0m', (df2))

# 説明変数
# yを列ごと削除してXを作成。
X = df2.drop(["y"], axis=1)
# 目的変数
# dfのyの値をyへ格納。yは正解ラベル。つまり(値下がりと値上がりの[0,1])だけのデータをyに充当。
y = df2["y"]
y

実行結果👇

前述のデータフレームから不必要な項目を削除。その際、未来の結果[0 or 1]を.shift(-1)で当日に持たせる。

未来の結果を[y]にいれて、それを表示。

つまり、正解・不正解が下図の黄色い枠(赤い丸で囲ったとこ)の中にある(0なら明日の株価下落。1なら明日の株価上昇)。




 



# デフォルトはtest_size=0.25で25%がテスト用、残りの75%が訓練用。ここでは、テスト用を30%に設定。
# デフォルトでは要素がシャッフルされて分割される。引数shuffle=Falseとするとシャッフルされずに先頭から順番に分割される。
# 機械学習のモデルの性能を比較するような場合、どのように分割されるかによって結果が異なってしまうため、乱数シードを固定して常に同じように分割されるようにする必要がある。
# ここでは使用していないが、要素がシャッフルされる場合、デフォルトでは実行するたびにランダムに分割される。引数random_stateを指定して乱数シードを固定すると常に同じように分割される。
# https://note.nkmk.me/python-sklearn-train-test-split/
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3,shuffle=False,)

# モデルに多層パーセプトロンを適用 [StandardScaler()で標準化] [shuffle=False先頭から順番に分割]
clf = make_pipeline(StandardScaler(), MLPClassifier(random_state=0, shuffle=False))
clf.fit(X_train,y_train)

print("\nX_train.shape", X_train.shape)
print("\ny_train.shape", y_train.shape)
print("\nX_test.shape", X_test.shape)
print("\ny_test.shape", y_test.shape)
print("\n学習用の説明変数 X_train -------------------------\n", X_train)
print("\n学習用の目的変数 y_train -------------------------\n", y_train)
print("\n評価用の説明変数 X_test  -------------------------\n", X_test)
print("\n評価用の目的変数 y_test  -------------------------\n", y_test)


# 学習させた内容を、テスト用データで確かめる。
predict = clf.predict(X_test)
print("\n予測値 predict --------------------------------------------------\n", predict)

実行結果👇

X_train.shape(679,4)は説明変数が679個の【金利・原油・ダウ・出来高】時系列データ。

4個の説明変数【金利・原油・ダウ・出来高】を表す。

y_tarin(679,)は[0 ore1]の答え。


X_testとかtestは同様のデータ(テスト用:3割に分割されたデータ)。




重要なのは、予測値(predict)。これがモデルで作成した明日の株価の上下予測である。👇




 



# 正解率(accuracy)、適合率(precision)、再現率(recall)を表示
print('\033[1m'+"\nモデルの正解率 全てのサンプルの正解率 (TP+TN/(TP+TN+FP+FN))--------------------------------------------------\n" + '\033[0m', accuracy_score(y_test, predict) , "\n\n適合率 真陽性/陽性の判定(真陽性+偽陽性)(TP/(TP+FP))\n",precision_score(y_test, predict),"\n\n再現率 真陽性/全ての陽性(真陽性+偽陰性)(TP/(TP+FN))\n", recall_score(y_test, predict))
print("\n教師データを使った正解率--------------------------------------------------\n",clf.score(X_train, y_train))
print("\nテストデータを使った正解率--------------------------------------------------\n",clf.score(X_test, y_test))

実行結果👇

黄色い線の正解率が予測と正解(教師データ)のマッチング率である。

再現率も大事だよ🙉




 


# 分類問題の評価基準であるprecision(適合率)、recall(再現率)、f1-score(F値)、support(実際のサンプル数)を出力。
import numpy as np
from sklearn.metrics import classification_report
print("\n評価基準--------------------------------------------------\n",classification_report(y_test, predict,target_names=['0 or price down', '1 or price up']))

# 平均二乗誤差を出力。
from sklearn.metrics import mean_squared_error
print("\n平均二乗誤差--------------------------------------------------\n",np.sqrt(mean_squared_error(y_test, predict)))

# 混同行列を生成: confusion_matrix()
from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

y_true = y_test
y_pred = predict

cm = confusion_matrix(y_true, y_pred)

print("\n混同行列--------------------------------------------------\n", cm)
#グラフの箱サイズ
plt.figure(figsize=(4,2))
sns.heatmap(cm, annot=True, cmap='Blues')
plt.savefig('sklearn_confusion_matrix.png')

# https://qiita.com/0NE_shoT_/items/b702ab482466df6e5569
# FPR(偽陽性率)、TPR(真陽性率)、閾値。
from sklearn.metrics import roc_curve, auc

Y_score = clf.predict_proba(X_test)[:, 1] # 検証データが陽性(クラス1)に属する確率
fpr, tpr, thresholds = roc_curve(y_true=y_test, y_score=Y_score)
#グラフの箱サイズ
plt.figure(figsize=(8,6))
plt.plot(fpr, tpr, label='roc curve (area = %0.3f)' % auc(fpr, tpr))
plt.plot([0, 1], [0, 1], linestyle='--', label='random')
plt.plot([0, 0, 1], [0, 1, 1], linestyle='--', label='ideal')
plt.legend()
plt.xlabel('FPR=false positive rate')
plt.ylabel('TPR=true positive rate')
plt.show()

実行結果👇

precision = 適合率

recall = 再現率

F1スコア、support = 個数


混同行列

左上 TN(真陰性)、右上 FP(偽陽性)、左下 FN(偽陰性)、右下 TP(真陽性)








➎ python一括コード

コードです(*^-^*)


#@title 多層パーセプトロンで株価予測
import datetime
import yfinance as yf
import matplotlib.pyplot as plt
import seaborn as sns
from pandas_datareader import data as pdr
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
import mplfinance as mpf
import requests
import argparse

#スタート日を決める
start = "2020-1-1" #@param {type:"string"}

#①株価を取得する
start = start #株価を取得するスタート日を決める
end = datetime.date.today()     #現在までの営業日(今日 =today)を取得する最終日とする
codelist = ["^DJI","^N225","^FTSE","000001.SS","IMOEX.ME","^BSESN","^BVSP", "BTC-USD","^TNX","GC=F","CL=F"]
#コードリストに取得したい銘柄を入れる アメリカ 日本 イギリス 中国 ロシア インド ブラジル ビットコ 10年金利 金 原油
yf.pdr_override() # <== that's all it takes :-)
# download dataframe
data2 = pdr.get_data_yahoo(codelist, start, end)["Adj Close"] .dropna()
data2.rename(columns={'^DJI':'ダウ',
 '^N225':'日経平均',
 '^FTSE':'イギリス',
 "000001.SS":'中国',
 'IMOEX.ME':'ロシア',
 '^BSESN':'インド',
 '^BVSP':'ブラジル',
 'BTC-USD':'ビットコ',
 '^TNX':'10 Year Treasury',
 "GC=F":"金",
 "CL=F":"原油"},inplace=True)

# 前処理開始。分析用のXとyを作成。
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

ticker = "MSFT" #@param {type:"string"}
yf.pdr_override()
df = pdr.get_data_yahoo(ticker, start, end)

# data2から説明変数追加
df["金利"] = data2['10 Year Treasury']
df["原油"] = data2['原油']
df["ダウ"] = data2['ダウ']
# 前日との差分をDiffへ格納
df["Diff"] = df["Adj Close"].diff()

# 終値×出来高を"出来高"へ格納
df["出来高"] = df["Adj Close"] * df["Volume"]
# 差分[Diff]を1行、上にずらして(.shift(-1))、xが0より大きい(つまり値上がりしていれば1。)
# xが0より大きくない(つまり値下がりしていれば0。)のラムダ式xを作成。.apply()でラムダ式をdf[Diff]へ格納して、df[y]を作成。
# yは未来の値。yが1なら明日の株価終値は上昇。つまりyは正解ラベル。
df["y"] = df["Diff"].apply(lambda x: 1 if x > 0 else 0).shift(-1)
# OpenとかHighとかをdfから削除.drop(["消すindex"],axis=1で列を指定)。.dropna()で欠損値のある行を削除。
df2 = df.drop(["Open", "High", "Low", "Close", "Volume", "Diff", "Adj Close"],axis=1,).dropna()
# print('\033[35m'+"dfを表示\n"+'\033[0m', (df))
# print('\033[35m'+"df2を表示\n"+'\033[0m', (df2))

# 説明変数
# yを列ごと削除してXを作成。
X = df2.drop(["y"], axis=1)
# 目的変数
# dfのyの値をyへ格納。yは正解ラベル。つまり(値下がりと値上がりの[0,1])だけのデータをyに充当。
y = df2["y"]

# デフォルトはtest_size=0.25で25%がテスト用、残りの75%が訓練用。ここでは、テスト用を30%に設定。
# デフォルトでは要素がシャッフルされて分割される。引数shuffle=Falseとするとシャッフルされずに先頭から順番に分割される。
# 機械学習のモデルの性能を比較するような場合、どのように分割されるかによって結果が異なってしまうため、乱数シードを固定して常に同じように分割されるようにする必要がある。
# ここでは使用していないが、要素がシャッフルされる場合、デフォルトでは実行するたびにランダムに分割される。引数random_stateを指定して乱数シードを固定すると常に同じように分割される。
# https://note.nkmk.me/python-sklearn-train-test-split/
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3,shuffle=False,)

# モデルに多層パーセプトロンを適用
clf = make_pipeline(StandardScaler(), MLPClassifier(random_state=0, shuffle=False))
clf.fit(X_train,y_train)

print("\nX_train.shape", X_train.shape)
print("\ny_train.shape", y_train.shape)
print("\nX_test.shape", X_test.shape)
print("\ny_test.shape", y_test.shape)
print("\n学習用の説明変数 X_train -------------------------\n", X_train)
print("\n学習用の目的変数 y_train -------------------------\n", y_train)
print("\n評価用の説明変数 X_test  -------------------------\n", X_test)
print("\n評価用の目的変数 y_test  -------------------------\n", y_test)


# 学習させた内容を、テスト用データで確かめる。
predict = clf.predict(X_test)
print("\n予測値 predict --------------------------------------------------\n", predict)

# 正解率(accuracy)、適合率(precision)、再現率(recall)を表示
print('\033[1m'+"\nモデルの正解率 全てのサンプルの正解率 (TP+TN/(TP+TN+FP+FN))--------------------------------------------------\n" + '\033[0m', accuracy_score(y_test, predict) , "\n\n適合率 真陽性/陽性の判定(真陽性+偽陽性)(TP/(TP+FP))\n",precision_score(y_test, predict),"\n\n再現率 真陽性/全ての陽性(真陽性+偽陰性)(TP/(TP+FN))\n", recall_score(y_test, predict))
print("\n教師データを使った正解率--------------------------------------------------\n",clf.score(X_train, y_train))
print("\nテストデータを使った正解率--------------------------------------------------\n",clf.score(X_test, y_test))

# 分類問題の評価基準であるprecision(適合率)、recall(再現率)、f1-score(F値)、support(実際のサンプル数)を出力。
import numpy as np
from sklearn.metrics import classification_report
print("\n評価基準--------------------------------------------------\n",classification_report(y_test, predict,target_names=['0 or price down', '1 or price up']))

# 平均二乗誤差を出力。
from sklearn.metrics import mean_squared_error
print("\n平均二乗誤差--------------------------------------------------\n",np.sqrt(mean_squared_error(y_test, predict)))

# tickerの株価をプロット
plt.figure(figsize=(6,4)) 
chart = yf.download(ticker, start=start, end=end)
mpf.plot(chart,type='candle',mav=(5,25,60),volume=True,show_nontrading=True)

# 混同行列を生成: confusion_matrix()
from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

y_true = y_test
y_pred = predict

cm = confusion_matrix(y_true, y_pred)

print("\n混同行列--------------------------------------------------\n", cm)
#グラフの箱サイズ
plt.figure(figsize=(4,2))
sns.heatmap(cm, annot=True, cmap='Blues')
plt.savefig('sklearn_confusion_matrix.png')

# https://qiita.com/0NE_shoT_/items/b702ab482466df6e5569
# FPR(偽陽性率)、TPR(真陽性率)、閾値。
from sklearn.metrics import roc_curve, auc

Y_score = clf.predict_proba(X_test)[:, 1] # 検証データが陽性(クラス1)に属する確率
fpr, tpr, thresholds = roc_curve(y_true=y_test, y_score=Y_score)
#グラフの箱サイズ
plt.figure(figsize=(5,4))
plt.plot(fpr, tpr, label='roc curve (area = %0.3f)' % auc(fpr, tpr))
plt.plot([0, 1], [0, 1], linestyle='--', label='random')
plt.plot([0, 0, 1], [0, 1, 1], linestyle='--', label='ideal')
plt.legend()
plt.xlabel('FPR=false positive rate')
plt.ylabel('TPR=true positive rate')
plt.show()


如何でしたでしょうか❕❓

これであなたも機械学習の使い手です!!!(^_-)-☆


銘柄と期間、パラメータを弄繰り回して、素晴らしい予測モデルを発見できるかも❕❓



❻ Facebookが開発した機械学習ライブラリで株価を予測



を使って、バンク・オブ・アメリカの今後の株価を予測して見た。


・対象銘柄:バンク・オブ・アメリカ【BAC】

・学習期間:2016.6.14~2021.6.13(5年間)


現在(2021.6.14時点)の株価が41.86ドルなのに対して、1年後は60ドル付近まで上昇する予測ですね(・ω・)ノ


ホンマけ~~?❓





では、ばいちゃ(・ω・)ノ


pythonでプログラミングがしたい!! 
pythonを学びたい!という方には、
オンラインプログラミング教室『Aidemy アイデミー』がお勧めです。
ご自身でプログラム環境を用意する必要は無く、サイトへアクセスするだけです。  創業者は東京大学工学部卒の石川聡彦さん。
著書に『人工知能プログラミングのための数学がわかる本』(KADOKAWA/2018年)など。  
無料で、お試しもできますので安心です。
お時間ある方は下記リンクを覗いてみてはいかがでしょうか(*'ω'*)






閲覧数:1,265回0件のコメント
bottom of page