見出し画像

深層学習で株価を予測しようとした話(全部無料で見れます

こんにちは.タイトルの通りなのですが,この記事は私がpythonを用いて,株価の上昇・下落を予測しようとして作ったプログラムの話になります.持論ですが,儲け話を(特に無料で)公開する人は何か裏があると思ってもらいたいです(わたしが公開する理由は後述).有料ですが,記事は全部タダで読めます.
コードも含めて技術的な事を書いていこうと思います.

結論から申し上げますと,自分的には満足したけどやっぱりテクニカルだけじゃ難しいです.しかし,私自身のやり方には向いていなかったので公開します.
しかしながら,うまく使える方がこの記事を読んで儲けられる可能性はあるとは思います.一応このプログラムを用いて利益を出すことはできました.
なので技術を誰かに託し,自分は有料にしてwin-winにしようという目論見です.結構時間などがかかったので・・・・サポートしていただけると幸いです.

ここから先をみるあなたがそうであることを祈っています.

プログラムを作成するにあたって最低限の過程を示します.
基礎的な部分は他の物を見つつ,補うとわかりやすいと思います.
出来る限り,この記事を読んだだけで理解できるようにします.

Pythonの準備をしよう

pythonが使える環境下には頑張ってしてみてください.少なくともインストールの段階では多くの試料があると思います.
例えば下記のサイトから,最新のverをインストールすることで使用可能になります.

最悪これとメモ帳があればできますが,プログラムを核となった場合,もう少し良いものを使った方がいいと思います.

私はサクラエディタを用いています.

少し古臭いですが,私はこれが好きです.
ほかにも,AtomとかVisual Studio Codeとかがあります.
好きなのをダウンロードして使ってください.
色がついてわかりやすくなります.


プログラムを作っていこう

本当はhello worldから書くべきかもしれませんが,そこは本質ではないので飛ばします.とりあえず下記のモジュールをインポートします.

インポートするためにpipというものが必要になります.
pipはパッケージを導入することが出来るコマンドです.例を出すと,たくさんあるゲームの追加コンテンツの管理をしています.やりたい追加コンテンツを選んでダウンロードするために,このコマンドを用います.

pipをダウンロードするためには,こちらのサイトでできます.

https://bootstrap.pypa.io/get-pip.py

これをインストールしたらコマンドプロンプトを起動して

python get-pip.py

と書きます.そうすることでダウンロードすることが出来るはずです.そして,これが完了したら下記をそれぞれ入力してください.

pip install pandas
pip install numpy
pip install sklearn
pip install yfinance
pip install pickle
pip install requests

意味を説明すると,
pip(を使って)install(次に書くものをインストールします) 名前
という感じです.
pandas, numpyはデータを扱いやすくするために入れます.
sklearn,pickleは機械学習用のモジュールです.
requestsはwebサイトから情報を取るときに使うモジュールです.
yfinanceはyahoo financeからデータを取得します.例えば株価など.

これをインストールすればはれて追加コンテンツをダウンロードできました.準備は完了です.あとは書くだけ!

作っていこう

ファイルを作成しましょう.a.pyとか.
とりあえず一番初めにこれを書きます.頭の部分に.

import pandas as pd
import numpy as np
import datetime as dt
from sklearn.preprocessing import StandardScaler
import requests
import os
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.linear_model import LinearRegression
from sklearn import svm, metrics
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
from sklearn import svm
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import load_digits
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
import pickle
import yfinance as yf

とりあえずこれらをインポートできるようにしましょう.
どれかは使ってないかもしれませんが,基本的に使ったはずです.
さっきダウンロードしたものがありますね.説明するとこんな感じ.
import(今から使いますよ)名前 as(次の略称として使います)略称

続き

def ready_to_data(code):
	#データを収集
	tmp_code = str(code) + '.T'
	my_share = share.Share(tmp_code)

	tmp_symbol_data = yf.download(tickers=tmp_code, period='2y', interval='1d')
	
	tmp_df = pd.DataFrame(tmp_symbol_data)
	print(tmp_df.head(5))	

	#データ整理
	df = tmp_df[['High', 'Low', 'Open', 'Close','Volume']]	


	df['25MA'] = 0
	df['50MA'] = 0
	df['75MA'] = 0
	
	#print(df.shape[0])
	
	for i in range(df.shape[0]):
		if(i>25):
			df.iloc[i,5]= round((df.iloc[i-25:i]).mean(axis=0)["Close"])
		if(i>50):
			df.iloc[i,6]= round((df.iloc[i-50:i]).mean(axis=0)["Close"])
		if(i>75):
			df.iloc[i,7]= round((df.iloc[i-75:i]).mean(axis=0)["Close"])

    df_shift1 = df.shift(1)
	df_shift2 = df.shift(2)
	df_shift3 = df.shift(3)
	df_shift4 = df.shift(4)
	df_shift5 = df.shift(5)


	df['shift_1'] = (df_shift1["Close"] - df["Close"])/(df["Close"])
	df['shift_2'] = (df_shift2["Close"] - df["Close"])/(df["Close"])
	df['shift_3'] = (df_shift3["Close"] - df["Close"])/(df["Close"])
	df['shift_4'] = (df_shift4["Close"] - df["Close"])/(df["Close"])
	df['shift_5'] = (df_shift5["Close"] - df["Close"])/(df["Close"])

	df_shiftf1 = df.shift(-3)
	df_y = (df_shiftf1["Close"] - df["Close"])/(df["Close"])


	df = df.iloc[90:]
	df = df.iloc[:-10]
	df = df.replace([np.inf, -np.inf], np.nan)
	df = df.fillna(0)

	df_y = df_y.iloc[:-10]
	df_y = df_y.iloc[90:]


	df_yy = df_y.copy()

	#評価基準
    df_yy.loc[df_y > 0.1] = 8
	df_yy.loc[(df_y > 0.05) & (df_y<= 0.1)] = 7
	df_yy.loc[(df_y > 0.02) & (df_y<= 0.05)] = 6
	df_yy.loc[(df_y > 0.005) & (df_y<= 0.02)] = 5
	df_yy.loc[(df_y> -0.005) & (df_y<= 0.005)] = 4
	df_yy.loc[(df_y > -0.02)& (df_y<= -0.005)] = 3
	df_yy.loc[(df_y > -0.05) & (df_y<= -0.02)] = 2
	df_yy.loc[(df_y > -0.1) & (df_y<= -0.05)] = 1
	df_yy.loc[(df_y <= -0.1)] = 0


	return df,df_y;

    

ここでは関数を定義しました.
関数は入力に対して何かを出力するものです.
今回は,入力は銘柄コード(トヨタであれば7203)で,
出力は当日のデータ(始値,終値,高値,安値,出来高)と1~5日前との終値の差分と(df),3日後にどの程度上昇しているかを評価した値(df_y)を出力しています.
def ready_to_data(入力):
何かしらを書く
return 出力
という構造です.defは今から関数を定義するという意味です.
ready_to_dataはこの関数名です.

これら二つの出力が,機械学習でいう教師データに該当します.では内部に関して少し詳しく解説していきます.

データを取得しよう

	#データを収集
	tmp_code = str(code) + '.T'

	tmp_symbol_data = yf.download(tickers=tmp_code, period='2y', interval='1d')

この部分が株価などのデータをネットから取得する部分に該当します.
code という変数の中には,例えば7203などの数字が入っています.

yfinanceから日本株のデータを取るためには,文字列で"7203.T"という入力が必要なのでtmp_codeで文字列に変換しています.

tmp_symbol_data = yf.download(tickers=tmp_code, period='2y', interval='1d')
この部分でデータをダウンロードしています.

tickers=コード,period=どのくらいの期間のデータか(今回は2年分),interval=頻度(今回は1日,つまりは日足を取得している)

	tmp_df = pd.DataFrame(tmp_symbol_data)
	#print(tmp_df.head(5))	

	#データ整理
	df = tmp_df[['High', 'Low', 'Open', 'Close','Volume']]	

この部分でデータの整理をしています.
pd.DataFrameという部分で,先ほど取得したデータを扱いやすいデータへ変換しています.pandasと調べていただければよりわかると思います.

print(tmp_df.head(5))正直この部分はいらないので,#を使って動かないようにしています.これをコメントアウトと呼びます.この#の部分を消すと,最新5日分の取得したデータをコマンドプロンプトに表示します.やってみてください.

もし動作しないなどのエラーがあったら,逐一このようにしてprint()してみるのがいいと思っています.

df = tmp_df[['High', 'Low', 'Open', 'Close','Volume']]

意味はほとんどないのですが,並び替えをしています.たぶんしなくてもいいのですが,昔書いたときに少し複雑な作業とかをしていたのでいれています.たぶんなくても動きます.

情報を追加しよう

現在あなたが持っているデータは,当日の始値,終値,高値,安値,出来高の4つです.これらだけで予測するのは難しいと思います.

例えばチャートを見るときに,チャートの形やMACD,RSIなどいくつかの指標を使っているかと思います.

なので,それを反映させます.

今回は,移動平均を導入してみました.

	df['25MA'] = 0
	df['50MA'] = 0
	df['75MA'] = 0
	
	#print(df.shape[0])
	
	for i in range(df.shape[0]):
		if(i>25):
			df.iloc[i,5]= round((df.iloc[i-25:i]).mean(axis=0)["Close"])
		if(i>50):
			df.iloc[i,6]= round((df.iloc[i-50:i]).mean(axis=0)["Close"])
		if(i>75):
			df.iloc[i,7]= round((df.iloc[i-75:i]).mean(axis=0)["Close"])

df['**MA']=0
これは25,50,75日の平均線の値を入れる箱をよういしました.

そして,for文を用いて,さきほど入手した2年分のデータを足し算します.
for i  in range(df.shape[0]):
では,取得したデータの日数分の計算を行います.それぞれif文で25より大きい場合などを取っている理由は,10日しかないのに25日平均が取れないからです.
後で説明しますが,これらのデータをディープラーニングに導入するので,最初の75日分のデータは捨てます.

   df_shift1 = df.shift(1)
	df_shift2 = df.shift(2)
	df_shift3 = df.shift(3)
	df_shift4 = df.shift(4)
	df_shift5 = df.shift(5)


	df['shift_1'] = (df["Close"]-df_shift1["Close"])/(df["Close"])
	df['shift_2'] = (df["Close"]-df_shift2["Close"])/(df["Close"])
	df['shift_3'] = (df["Close"]-df_shift3["Close"])/(df["Close"])
	df['shift_4'] = (df["Close"]-df_shift4["Close"])/(df["Close"])
	df['shift_5'] = (df["Close"]-df_shift5["Close"])/(df["Close"])

チャートの形も大事ですよね.
ここでは5日分しか導入していませんが,1-5日前の終値との差分も学習データに入れてあげましょう.
それぞれdf.shift(日数分)として,終値をずらして引き算しています.n日前からどの程度上昇したかを計算しています.

こんなかんじの構造になっています.名前がややこしくてすみません.

教師データ作成

	df_shiftf1 = df.shift(-3)
	df_y = (df_shiftf1["Close"] - df["Close"])/(df["Close"])

三日後のデータから今のデータを引いています.shift(-3)とマイナスになっています.さっきと逆のことをしているってことですね.
前と同じように最新三日のデータは使えません.心配しないでください.学習用だから大丈夫です.

データ整理


	df = df.iloc[90:]
	df = df.iloc[:-10]
	df = df.replace([np.inf, -np.inf], np.nan)
	df = df.fillna(0)

	df_y = df_y.iloc[:-10]
	df_y = df_y.iloc[90:]
	df_y = df_y.replace([np.inf, -np.inf], np.nan)
	df_y = df_y.fillna(0)

どちらもデータを整理しています.教師データたちのデータを編集しています.一応少し広くとって古い90日分と最新10日分のデータを抜いています.
どこかで言いましたが,75MAを計算するために過去75日分のデータが必要なのです.
df.replace(・・・)の部分はinfと呼ばれる無限大になってしまったデータをnan(not a number)と呼ばれる数字でないデータに変換します.
そして,そのnanを0に置き換えます.

一応変な値は出ないようになっていますが,nanあると学習できないので保険としてやっています.

回答を用意する

	df_yy = df_y.copy()

	#評価基準
    df_yy.loc[df_y > 0.1] = 8
	df_yy.loc[(df_y > 0.05) & (df_y<= 0.1)] = 7
	df_yy.loc[(df_y > 0.02) & (df_y<= 0.05)] = 6
	df_yy.loc[(df_y > 0.005) & (df_y<= 0.02)] = 5
	df_yy.loc[(df_y> -0.005) & (df_y<= 0.005)] = 4
	df_yy.loc[(df_y > -0.02)& (df_y<= -0.005)] = 3
	df_yy.loc[(df_y > -0.05) & (df_y<= -0.02)] = 2
	df_yy.loc[(df_y > -0.1) & (df_y<= -0.05)] = 1
	df_yy.loc[(df_y <= -0.1)] = 0

それぞれ上がり幅(下がり幅)で評価を決めています.例えば3日前から10%以上上がっていれば8みたいな感じです.離散化することで評価しやすくしています.

やっていることは数字の認識のようなものに近いです.たくさん数字(今回はチャートの形など)を学習させて,未知のものに対してこれは何に近いかを聞いています.

それでこれらのチャートの形と評価を教師データにします.
これでデータ取得の関数bは作成完了.
次に進みましょう.

銘柄コード一覧を取得しよう

 	url = "https://www.jpx.co.jp/markets/statistics-equities/misc/tvdivq0000001vg2-att/data_j.xls"
	r = requests.get(url)
	with open('data_j.xls', 'wb') as output:
	    output.write(r.content)
	    
	    
	
	stocklist = pd.read_excel("./data_j.xls")
	stocklist.loc[stocklist["市場・商品区分"]=="市場第一部(内国株)",
	              ["コード","銘柄名","33業種コード","33業種区分","規模コード","規模区分"]
	             ]


	stocklist.loc[stocklist["33業種コード"] == "-",["コード"]] = -1
	stocklist.loc[stocklist["規模区分"] == "-",["コード"]] = -1
	stocklist.loc[stocklist["規模区分"] == "TOPIX Small 1",["コード"]] = -1
	stocklist.loc[stocklist["規模区分"] == "TOPIX Small 2",["コード"]] = -1
	stocklist.loc[stocklist["規模区分"] == "TOPIX Mid400",["コード"]] = -1
	

銘柄コード一覧を取得します.

日本証券取引所から全銘柄名を取得して,エクセルファイルに入れます.

全銘柄を用いて学習すると,普通のパソコンでは困難なので,銘柄をしぼるように書いています.
おあつらえ向きに規模区分というのがあったので,私はそれで分けました.
大型株だけを学習データに使いました.例えば10円の株とかだと1円あがるだけで10%の上昇と判断されてうまく学習できないからですね.
区分がないもの,TOPIX small 1,2,midを省きました.ここら辺は好きに編集してください.

学習データセットを用意しよう

	df,df_y = module.ready_to_data(7203)
	
  for code in stocklist["コード"]:	
		if(code > 0 and code <10000) and(code != 7203) and (type(code) is int)):
			print(code)
			#tmp_code = str(code) + '.T'
			count = count +1
			tmp_df,tmp_df_y = module.ready_to_data(code)
			df = pd.concat([df,tmp_df])
			df_y = pd.concat( [df_y,tmp_df_y])
			

最初に適当にトヨタのデータをロードします.
そしてさきほど用意した銘柄コード(大型を抽出した)のリストでfor文を回します.そして,concatでそれぞれのデータを結合しています.
銘柄にAとかつき始めたので,とりあえずこいつらは省いています.本当は使った方がいいのですが,使ってた頃はなかったので・・.改変するのがめんどくさいのでこのまま使えるように適当修正です.

このfor文が回し終えたとき,dfとdf_yには,2年分のそれぞれ銘柄のデータをくっつけたような配列が出来ます.

学習させよう

	X_train, X_test, y_train, y_test = train_test_split(df, df_y, test_size=0.4, random_state42)

	#-------------SVC-------------
	xtrain = X_train.to_numpy()
	ytrain = y_train.to_numpy()

	xtest = X_test.to_numpy()
	ytest = y_test.to_numpy()

さきほど作成したデータを学習データとテストデータに分割します.60%を学習へ40%をテストデータにしています.

今回はCIFと呼ばれるニューラルネットワークで学習させましょう.

	clf = make_pipeline(StandardScaler(), SVC(kernel='rbf'))
	clf = OneVsRestClassifier(clf)
	clf.fit(xtrain,ytrain)

	#print(clf.score(xtest, ytest))

実行したら結構時間がかかります(銘柄の数や取得した年数によりますが).

そしてスコア(どれだけ正確かを示す値)を出力しています.

予測

def predict_data(code):
	#データを収集
	tmp_code = str(code) + '.T'
	my_share = share.Share(tmp_code)

	tmp_symbol_data = yf.download(tickers=tmp_code, period='1y', interval='1d')
	
	tmp_df = pd.DataFrame(tmp_symbol_data)
	print(tmp_df.head(5))	

	#データ整理
	df = tmp_df[['High', 'Low', 'Open', 'Close','Volume']]	


	df['25MA'] = 0
	df['50MA'] = 0
	df['75MA'] = 0
	
	#print(df.shape[0])
	
	for i in range(df.shape[0]):
		if(i>25):
			df.iloc[i,5]= round((df.iloc[i-25:i]).mean(axis=0)["Close"])
		if(i>50):
			df.iloc[i,6]= round((df.iloc[i-50:i]).mean(axis=0)["Close"])
		if(i>75):
			df.iloc[i,7]= round((df.iloc[i-75:i]).mean(axis=0)["Close"])

    df_shift1 = df.shift(1)
	df_shift2 = df.shift(2)
	df_shift3 = df.shift(3)
	df_shift4 = df.shift(4)
	df_shift5 = df.shift(5)

	df['shift_1'] = (df_shift1["Close"] - df["Close"])/(df["Close"])
	df['shift_2'] = (df_shift2["Close"] - df["Close"])/(df["Close"])
	df['shift_3'] = (df_shift3["Close"] - df["Close"])/(df["Close"])
	df['shift_4'] = (df_shift4["Close"] - df["Close"])/(df["Close"])
	df['shift_5'] = (df_shift5["Close"] - df["Close"])/(df["Close"])

	return df[-1:];

殆ど前のデータ取得のプログラムのコピペで大丈夫です.
違う点は一番新しい点,つまりは今日のデータだけを出力しています.

xpredict = predict_data(7203)
predicted = clf.predict(xpredict)
print(predicted)

これで,トヨタの最新のデータから三日後,どうなっているかを予測しています.printで自分で設定したスコアが出てくるはずです.8とか2とか.

これを基準に私は判断していました.

終わりに

申し訳ありませんが,動作を全て確認したわけではありません.
半年前くらいは動いてたのですが・・・.

もし動かないことがあれば教えてください.編集します.

向上するところとしては学習データにMACDとかRSIとかを私は入れていました.あとはMAとかの過去データも学習させていました.

基礎的なpythonの知識がある前提で話してしまっているところがいくつかあると思います.その部分は申し訳ございません.

df_out =df_out.sample(frac=0.3)

こんな感じで学習データを10年分くらいにしてそのサンプルから30%をランダムにとったりしていました.

まとめ

これを極めれば,テクニカルだけでできるのかなあと思いながら.
作るのはとても楽しかったです.いろいろなサイトを参考にしました.
ここで感謝を申し上げます.

ここから先は

0字

¥ 500

よろしければサポートお願いします! いただいたサポートはクリエイターとしての活動費に使わせていただきます!