LoginSignup
30
46

AIを使った株価予想をPythonのscikit-learnライブラリRandam Forestで試してみた

Last updated at Posted at 2020-04-05

概要

Pythonの機械学習のライブラリscikit-learnを使って任意の銘柄の明日の株価を予想する。scikit-learnの中のRandam Forestを使う。Randam Forestは小さな独立した決定木を複数作成して学習させ、それぞれの決定木から得られた解の多数決によって解を導き出すアルゴリズムである。
これを株価予想に使い、予想する銘柄と関係のある複数の銘柄で過去の株価の動きより決定木を作成し、その多数決で目的の銘柄の株価を予想する。
16306.jpg
木々(決定木)の色(解)はそれぞれ違う(Randam)かもしれないが、森(Forest)として見れば、色が見えてくる。Randam forestになる訳です。

目標

  • 日本の任意の銘柄の明日の株価を予想する
  • Pythonのscikit-learnのRandam Forestを使う

株価データの準備

過去の株価のデータを集めて、扱いやすいようにデーターベースに格納する。過去3年分の日本の全株価のデータをダウンロードして終値をデータベースに格納する。データベースは、ローカルデータベースのSQLiteを使う。Pythonには、SQLiteを扱うライブラリも揃っており扱い易い。下記のようなSQLiteのテーブルを作成して、過去の株価を格納する。

scode 証券コード 文字列 key1
tday 日付 DATE key2
val 終値 FLOAT
検索速度を得るために、証券コード(scode)と日付(tday)にインディクスを張る。

株価データは、例えば下記のサイトよりダウンロードする。
http://souba-data.com/

ダウンロードした株価のZIPファイルを解凍して、CSVファイルの中から証券コード、終値を読込んでSQLiteに保存するサンプルコードを記載する。

data_dl()
def data_dl():

   conn = sqlite3.connect("chart.db");
   c = conn.cursor()
   d = datetime.datetime.today();
   for i in range(0,365):
      d = datetime.datetime.today() - datetime.timedelta(days=i);
      zip_path = "data\\T%s.zip" % (d.strftime("%y%m%d"));
      if not zipfile.is_zipfile(zip_path):
         continue;
      zf = zipfile.ZipFile(zip_path, 'r')
      zf.extractall(path="data\\");
           
      csv_path = "data\\T%s.csv" % (d.strftime("%y%m%d"));
      f = open(csv_path, "r");
      csv_reader = csv.reader(f); 
      
      td = d.strftime("%Y-%m-%d");
      query = "delete from chrt where tday='"+td+"'";
      c.execute(query);
      
      for row in csv_reader:
         if (row[1]=="")or(row[7]=="")or(row[2]!="11") :
            continue;
         query = "insert into chrt (scode,tday,val) values('"+row[1]+"','"+td+"',"+row[7]+")";
         c.execute(query);

      f.close();      
      print csv_path;

   conn.commit();
   conn.close();

   return;

Randam Forestを使った株価予想の実践

ターゲットとする株価として、ソニー(株)[T-6758]を予想する事とする。特に深い理由はない。一般的で名の通った代表株としてサンプルとして取り上げた。木となる指数としては、為替相場、消費者物価指数なども適当なのかもしれないが、データベースに過去の株価データを揃えたので、株価データの中で指数となりそうなETFなどのデータを使う事とした。


1309 	 上海上証50
1313 	 KODEX200
1314 	 日本新興株
1322 	 上場パンダ
1326 	 SPDRゴールド
1343 	 NF東REIT
1543 	 パラジウム信託
1548 	 上場中国H株
1551 	 JTOP20投信
1633 	 NEXT不動産
1671 	 WTI原油投信
1673 	 ETFS銀上場
1678 	 NFインド
1681 	 新興国株式
1682 	 東工取白金投信
1693 	 銅上場投信
1698 	 東証配当投信
1699 	 原油イン投信

これら各のETFの過去の株価とターゲットの株価の相関関係を学習させる。この時、前日から株価が増加した場合は+1、減少した場合は-1として正規化する。さらに注意点として、前日のETFの値から、本日のターゲットの株価を予想する必要があるので、ETFの株価とターゲットの株価は、一日ずらす必要がある。このようにして学習したRandam Forestデータ使って、前日のETFの値から本日のターゲットの株価を予想値を出す。
無題.jpg
ソースの一式を下記に載せる。

rnd_forest

import ConfigParser
import urllib
import sqlite3
import datetime
import os.path
import zipfile
import csv
from sklearn.ensemble import RandomForestClassifier

lscode=[];
ltday=[];

def learn_db_init():

   conn = sqlite3.connect("chart.db");
   c = conn.cursor();
   query = "select scode from chrt group by scode order by scode";
   c.execute(query)
   for row in c:
      lscode.append(row[0]);
   query = "select tday from chrt group by tday order by tday"; 
   c.execute(query)
   for row in c:
      ltday.append(row[0]);
   conn.close();

def rnd_forest(ltree_scode,tscode,tdate):

   conn = sqlite3.connect("chart.db");
   c = conn.cursor();

   ltra_x=[];    
   ltra_y=[];
   ltest_x=[];

   dp = -1;
   for i in range(0,len(ltday)):
      if ltday[i]==tdate:
         dp=i;
         break;
   if dp-102 < 0:
      return 0;

   whe="";
   for sc in ltree_scode:
      if whe!="":
         whe = whe + "or";
      whe = whe + "(scode='"+sc+"')";

   lr={};
   lbr={};
   for s in ltree_scode:
      lr[s]=-1;
      lbr[s]=-1;   
   for d in range(dp-102,dp,1):      
      query = "select scode,val from chrt where (tday='%s')and(%s) order by scode" % \
            (ltday[d],whe);
      c.execute(query);
      for row in c:
         lr[row[0]]=row[1];      
      
      if d > dp-102:      
         lx=[];
         for s in ltree_scode:
            if lr[s] < lbr[s]:
               lx.append(-1);
            else:
               lx.append(1);   
            lbr[s]=lr[s];
         if d==dp-1:
            ltest_x=lx;            
         else:
            ltra_x.append(lx); 

      for s in ltree_scode:
         if lr[s] > 0:
            lbr[s]=lr[s];
                        
   
   query = "select tday,val from chrt where (scode='%s')and(tday>='%s')and(tday<'%s') order by tday" % \
            (tscode,ltday[dp-101],ltday[dp]);
   c.execute(query)
   r=-1;
   for row in c:
      if r>0:
         if r &lt; row[1]:
            ltra_y.append(1);
         else:
            ltra_y.append(-1);
      r=row[1];

   conn.close();

   classifier = RandomForestClassifier(n_estimators=len(ltree_scode), random_state=0,max_depth=5);
   classifier.fit(ltra_x,ltra_y);
   ret = classifier.predict([ltest_x]);

   return ret;      
   print tdate,ret; 
#--------------------------------------------------------------------------------------------------
learn_db_init();
letf=["1309","1313","1314","1322","1326","1343","1543","1548","1551","1633","1671","1673","1678","1681","1682","1693","1698","1699"];
ans=rnd_forest(letf,'6758',"2017-03-07");  

ソースについての簡単な説明しておく。
learn_db_init()
でデータベースに格納されている証券コードのリストをlscode、日付のリストをltdayに保管する。
rnd_forest(ltree_scode,tscode,tdate) で株価予想を行う。ltree_scodeには、指数とするETFなどのコードをリストで入れる。tscodeには、ターゲットの証券コード、今回の場合ソニーの証券コード6758をセットしている。tdateには、予想する日付を入れる。

rnd_forest(ltree_scode,tscode,tdate) の関数の中でclassifierが出てくる最後の5行目までは、
データベースから、配列に過去の株価をセットする処理になる。
ltra_x: 各ETFの株価の上昇下降を1 or -1でtdateの2日前までセット
ltra_y: ターゲットの株価の上昇下降を1 or -1でtdateの1日前までセット
これを使って、classifier.fit(ltra_x,ltra_y);で学習させる。ltest_xには、tdateの1日前の各ETFの値を配列としてセット。これを使って、classifier.predict([ltest_x]);でtdate日のターゲットの株価を予想します。

テストの日付の株価は、1:上昇と実際の値と合致していた。しかし、日によっては外れる事もある。

予想結果の検証と考察

前章でソニー(株)[T-6758]の株価を予想する為に、目ぼしいETFを18個選択して、前日までのソニー(株)[T-6758]の株価と18個のETFの株価と相関関係を100日分を使って学習させ、本日のETFの株価を与えて、明日のターゲットの株価を予想した。2017/8/29-2018/3/16 までのソニー(株)[T-6758]の株価を予想した結果は下図の通り。青色はソニーの株価変動、紫は累積の的中率、緑色は間近20日間平均の的中率を示している。
無題a.jpg
累積的中率は43%。 低すぎる。これだったら、予想の逆を買った方が儲かる構図になる。考えられる理由として、

  • 選択した18個ETFは、適当に選んだ為、ソニーの株価の連動と関連性が低く適切でなかった可能性がある。いくつかは、関連性が高かったのかもしれないが、多数決で掻き消されてしまったのかもしれない。
  • 本日のETFの値を使って、明日のターゲットの株価を予想しており1日ずれている。 動きの早い現在の株式市場では1日前の株価の連動性はほとんどないのかもしれない。

Randam Forestの改良と成果

そこで、ソニーの株価と1日差を付けて、連動性の高い銘柄を探す事にする。的中率の高い有能な助言者のみを集めて多数決を行うイメージ。そこで、1銘柄だけでRandam Forestを計算し、先と同じように的中率を求めていく事にする。その結果で、的中率の高い関連性の高い上位10~20銘柄を使って、Randam Forestすれば、的中率も上がるのでは無いかと考えた。今回は、ETF銘柄に絞らずに一般的な銘柄も含めてすべてでチェックしてみる。関連会社と取引会社など想像もしなかった銘柄が見つかるかもしれないので。その結果が下表になる。
無題b.jpg
最も高かった銘柄でも、的中率は61%だった。大丈夫だろうか。上位に上がった銘柄を見てみると、やはり関係の無い銘柄が多く上がってきた。関連会社とかではなくて、株価の動きが似たような動きをする銘柄になっているのか。
この結果を元に関連性が高いと思われる上位20銘柄を使って、Randam Forestを掛けてみた。その結果が下図になる。
無題c.jpg
累積的中率が64%と大幅にアップした。20日の累積的中率に限っては最大で85%と驚異的となった。

最後に

最後まで読んで頂いてありがとうございます。本稿で述べたRandamForestをC言語で実装したソフトウェアについて下記の記事で紹介していますので、合わせてお読み下さい。
AI(機械学習)Randam Forestで株価を予想するWinodwsアプリケーションの開発

更には、上記のソフトウェアを強化した有償版を利用した株式投資法についても下記で紹介しています。 
G-Chartを用いた株式投資法の紹介

追記(2023年10月)

最初の投稿から日時が経過して投資環境も変化する中、本手法が現時点でも有効か検証した。今回は、C言語で開発したG-Chartを使ってRandam Forestを検証した。
gctotal4.jpg
記載の通り、20日平均で72.4%、100日平均で71.4%の的中率と、現時点においても有効に株価予想できある事が確認できた。

30
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
46