bookloveru2
pythonで株価分析⑮【コピペでOK! 多層パーセプトロンで株価予測モデルを構築する(機械学習)】
更新日:2021年11月1日
皆様こんにちは。
今回はpythonで株価分析の第15幕です(・ω・)ノ
表題の通り、機械学習を利用して株価予測モデルを構築していきます。
では、早速いってみましょう(・ω・)ノ
目次
❶ 多層パーセプトロンとは?を30秒で解説
❷ 株価予測モデル構築
❸ 株価予測モデル解説
❹ pythonコード解説
➎ python一括コード
❻ Facebookが開発した機械学習ライブラリで株価を予測
❶ 多層パーセプトロンとは?を30秒で解説
パーセプトロンは、下図の〇(赤丸)のことです。
例:ここに1本のワインがあるとします。
これが「赤ワイン」か「白ワイン」かを判定する為に、
【年数・アルコール度数・色合い】
という3つの情報を利用して結果を判定します。
左側から、入力層、中間層、出力層と層がたくさんある為、多層パーセプトロンと言います。

画像は国内最強の頭脳集団:PFNのchainerチュートリアルより👆
https://tutorials.chainer.org/ja/13_Basics_of_Neural_Networks.html
多層パーセプトロンの説明は以上です(`・ω・´)ゞ
❷ 株価予測モデル構築
では、ここからは多層パーセプトロンを利用して株価予測モデルを構築していきます。
ワイン判定の様に
【年数・アルコール度数・色合い】
という情報の、株価バージョンを決めて、株価予測モデルを構築します。
今回の株価バージョンでは、
【金利・原油価格・ダウ平均株価・出来高】
を情報として利用します。
上記4点を基に、多層パーセプトロンで構築した株価予測モデルの結果を下記に示します。

・多層パーセプトロンで構築したモデル:株価の上下予測
・対象銘柄:バンク・オブ・アメリカ【BAC】
・学習期間:2016.6.14~2021.6.13(5年間)
・予測モデルの精度(結果):54.9%
となりました。
これは、【金利・原油価格・ダウ平均株価・出来高】という情報を利用すると約55%の確率で株価の上下予測が可能❕ということを意味します(`・ω・´)ゞ
まず、バンク・オブ・アメリカの5年間の株価を見ていきます。
2016年ちゅばんより上昇傾向。コロナショックで死亡。異次元緩和で再復活という感じです👇

https://finance.yahoo.com/quote/BAC?p=BAC
ここで、今回作成した予測モデルの出力結果です👇

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_