トレーディングにおける機械学習:理論、モデル、実践、アルゴトレーディング - ページ 213

 

また、シミュレーションの例です。

我々は、20,000の線形モデル(どこでも1,000のオブザベーション、1から20までの予測変数の数(各数に対して1,000のモデル)、および1つの独立変数)を構築します。データ i.i.d., N(0,1).

シミュレーションの目的は、MNA回帰を独立データ(依存関係を含まない)で構築したときに、F統計量が臨界値を超えないことを確認することであり、lin.modelの要件を満たすことです。そのため、モデル学習の指標として利用することができる。

############### simulate lm f-stats with random vars


rm(list=ls());gc()


library(data.table)

library(ggplot2)


start <- Sys.time()


set.seed(1)


x <- as.data.table(matrix(rnorm(21000000, 0, 1), ncol = 21))

x[, sampling:= sample(1000, nrow(x), replace = T)]


lm_models <- x[, 

 {

  lapply(c(1:20), function(x) summary(lm(data = .SD[, c(1:x, 21), with = F], formula = V21 ~ . -1))$'fstatistic'[[1]])

 }

 , by = sampling

]


lm_models_melted <- melt(lm_models, measure.vars = paste0('V', c(1:20)))


crtitical_f_stats <- qf(p = 0.99, df1 = c(1:20), df2 = 1000, lower.tail = TRUE, log.p = FALSE)


boxplot(data = lm_models_melted, value ~ variable); lines(crtitical_f_stats, type = 's', col = 'red')


Sys.time() - start


gc()

コードランニングタイム。1.35分

 

便利なコードです。3つのハイパーフェーズでトランザクションシーケンスを可視化します。

##########################


rm(list=ls());gc()


library(data.table)

library(ggplot2)

library(gridExtra)

library(tseries)


start <- Sys.time()


set.seed(1)


x <- as.data.table(matrix(rnorm(1000000, 0.1, 1), ncol = 1)) #random normal value with positive expectation

x[, variable:= rep(1:1000, times = 1000)]

x[, trade:= 1:.N, by = variable]


x.cast = dcast.data.table(x, variable ~ trade, value.var = 'V1', fun.aggregate = sum)

x_cum <- x.cast[, as.list(cumsum(unlist(.SD))), by = variable]


monte_trades <- melt(x_cum, measure.vars = names(x_cum)[-1], variable.name = "trade", value.name = 'V1')

setorder(monte_trades, variable, trade)

monte_trades_last <- as.data.table(monte_trades[trade == '1000', V1])

quantile_trade <- monte_trades[, quantile(V1, probs = 0.05), by = trade]

RF_last <- monte_trades[, V1[.N] / maxdrawdown(V1)[[1]], by = variable]



p1 <- ggplot(data = monte_trades, aes(x = trade, y = V1, group = variable)) +

geom_line(size = 2, color = 'blue', alpha = 0.01) + 

geom_line(data = quantile_trade, aes(x = trade, y = V1, group = 1), size = 2, alpha = 0.5, colour = 'blue') +

ggtitle('Simulated Trade Sequences of Length 1000')


p2 <- ggplot(data = monte_trades_last, aes(V1)) +

geom_density(alpha = 0.1, size = 1, color = 'blue', fill = 'blue') + 

scale_x_continuous(limits = c(min(monte_trades$V1), max(monte_trades$V1))) +

coord_flip() +

ggtitle('Cumulative Profit Density')


p3 <- ggplot(data = RF_last, aes(V1)) +

geom_density(alpha = 0.1, size = 1, color = 'blue', fill = 'blue') + 

geom_vline(xintercept = mean(RF_last$V1), colour = "blue", linetype = 2, size = 1) +

geom_vline(xintercept = median(RF_last$V1), colour = "red", linetype = 2, size = 1) +

ggtitle('Recovery Factor Density + Mean (blue) and Median (red)')


grid.arrange(p1, p2, p3, ncol = 3)


Sys.time() - start


gc()

約45秒間の動作。約1.5分間描画します。

 
アレクセイ・ブルナコフ


美しい、ありがとうございます。
 
アレクセイ・ブルナコフ

シミュレーションの目的は、MNA回帰が(依存関係を含まない)独立したデータで構築され、線形モデルの要件を満たす場合に、F統計量が臨界値を超えないことを確認することである。そのため、モデル学習の指標として利用することができる。

fstatisticについてよく理解できていなかった。ここのデータはランダムですが、モデルは何かに対してトレーニングされているので、モデルがフィットしてオーバートレーニングになっていると結論付けることができます。ということは、モデルの評価が悪いのでしょう。つまり、fstatistisがマイナスになるとか、グラフ上で状況が悪くなることを期待していたのです。
その例から、結果を正しく解釈するにはどうしたらよいでしょうか?
第一予測因子+第二予測因子よりも質的なものと考えてよいと理解しています。1+2+3より1+2の方がいいし。そうなんですか?最も高いfstatisticを与える予測変数の集合を遺伝学によって選択することは,妥当なことなのだろうか?
 
Dr.トレーダー
fstatisticについては、十分に理解していませんでした。このデータはランダムですが、モデルは何かを学習しているので、モデルはフィッティングしており、オーバートレーニングしていると結論付けることができます。ということは、モデルの評価が悪いのでしょう。つまり、fstatistisがマイナスになるとか、グラフ上で状況が悪くなることを期待していたのです。
その例から、結果を正しく解釈するにはどうしたらよいでしょうか?
第一予測因子+第二予測因子よりも質的なものと考えてよいと理解しています。1+2+3より1+2の方がいいし。そうなんですか?最も高いfstatisticを与える予測変数の集合を遺伝学によって選択することは,妥当なことなのだろうか?

F分布表を見てください。http://www.socr.ucla.edu/applets.dir/f_table.html

F 統計量は自由度に依存する値である。一方的な分布をしているので、常に正である。

しかし、モデルは何も学習しません。学習したモデルは高いF統計量(与えられたアルファ値で臨界値以上-帰無仮説を検定するときのように)を持たなければならないからです。

すべての場合において、α=0.01で臨界値を超えないが、例えば0.0001に設定することも可能である。

とはいえ、ノイズ変数を追加することで、線形モデルが学習量の増加を示さないことを確認したかったのです(大学では勉強していません)。ご覧の通り...

F-Distribution Tables
  • Ivo Dinov: www.SOCR.ucla.edu
  • www.socr.ucla.edu
Statistics Online Computational Resource
 
アレクセイ・ブルナコフ

便利なコードです。3つのハイパーフェーズでトランザクションシーケンスを可視化します。

上記のコードについて。コードに簡単なコメントくらいは書いてください。特に、複雑な表現を使う場合。誰もがdata.tableパッケージを知っていて使っているわけではないので、dcast.data.tableが何をするのか、.Nや.SDは何なのかを説明するのは余計なお世話です。コードを掲載しないのは、自分がどれだけ深い知識を持っているかを示すためです。私の考えでは、公開されたコードは、他のユーザー(たとえ初級レベルの訓練を受けている人であっても)がスクリプトを理解するのに役立つはずです。

Rでは、アクションを何通りにもプログラムできるのはいいのですが、コードの可読性を損なわないことが望まれます。

コードに関するいくつかの提案です。

- 中間変数 x, x.cast, x.cum は計算には必要なく、メモリを消費するだけです。中間結果を保存する必要のないすべての計算は、できればパイプを通して行うべきです。

例えば

#---variant-------------
rm(list=ls());gc()
library(data.table)
library(ggplot2)
library(gridExtra)
library(tseries)
#----
require(magrittr)
require(dplyr)
start <- Sys.time()
monte_trades <- as.data.table(matrix(rnorm(1000000, 0.1, 1), ncol = 1)) %>%
        .[, variable := rep(1:1000, times = 1000)]%>%
        .[, trade := 1:.N, by = variable] %>%
        dcast.data.table(., variable ~ trade, value.var = 'V1', fun.aggregate = sum)%>%
        .[, as.list(cumsum(unlist(.SD))), by = variable]%>%
        melt(., measure.vars = names(.)[-1], variable.name = "trade", value.name = 'V1')%>%
        setorder(., variable, trade)
monte_trades_last <- as.data.table(monte_trades[trade == '1000', V1])
quantile_trade <- monte_trades[, quantile(V1, probs = 0.05), by = trade]
RF_last <- monte_trades[, V1[.N] / maxdrawdown(V1)[[1]], by = variable]
Sys.time() - start
#Time difference of 2.247022 secs

もちろん、グラフの作成には非常に時間がかかります。

批判ではありません。

グッドラック

 
Dr.トレーダー
fstatisticについては、十分に理解していませんでした。このデータはランダムですが、モデルは何かを学習しているので、モデルはフィッティングしており、オーバートレーニングしていると結論づけることができます。ということは、モデルの評価が悪いのでしょう。つまり、fstatistisがマイナスになるとか、グラフ上で状況が悪くなることを期待していたのです。
その例から、結果を正しく解釈するにはどうしたらよいでしょうか?
第一予測因子+第二予測因子よりも質的なものと考えてよいと理解しています。1+2+3より1+2の方がいいし。そうなんですか?最も高いfstatisticを与える予測変数の集合を遺伝学によって選択することは,妥当なことなのだろうか?

そして、完全に学習されたモデルには20の変数が含まれると仮定し、重みを増やしていく例です(1変数 - 重み1、20番目の変数 - 重み20)。そして、モデルに予測変数が次々に追加された後、F統計量の分布がどのように変化するかを見てみましょう。

############### simulate lm f-stats with non-random vars


rm(list=ls());gc()


library(data.table)

library(ggplot2)


start <- Sys.time()


set.seed(1)


x <- as.data.table(matrix(rnorm(20000000, 0, 1), ncol = 20))

x[, (paste0('coef', c(1:20))):= lapply(1:20, function(x) rnorm(.N, x, 1))]

x[, output:= Reduce(`+`, Map(function(x, y) (x * y), .SD[, (1:20), with = FALSE], .SD[, (21:40), with = FALSE])), .SDcols = c(1:40)]

x[, sampling:= sample(1000, nrow(x), replace = T)]


lm_models <- x[, 

 {

  lapply(c(1:20), function(x) summary(lm(data = .SD[, c(1:x, 41), with = F], formula = output ~ . -1))$'fstatistic'[[1]])

 }

 , by = sampling

]


lm_models_melted <- melt(lm_models, measure.vars = paste0('V', c(1:20)))


crtitical_f_stats <- qf(p = 0.99, df1 = c(1:20), df2 = 1000, lower.tail = TRUE, log.p = FALSE)


boxplot(data = lm_models_melted, value ~ variable, log = 'y'); lines(crtitical_f_stats, type = 's', col = 'red')


summary(lm(data = x[sample(1000000, 1000, replace = T), c(1:20, 41), with = F], formula = output ~ . -1))


Sys.time() - start


gc()

Y軸を対数にしたグラフ。

どうやら、そうらしい...。

> summary(lm(data = x[sample(1000000, 1000, replace = T), c(1:20, 41), with = F], formula = output ~ . -1))


Call:

lm(formula = output ~ . - 1, data = x[sample(1e+06, 1000, replace = T), 

    c(1:20, 41), with = F])


Residuals:

     Min       1Q   Median       3Q      Max 

-19.6146  -2.8252   0.0192   3.0659  15.8853 


Coefficients:

    Estimate Std. Error t value Pr(>|t|)    

V1    0.9528     0.1427   6.676  4.1e-11 ***

V2    1.7771     0.1382  12.859  < 2e-16 ***

V3    2.7344     0.1442  18.968  < 2e-16 ***

V4    4.0195     0.1419  28.325  < 2e-16 ***

V5    5.2817     0.1479  35.718  < 2e-16 ***

V6    6.2776     0.1509  41.594  < 2e-16 ***

V7    6.9771     0.1446  48.242  < 2e-16 ***

V8    7.9722     0.1469  54.260  < 2e-16 ***

V9    9.0349     0.1462  61.806  < 2e-16 ***

V10  10.1372     0.1496  67.766  < 2e-16 ***

V11  10.8783     0.1487  73.134  < 2e-16 ***

V12  11.9129     0.1446  82.386  < 2e-16 ***

V13  12.8079     0.1462  87.588  < 2e-16 ***

V14  14.2017     0.1487  95.490  < 2e-16 ***

V15  14.9080     0.1458 102.252  < 2e-16 ***

V16  15.9893     0.1428 111.958  < 2e-16 ***

V17  17.4997     0.1403 124.716  < 2e-16 ***

V18  17.8798     0.1448 123.470  < 2e-16 ***

V19  18.9317     0.1470 128.823  < 2e-16 ***

V20  20.1143     0.1466 137.191  < 2e-16 ***

---

Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1


Residual standard error: 4.581 on 980 degrees of freedom

Multiple R-squared:  0.9932, Adjusted R-squared:  0.993 

F-statistic:  7123 on 20 and 980 DF,  p-value: < 2.2e-16

 
ウラジミール・ペレヴェンコ

ありがとうございました。まだやり方がわからなかったんです。でも本当は、できるだけ計算を頭に入れておいたほうがいいんです。もっと早くなりますよ。カンフーがうまい・・・。

あなたのツッコミで。

#---variant-------------

rm(list=ls());gc()

library(data.table)

library(ggplot2)

library(gridExtra)

library(tseries)

#----

require(magrittr)

require(dplyr)

start <- Sys.time()

monte_trades <- as.data.table(matrix(rnorm(1000000, 0.1, 1), ncol = 1)) %>%

.[, variable := rep(1:1000, times = 1000)]%>%

.[, trade := 1:.N, by = variable] %>%

dcast.data.table(., variable ~ trade, value.var = 'V1', fun.aggregate = sum)%>%

.[, as.list(cumsum(unlist(.SD))), by = variable]%>%

melt(., measure.vars = names(.)[-1], variable.name = "trade", value.name = 'V1')%>% 

setorder(., variable, trade)

monte_trades_last <- as.data.table(monte_trades[trade == '1000', V1])

quantile_trade <- monte_trades[, quantile(V1, probs = 0.05), by = trade]

RF_last <- monte_trades[, V1[.N] / maxdrawdown(V1)[[1]], by = variable]


p1 <- ggplot(data = monte_trades, aes(x = trade, y = V1, group = variable)) +

geom_line(size = 2, color = 'blue', alpha = 0.01) + 

geom_line(data = quantile_trade, aes(x = trade, y = V1, group = 1), size = 2, alpha = 0.5, colour = 'blue') +

ggtitle('Simulated Trade Sequences of Length 1000')


p2 <- ggplot(data = monte_trades_last, aes(V1)) +

geom_density(alpha = 0.1, size = 1, color = 'blue', fill = 'blue') + 

scale_x_continuous(limits = c(min(monte_trades$V1), max(monte_trades$V1))) +

coord_flip() +

ggtitle('Cumulative Profit Density')


p3 <- ggplot(data = RF_last, aes(V1)) +

geom_density(alpha = 0.1, size = 1, color = 'blue', fill = 'blue') + 

geom_vline(xintercept = mean(RF_last$V1), colour = "blue", linetype = 2, size = 1) +

geom_vline(xintercept = median(RF_last$V1), colour = "red", linetype = 2, size = 1) +

ggtitle('Recovery Factor Density + Mean (blue) and Median (red)')


grid.arrange(p1, p2, p3, ncol = 3)


Sys.time() - start

再生時間は47秒です。つまり、コードはよりきれいに、よりコンパクトになったが、スピードに違いはない...というわけだ。描画、そうですね、とても長いです。透明度の高い1000本の線 - 彼らのおかげで...

 
Alexey Burnakov:

ありがとうございました。まだやり方がわからなかったんです。でも本当は、できるだけ計算を頭に入れておいたほうがいいんです。もっと早くなりますよ。カンフーがうまい・・・。

上映時間47秒コードがきれいになってコンパクトになったのに、スピードに差がないって......。デッサン、そうですね、とても長いです。透明度の高い1000本の線 - 彼らのおかげで...

私の計算では

# ランタイム(秒

# 最小値 lq 平均値 中央値 uq 最大値 neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

しかし、それはそれほど重要なことではありません。コードの可読性について でした。

グッドラック

PS.そして、lm()の計算を並列化する。まさに、必要な時に必要な

 
Vladimir Perervenko:

を取る計算があります。

#-実行時間(秒)

# 最小値 lq 平均値 中央値 uq 最大値 neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

しかし、それはそれほど重要なことではありません。コードの可読性について でした。

グッドラック

PS.そして、lm()の計算を並列化する。まさに、必要なときに必要なのです。

いや、そうではない。グラフの前にコードの一部の時間を出していますね。グラフと一緒に示しました。

チャートの前に1.5秒あるんです。あなたの方法は1.15秒です。

rm(list=ls());gc()


library(data.table)

library(ggplot2)

library(gridExtra)

library(tseries)


start <- Sys.time()


set.seed(1)


x <- as.data.table(matrix(rnorm(1000000, 0.1, 1), ncol = 1)) #random normal value with positive expectation

x[, variable:= rep(1:1000, times = 1000)]

x[, trade:= 1:.N, by = variable]


x.cast = dcast.data.table(x, variable ~ trade, value.var = 'V1', fun.aggregate = sum)

x_cum <- x.cast[, as.list(cumsum(unlist(.SD))), by = variable]


monte_trades <- melt(x_cum, measure.vars = names(x_cum)[-1], variable.name = "trade", value.name = 'V1')

setorder(monte_trades, variable, trade)

monte_trades_last <- as.data.table(monte_trades[trade == '1000', V1])

quantile_trade <- monte_trades[, quantile(V1, probs = 0.05), by = trade]

RF_last <- monte_trades[, V1[.N] / maxdrawdown(V1)[[1]], by = variable]


Sys.time() - start

rm(list=ls());gc()

library(data.table)

library(ggplot2)

library(gridExtra)

library(tseries)

#----

require(magrittr)

require(dplyr)


start <- Sys.time()


monte_trades <- as.data.table(matrix(rnorm(1000000, 0.1, 1), ncol = 1)) %>%

.[, variable := rep(1:1000, times = 1000)]%>%

.[, trade := 1:.N, by = variable] %>%

dcast.data.table(., variable ~ trade, value.var = 'V1', fun.aggregate = sum)%>%

.[, as.list(cumsum(unlist(.SD))), by = variable]%>%

melt(., measure.vars = names(.)[-1], variable.name = "trade", value.name = 'V1')%>% 

setorder(., variable, trade)

monte_trades_last <- as.data.table(monte_trades[trade == '1000', V1])

quantile_trade <- monte_trades[, quantile(V1, probs = 0.05), by = trade]

RF_last <- monte_trades[, V1[.N] / maxdrawdown(V1)[[1]], by = variable]


Sys.time() - start

あなたの方法の方が速いことがわかった...。