Rの quantmod パッケージを利用することにより、株価データを取り扱えるようになりました [1]。たくさんのデータを利用できるようになったので、機械学習でなにかを予測しようと考えました。今回も昨年末 (2018-12-19) に東証一部へ再上場したソフトバンク株式会社 (9434.T) を例に取り上げました。
関数関係を考える
\(9434.T\) の株価を、ある要因 \(x_{1}, x_{2}, ...\) を使って \(f\) という処理をして予測する、すなわち、
\(9434.T = f(x_{1}, x_{2}, ...)\)というような関数関係を設定します。
株の値動きはそんなに単純ではないと、トレーダーからお叱りを受けそうですが、機械学習による予測はどんなものかを知りたかったので、問題を単純化して次のようにしてみました。
当日の株関連情報を用いて、翌日(次回)のソフトバンク株式会社 (9434.T) の始値を予測せよ。
当日の株関連情報は、予測対象のソフトバンクの情報に加えて、ソフトバンク・グループ企業と同業企業の情報および日経平均を加ました。
企業 | コード | ソース | 情報 |
---|---|---|---|
ソフトバンク | 9434.T | yahooj | Open, High, Low, Close, Volume, Adjusted |
ヤフー | 4689.T | ||
ソフトバンク・テクノロジー | 4726.T | ||
楽天 | 4755.T | ||
KDDI | 9433.T | ||
NTT ドコモ | 9437.T | ||
ソフトバンク・グループ | 9984.T | ||
日経平均 | NIKKEI225 | FRED | - |
R のコードを下記に示しました。機械学習には Random Forest (rf) を使用しています。
library(quantmod) library(caret) library(doParallel) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GLOBAL SETTINGS options("getSymbols.warning4.0" = FALSE) # selection for modeling method #model.method <- "pls" model.method <- "rf" #model.method <- "xgbLinear" #model.method <- "xgbTree" if (!exists("model.method")) { model.method <- "rf" } # prediction table object for store predTblFile <- paste(Sys.getenv("HOME"), "preidction.RDS", sep = "/") # for multi-threading cl <- makePSOCKcluster(detectCores()) registerDoParallel(cl) # ----------------------------------------------------------------------------- # Function: get.stock # get table of certain stock data # # code: stock name/code # start: start date # source: data source, default = 'yahooj' # # return # data frame for specified stock data including Date column # ----------------------------------------------------------------------------- get.stock <- function(code, start, source = "yahooj") { tbl <- getSymbols(code, src = source, from = start, auto.assign = FALSE) tmp <- data.frame(tbl) tmp$Date <- rownames(tmp) return(tmp) } # ----------------------------------------------------------------------------- # Function: train.model # train model with specified ML method # # df: data frame for training including Y column # name.method: ML method # # return # trained model # ----------------------------------------------------------------------------- train.model <- function(df, name.method) { gc() set.seed(0) model <- train( Y ~ ., data = df, method = name.method, tuneLength = 10, preProcess = c('center', 'scale'), trControl = trainControl(method = "cv") ) return(model) } # ============================================================================= # MAIN # ============================================================================= date.start <- "2018-12-19" code.target <- "9434.T"; # SoftBank col.target <- "YJ9434.T.Open" tbl <- get.stock(code.target, date.start) list.code <- NULL # Yahoo Japan list.code <- append(list.code, "4689.T") # SBT, SoftBank Technology list.code <- append(list.code, "4726.T") # Rakuten list.code <- append(list.code, "4755.T") # KDDI list.code <- append(list.code, "9433.T") # docomo list.code <- append(list.code, "9437.T") # SBG, SoftBank group list.code <- append(list.code, "9984.T") # get stock data for (code in list.code) { tbl <- merge(tbl, get.stock(code, date.start)) } # NIKKEI225 tbl <- merge(tbl, get.stock("NIKKEI225", date.start, "FRED")) # add data and Y data tbl$Date <- as.Date(tbl$Date) y <- tbl[, col.target] y <- y[2:length(y)]; # shifting for using training y <- append(y, NA) tbl$Y <- y # start model training model <- train.model(tbl[1:(nrow(tbl) - 1),], model.method) model # update prediction table date.last <- tbl[nrow(tbl), "Date"] open.next <- predict(model, tbl[nrow(tbl),]); # prediction for next opening tmp <- data.frame(Date = date.last, Open.Next = open.next, Method = model.method) rownames(tmp) <- NULL tmp$Method <- as.character(tmp$Method) if (!file.exists(predTblFile)) { predTbl <- tmp } else { predTbl <- readRDS(predTblFile) if (date.last %in% predTbl$Date) { predTbl[predTbl$Date == date.last, "Open.Next"] <- tmp[1, "Open.Next"] predTbl[predTbl$Date == date.last, "Method"] <- tmp[1, "Method"] } else { predTbl <- rbind(predTbl, tmp[1, ]) } } predTbl saveRDS(predTbl, file = predTblFile) # prediction performance tbl2 <- data.frame(Date = tbl$Date, Act = tbl[, col.target], Pred = NA) for (d in predTbl$Date) { if (d %in% tbl2$Date) { r <- as.integer(rownames(tbl2[tbl2$Date == d,])) + 1 p <- predTbl[predTbl$Date == d, "Open.Next"] tbl2[r, "Pred"] <- p } } tbl2$Date <- as.character(tbl2$Date) xData <-as.integer(rownames(tbl2)) plot(xData, tbl2$Act, main = "Prediction", xlab = NA, xaxt = "n", ylab = col.target, las = 1, type = "n") axis(side = 1, at = xData, label = tbl2$Date, las = 2) grid(NULL, NULL, lty = 2, col = "darkgray") abline(v = xData[length(xData) - 1], lty = 1, col = "darkgray") points(xData, tbl2$Act, type = "b", col = "blue", pch = 21) points(xData, tbl2$Pred, type = "b", col = "red", pch = 19) legend("bottom", bty="n", ncol = 2, legend = c("Actual", "Prediction"), pch = c(21, 19), lty = c(1, 1), col = c("blue", "red") ) # --- # PROGRAM END
トレーニング用のデータフレームでは Y の列に、翌日の YJ9434.T.Open の値を一日前までの行にずらして設定しました。つまり毎回 2018-12-19 から前日までのデータをトレーニング用データにして学習させ、そのモデルで当日の Y 値(翌日の始値)を予測しています。したがって、日が経てばそれだけトレーニング用データが増えていきます。
R version 3.5.2 (2018-12-20) -- "Eggshell Igloo" Copyright (C) 2018 The R Foundation for Statistical Computing Platform: x86_64-redhat-linux-gnu (64-bit) R は、自由なソフトウェアであり、「完全に無保証」です。 ... (途中省略) ... > # start model training > model <- train.model(tbl[1:(nrow(tbl) - 1),], model.method) > model Random Forest 26 samples 44 predictors Pre-processing: centered (44), scaled (44) Resampling: Cross-Validated (10 fold) Summary of sample sizes: 23, 23, 23, 24, 23, 24, ... Resampling results across tuning parameters: mtry RMSE Rsquared MAE 2 35.92638 0.8852441 26.70252 6 30.32361 0.8886168 22.45102 11 27.67208 0.8935603 20.18447 16 25.91685 0.8933522 19.27206 20 25.73431 0.8920262 19.11300 25 25.14702 0.9064315 18.88401 30 25.65043 0.8912953 18.91954 34 25.31299 0.9627597 18.95446 39 25.13421 0.8906889 18.39020 44 25.32338 0.9203712 18.77740 RMSE was used to select the optimal model using the smallest value. The final value used for the model was mtry = 39. > > # update prediction table > date.last <- tbl[nrow(tbl), "Date"] ... (途中省略) ... > > predTbl Date Open.Next Method 1 2019-01-21 1420.734 rf 2 2019-01-22 1426.954 rf 3 2019-01-23 1424.429 rf 4 2019-01-24 1420.733 rf 5 2019-01-25 1416.661 rf 6 2019-01-28 1419.768 rf 7 2019-01-29 1416.853 rf 8 2019-01-30 1379.564 rf 9 2019-01-31 1353.043 rf 10 2019-02-01 1339.745 rf > saveRDS(predTbl, file = predTblFile) > ... (途中省略) ... > # --- > # PROGRAM END >
毎日プログラムを実行して予測された値は、データフレーム predTbl で予測日の行の Open.Next 列に格納されて保存されます。ちなみに、データソースの FRED の更新タイミングが一番遅くて、正確に計測がしていませんが、その日の午後10時頃になります。
プロットでは翌日分の予測値をずらして赤点で表示して、青点の実績値との予実が見れるようにしました。
まとめ
まだデータ数が少なく、また、要因としての入力情報もざっくり選んでいるので、値動きがあるときに適切に予測できるのかはまだ判りません。モデルのトレーニングを実施して平均二乗誤差 RMSE が 25 程度ということは、予実プロットでもっともらしい結果が出ていても±25円程度の予測精度しかないということです。データが増えるにつれて、RMSE の値がじわりと下がってきているので、どの程度まで RMSE が小さくなるかを監視し、また予測精度向上に寄与する他の要因を加えられないかの検討を続けます。
参考 サイト
関連しそうな書籍を Amazon.co.jp からピックアップしましたが、現時点ではまだ金融データを扱うために特定の参考書を読んではいません。まずは自分が思うようにデータを処理してみて、しかる後に世の中の専門家が考えていることを付き合わせてみたいと思っています。
にほんブログ村
0 件のコメント:
コメントを投稿