Maschinelles Lernen im Handel: Theorie, Modelle, Praxis und Algo-Trading - Seite 213

 

Ein weiteres Beispiel für eine Simulation.

Wir erstellen 20.000 lineare Modelle (überall 1.000 Beobachtungen, Anzahl der Prädiktoren von 1 bis 20 (1.000 Modelle für jede Anzahl), plus eine unabhängige Variable). Daten i.i.d., N(0,1).

Mit der Simulation soll sichergestellt werden, dass die F-Statistik den kritischen Wert nicht überschreitet, wenn die MNA-Regression auf unabhängigen Daten (die keine Abhängigkeiten enthalten) aufgebaut ist und die Anforderungen des lin.model erfüllt. Sie kann also als Indikator für die Modellausbildung verwendet werden.

############### 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()

Laufzeit des Codes: 1,35 Minuten.

 

Nützlicher Code. Visualisiert Transaktionssequenzen in drei Hypostasen.

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


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()

Läuft etwa 45 Sekunden lang. Hat eine Laufzeit von etwa 1,5 Minuten.

 
Alexey Burnakov:


Wunderbar, danke.
 
Alexey Burnakov:

Mit der Simulation soll sichergestellt werden, dass die F-Statistik einen kritischen Wert nicht überschreitet, wenn die MNA-Regression auf unabhängigen Daten (die keine Abhängigkeiten enthalten) aufgebaut ist und die Anforderungen eines linearen Modells erfüllt. Sie kann also als Indikator für die Modellausbildung verwendet werden.

Ich habe nicht ganz verstanden, was fstatistic ist. Die Daten sind hier zufällig, aber das Modell ist auf etwas trainiert, so dass man daraus schließen kann, dass das Modell passt und übertrainiert ist. Das bedeutet, dass die Modellbewertung schlecht sein muss. D.h. ich habe eine negative fstatistis oder einen anderen Hinweis darauf erwartet, dass es in der Grafik schlecht aussieht.
Wie interpretiere ich das Ergebnis aus diesem Beispiel richtig?
Ich verstehe, dass der erste Prädiktor als qualitativer angesehen werden kann als der erste + zweite. Und 1+2 ist besser als 1+2+3. Ist das so? Ist es sinnvoll, die Prädiktoren mit Hilfe der Genetik auszuwählen, die die höchste fstatistic ergeben?
 
Dr. Trader:
Ich habe das mit der fstatistic nicht ganz verstanden. Die Daten sind hier zufällig, aber das Modell hat etwas gelernt, so dass man daraus schließen kann, dass das Modell passt und übertrainiert ist. Das bedeutet, dass die Modellbewertung schlecht sein muss. D.h. ich habe eine negative fstatistis oder einen anderen Hinweis darauf erwartet, dass es in der Grafik schlecht aussieht.
Wie interpretiere ich das Ergebnis aus diesem Beispiel richtig?
Ich verstehe, dass der erste Prädiktor als qualitativer angesehen werden kann als der erste + zweite. Und 1+2 ist besser als 1+2+3. Ist das so? Ist es sinnvoll, die Prädiktoren mit Hilfe der Genetik auszuwählen, die die höchste fstatistic ergeben?

Sehen Sie sich die F-Verteilungstabelle an.http://www.socr.ucla.edu/applets.dir/f_table.html

Die F-Statistik ist ein Wert, der von den Freiheitsgraden abhängt. Sie ist immer positiv, weil wir eine einseitige Verteilung haben.

Aber das Modell lernt nichts, denn ein trainiertes Modell muss eine hohe F-Statistik haben (größer oder gleich kritisch bei einem gegebenen Alpha - wie es beim Testen der Nullhypothese klingt).

In allen Fällen darf der kritische Wert bei alpha = 0,01 nicht überschritten werden, aber man könnte ihn z. B. auf 0,0001 setzen.

Allerdings wollte ich mich vergewissern (ich habe das nicht an der Universität studiert), dass das lineare Modell durch das Hinzufügen von Rauschvariablen keinen Anstieg der Lernleistung zeigen würde. Wie Sie sehen können...

F-Distribution Tables
  • Ivo Dinov: www.SOCR.ucla.edu
  • www.socr.ucla.edu
Statistics Online Computational Resource
 
Alexey Burnakov:

Nützlicher Code. Visualisiert Transaktionssequenzen in drei Hypostasen.

Bezüglich des obigen Codes. Bitte schreiben Sie zumindest kurze Kommentare in den Code. Vor allem, wenn Sie komplexe Ausdrücke verwenden. Nicht jeder kennt und benutzt das "data.table" Paket, es ist nicht überflüssig zu erklären, was dcast.data.table macht, was .N, .SD ist. Sie stellen den Code nicht ein, um zu zeigen, wie gut Sie sich damit auskennen. Meiner Meinung nach sollte der veröffentlichte Code anderen Nutzern (auch solchen mit Grundkenntnissen) helfen, das Skript zu verstehen.

Es ist großartig, dass R es Ihnen erlaubt, die Aktion auf verschiedene Arten zu programmieren, aber es ist wünschenswert, dass Sie die Lesbarkeit des Codes nicht verlieren.

Ein paar Vorschläge zum Code:

- Die Zwischenvariablen x, x.cast, x.cum werden in den Berechnungen nicht benötigt und nehmen nur Speicherplatz in Anspruch. Alle Berechnungen, die keine Speicherung von Zwischenergebnissen erfordern, sollten vorzugsweise über Rohrleitungen durchgeführt werden.

Zum Beispiel

#---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

Natürlich dauert es sehr lange, Diagramme zu erstellen.

Das ist keine Kritik.

Viel Glück!

 
Dr. Trader:
Ich habe das mit der fstatistic nicht ganz verstanden. Die Daten sind hier zufällig, aber das Modell hat etwas gelernt, so dass man daraus schließen kann, dass das Modell passt und übertrainiert ist. Das bedeutet, dass die Modellbewertung schlecht sein muss. D.h. ich habe eine negative fstatistis oder einen anderen Hinweis darauf erwartet, dass es in der Grafik schlecht aussieht.
Wie interpretiere ich das Ergebnis aus diesem Beispiel richtig?
Ich verstehe, dass der erste Prädiktor als qualitativer angesehen werden kann als der erste + zweite. Und 1+2 ist besser als 1+2+3. Ist das so? Ist es sinnvoll, die Prädiktoren mit Hilfe der Genetik auszuwählen, die die höchste fstatistic ergeben?

Und hier ein Beispiel, bei dem wir davon ausgehen, dass das vollständig trainierte Modell 20 Variablen mit steigender Gewichtung enthält (1 Variable - Gewichtung 1, die 20. Variable - Gewichtung 20). Und wir wollen sehen, wie sich die Verteilung der F-Statistiken nach dem sukzessiven Hinzufügen von Prädiktoren zum Modell verändert:

############### 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()

Diagramm mit logarithmischer y-Achse:

Anscheinend ja...

> 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

 
Vladimir Perervenko:

Ich danke Ihnen! Ich wusste noch nicht, wie man das macht. Aber eigentlich sollten Sie die Berechnungen so weit wie möglich im Hinterkopf behalten. Es wird schneller gehen. Gutes Kung Fu...

Mit Ihren Optimierungen:

#---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

Die Laufzeit beträgt 47 Sekunden. Ich meine, der Code ist hübscher und kompakter, aber es gibt keinen Unterschied in der Geschwindigkeit... Zeichnen, ja, sehr lang. 1000 Zeilen mit Transparenz - wegen ihnen...

 
Alexey Burnakov:

Ich danke Ihnen! Ich wusste noch nicht, wie man das macht. Aber eigentlich sollten Sie die Berechnungen so weit wie möglich im Hinterkopf behalten. Es wird schneller gehen. Gutes Kung Fu...

Die Laufzeit beträgt 47 Sekunden. Ich meine, der Code ist hübscher und kompakter, aber es gibt keinen Unterschied in der Geschwindigkeit... Das Zeichnen, ja, ist sehr lang. 1000 Zeilen mit Transparenz - wegen ihnen...

Meine Berechnung geht von

# Laufzeit in Sekunden

# min lq mittel median uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Aber das ist nicht so wichtig. Es ging um die Lesbarkeit des Codes.

Viel Glück!

PS. Und parallelisieren Sie die lm()-Berechnung. Dies ist genau der Fall, wenn Sie Folgendes benötigen

 
Vladimir Perervenko:

Ich habe eine Berechnung, bei der

#-Ausführungszeit in Sekunden

# min lq mittel median uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Aber das ist nicht so wichtig. Es ging um die Lesbarkeit des Codes.

Viel Glück!

PS. Und parallelisieren Sie die lm()-Berechnung. Das ist genau der Fall, wenn es notwendig ist.

Nö. Sie geben die Zeit für einen Teil des Codes vor den Diagrammen an. Ich habe sie zusammen mit den Diagrammen angegeben.

Ich habe 1,5 Sekunden vor den Karten. Ihre Methode beträgt 1,15 Sekunden.

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

Es stellt sich heraus, dass Ihre Methode schneller ist...