機械学習で株価分析 14.解析結果の分析

# はじめに
本記事では、**実際に機械学習を試行する方法**を紹介します。なお、本記事は過去の記事 [13.機械学習の試行](https://qiita.com/ryo2_9393/items/cdaabd70803fa535b30e) の続きですので、未読の方はまずこちらをご参照ください。

本記事では、機械学習による予測から、以下の2つの値を算出しています。
* 予測を実行した全日数に対する株価高騰フラグが立った日数の割合
* 予測の適合率（予測が行われた日数に対する予測が的中した日数の割合）


# 概要
機械学習の予測結果に対し、以下のプログラムを実行することで、結果を分析できます。予測の期待値が1以上の場合、トレード日数がなるべく多くなるようロジックを組む必要があります。また、そもそもの予測の適合率を上げることも重要です。そのため、今回は上述の2点を分析内容として選出しています。

```python
# 予測結果の二値化関数
def func_binary(x):
  if  x > 0.5:
    return  1
  else:
    return  0

# 予測結果の比較関数
def func_result(data):
  # 株価高騰と予測 and 予測的中
  if   data["Pred Binary"] != 0 and data["Act"] == data["Pred Binary"] :
    return  1
  # 株価高騰と予測 and 予測失敗
  elif data["Pred Binary"] != 0 and data["Act"] != data["Pred Binary"] :
    return -1
  # 株価高騰なしと予測
  else:
    return  0

# 予測値を正解値と比較できる値に変換
df_result = pd.DataFrame()
df_result["Act"]  = y_test
df_result["Pred"] = y_pred
df_result['Pred Binary'] = df_result['Pred'].apply(func_binary)

# 予測結果を算出
df_result["Result"] = df_result.apply(func_result, axis =1)

print("-----------------------------------------------------------------------")
# トレード日の割合を算出
print("trade day = ", (df_result["Result"] !=  0).sum())
print("total day = ",  df_result["Result"].shape[0])
print("trade day ratio = ", (df_result["Result"] !=  0).sum() / df_result["Result"].shape[0])
print("-----------------------------------------------------------------------")
# 適合率を算出
print("collect day = ", (df_result["Result"] ==  1).sum())
print("   fail day = ", (df_result["Result"] == -1).sum())
print("  Precision = ", (df_result["Result"] ==  1).sum() / (df_result["Result"] !=  0).sum())
print("-----------------------------------------------------------------------")
```


# ソースコード全文
以下にソースコード全文を示します。

```python
!pip install yfinance

# 必要なライブラリをインポート
import yfinance as yf
yf.pdr_override()
import numpy  as np
import pandas as pd
from   pandas_datareader       import data
import datetime
from   IPython.display         import display
from   sklearn.model_selection import train_test_split
from   sklearn.preprocessing   import StandardScaler
import lightgbm as lgb

# 株価データの取得 ----------------------------------------------------------

# データの取得開始日・取得終了日を設定
day_start = datetime.date(2021,1,1)
day_end   = datetime.date.today() + datetime.timedelta(days=1)

# 銘柄名を変数に格納
stock_name  = ["4689.T"]

# 株価データを読み込み
df_stock = data.DataReader(stock_name, day_start, day_end, interval="1d")

# 取得する経済指標を設定
fred_name = ["^N225"]

# 経済指標データ取得
df_fred = data.DataReader(fred_name, day_start, day_end, interval="1d")

# 経済指標データの列名を編集
for i in range(len(fred_name)):
  df_fred = df_fred.add_prefix(fred_name[i] + " ")

# 株価データに経済指標データを統合
df_merge = pd.concat([df_stock, df_fred], axis=1)

# データのブランクを穴埋め
df_merge = df_merge.fillna(method='ffill')
df_merge = df_merge.fillna(0)


# 機械学習の試行 ----------------------------------------------------------

# 説明変数に株価に関する情報を追加
df_merge['Body']        = df_merge['Open']  - df_merge['Close']
df_merge['Body Ratio']  = df_merge['Body']  / df_merge['Close']
df_merge['Range']       = df_merge['High']  - df_merge['Low']
df_merge['Range Ratio'] = df_merge['Range'] / df_merge['Close']
df_merge['Gap']         = df_merge['Open']  - df_merge.shift(1)['Close']
df_merge['Gap Ratio']   = df_merge['Gap']   / df_merge.shift(1)['Close']
df_merge["Return"]      = df_merge.pct_change()["Adj Close"]

# 説明変数に各種移動平均と移動平均乖離率を追加
for dd in [5,10,15,20,25,30]:
  df_merge["Close " + str(dd) + "days Average"]  = df_merge["Close"].rolling(dd).mean()
  df_merge["Close " + str(dd) + "days Diverge"]  = (df_merge["Close"] - df_merge["Close " + str(dd) + "days Average"]) / df_merge["Close " + str(dd) + "days Average"]
  df_merge["Volume " + str(dd) + "days Average"] = df_merge["Volume"].rolling(dd).mean()
  df_merge["Volume " + str(dd) + "days Diverge"] = (df_merge["Volume"] - df_merge["Volume " + str(dd) + "days Average"]) / df_merge["Volume " + str(dd) + "days Average"]

# 株価高騰判定関数
rate_rise =  0.01
def func_flg_rise(data):
  if data["Return 1d_After"] > rate_rise :
    return  1
  else:
    return  0

# 目的変数作成のために翌日の株価の変動率データを追加
df_merge["Return 1d_After"] = df_merge.shift(-1)["Return"]

# 目的変数として翌日の株価の変動率を二値化
df_merge["flg_rise"] = df_merge.apply(func_flg_rise, axis=1)

# 説明変数から未来指標を削除
df_merge = df_merge.drop(["Return 1d_After"], axis=1)

# トレーニングデータとテストデータに分割
df_train, df_test = train_test_split(df_merge, test_size=0.2, shuffle=False)

# 説明変数と目的変数に分割
X_train = df_train.drop(["flg_rise"], axis=1)
X_test  = df_test.drop(["flg_rise"], axis=1)
y_train = df_train["flg_rise"]
y_test  = df_test["flg_rise"]

# 説明変数を標準化
std_scaler = StandardScaler()
std_scaler.fit(X_train)
X_train = pd.DataFrame(std_scaler.transform(X_train), columns=X_train.columns)
std_scaler.fit(X_test)
X_test  = pd.DataFrame(std_scaler.transform(X_test), columns=X_test.columns)

# 説明変数をnumpyに変換
X_train = np.array(X_train, dtype = 'float64')
X_test  = np.array(X_test,  dtype = 'float64')

# 機械学習（LightGBM）のパラメータ設定
params = {
            'objective'    : 'binary',
            'metric'       : 'auc'   ,
            'boosting_type': 'gbdt'  ,
        }

# 機械学習（LightGBM）の試行
lgb_train = lgb.Dataset(X_train, y_train)
lgb.test  = lgb.Dataset(X_test, y_test)
gbm       = lgb.train(params, lgb_train)
y_pred    = gbm.predict(X_test)


# 予測結果の可視化 ----------------------------------------------------------

# 予測結果の二値化関数
def func_binary(x):
  if  x > 0.5:
    return  1
  else:
    return  0

# 予測結果の比較関数
def func_result(data):
  # 株価高騰と予測 and 予測的中
  if   data["Pred Binary"] != 0 and data["Act"] == data["Pred Binary"] :
    return  1
  # 株価高騰と予測 and 予測失敗
  elif data["Pred Binary"] != 0 and data["Act"] != data["Pred Binary"] :
    return -1
  # 株価高騰なしと予測
  else:
    return  0

# 予測値を正解値と比較できる値に変換
df_result = pd.DataFrame()
df_result["Act"]  = y_test
df_result["Pred"] = y_pred
df_result['Pred Binary'] = df_result['Pred'].apply(func_binary)

# 予測結果を算出
df_result["Result"] = df_result.apply(func_result, axis =1)

print("-----------------------------------------------------------------------")
# トレード日の割合を算出
print("trade day = ", (df_result["Result"] !=  0).sum())
print("total day = ",  df_result["Result"].shape[0])
print("trade day ratio = ", (df_result["Result"] !=  0).sum() / df_result["Result"].shape[0])
print("-----------------------------------------------------------------------")
# 適合率を算出
print("collect day = ", (df_result["Result"] ==  1).sum())
print("   fail day = ", (df_result["Result"] == -1).sum())
print("  Precision = ", (df_result["Result"] ==  1).sum() / (df_result["Result"] !=  0).sum())
print("-----------------------------------------------------------------------")
```

プログラムを実行し、分析結果が表示されれば成功です。

# おわりに
以上、ここまでで機械学習による株価予測の基礎をご紹介しました。なるべく簡潔かつアップデートが容易となるよう心がけたつもりです。この先は、単純に考えれば適合率が0.5より大きくなれば期待値が1以上のトレードを実行できます。なお、ご紹介したアルゴリズムには多分に改良の余地を残していますので、良案あればコメントいただければ幸いです。

# 目次へのリンク

https://qiita.com/ryo2_9393/items/2b1ada62c2537c901b6b