L'apprendimento automatico nel trading: teoria, modelli, pratica e algo-trading - pagina 213

 

Un altro esempio di simulazione.

Costruiamo 20.000 modelli lineari (1.000 osservazioni ovunque, numero di predittori da 1 a 20 (1.000 modelli per ogni numero), più una variabile indipendente). Dati i.i.d., N(0,1).

Lo scopo della simulazione è assicurarsi che la statistica F non superi il valore critico quando la regressione MNA è costruita su dati indipendenti (non contenenti dipendenze), soddisfacendo i requisiti del modello lin. Quindi, può essere usato come un indicatore della formazione del modello.

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

Tempo di esecuzione del codice: 1,35 minuti.

 

Codice utile. Visualizza le sequenze di transazioni in tre ipostasi.

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


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

Funziona per circa 45 secondi. Disegna per circa 1,5 minuti.

 
Alexey Burnakov:


Bellissimo, grazie.
 
Alexey Burnakov:

Lo scopo della simulazione è quello di garantire che la statistica F non superi un valore critico quando la regressione MNA è costruita su dati indipendenti (non contenenti dipendenze), soddisfacendo i requisiti di un modello lineare. Quindi, può essere usato come un indicatore della formazione del modello.

Non ho capito bene il concetto di fstatistic. I dati qui sono casuali, ma il modello ha imparato qualcosa, quindi si può concludere che il modello si adatta e viene riqualificato. Il che significa che la valutazione del modello deve essere cattiva. Cioè mi aspettavo un fstatistis negativo, o qualche altra indicazione che le cose vanno male sul grafico.
Come posso interpretare correttamente il risultato di questo esempio?
Capisco che il primo predittore può essere considerato più qualitativo del primo + il secondo. E 1+2 è meglio di 1+2+3. È così? È ragionevole selezionare per via genetica il set di predittori che darà la più alta fstatistica?
 
Dr.Trader:
Non ho capito bene la fstatistica. I dati qui sono casuali, ma il modello ha imparato qualcosa, quindi si può concludere che il modello è adatto e sovrallenato. Il che significa che la valutazione del modello deve essere cattiva. Cioè mi aspettavo un fstatistis negativo, o qualche altra indicazione che le cose vanno male sul grafico.
Come posso interpretare correttamente il risultato di questo esempio?
Capisco che il primo predittore può essere considerato più qualitativo del primo + il secondo. E 1+2 è meglio di 1+2+3. È così? È ragionevole selezionare per via genetica il set di predittori che darà la più alta fstatistica?

Guarda la tabella di distribuzione F.http://www.socr.ucla.edu/applets.dir/f_table.html

La statistica F è un valore che dipende dai gradi di libertà. È sempre positivo, perché abbiamo una distribuzione unilaterale.

Ma il modello non impara nulla, perché un modello addestrato deve avere una F-statistica alta (maggiore o uguale al critico ad una data alfa - come suona quando si testa l'ipotesi nulla).

In tutti i casi, non superando il valore critico ad alfa = 0,01, ma si potrebbe impostarlo a 0,0001, per esempio.

Detto questo, volevo essere sicuro (non ho studiato questo all'università) che aggiungendo variabili di rumore il modello lineare non avrebbe mostrato un aumento dell'apprendimento. Come potete vedere...

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

Codice utile. Visualizza le sequenze di transazioni in tre ipostasi.

Per quanto riguarda il codice di cui sopra. Si prega di scrivere almeno brevi commenti nel codice. Soprattutto quando si usano espressioni complesse. Non tutti conoscono e usano il pacchetto "data.table", non è superfluo spiegare cosa fa dcast.data.table, fondere cos'è .N, .SD. Non si pubblica il codice per mostrare quanto a fondo lo si conosce. Secondo me il codice pubblicato dovrebbe aiutare altri utenti (anche con un livello elementare di formazione) a capire lo script.

È fantastico che R permetta di programmare l'azione in diversi modi, ma è auspicabile che non si perda la leggibilità del codice.

Alcuni suggerimenti sul codice:

- le variabili intermedie x, x.cast, x.cum non sono necessarie nei calcoli e occupano solo memoria. Tutti i calcoli che non richiedono il salvataggio dei risultati intermedi dovrebbero essere eseguiti preferibilmente tramite pipe

Per esempio

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

Naturalmente, ci vuole molto tempo per costruire i grafici.

Non è una critica.

Buona fortuna

 
Dr.Trader:
Non ho capito bene la fstatistica. I dati qui sono casuali, ma il modello ha imparato qualcosa, quindi si può concludere che il modello è adatto e sovrallenato. Il che significa che la valutazione del modello deve essere cattiva. Cioè mi aspettavo un fstatistis negativo, o qualche altra indicazione che le cose vanno male sul grafico.
Come posso interpretare correttamente il risultato di questo esempio?
Capisco che il primo predittore può essere considerato più qualitativo del primo + il secondo. E 1+2 è meglio di 1+2+3. È così? È ragionevole selezionare per via genetica il set di predittori che darà la più alta fstatistica?

Ed ecco un esempio in cui assumiamo che il modello completamente addestrato includerà 20 variabili, con pesi crescenti (1 variabile - peso 1, la 20° variabile - peso 20). E vediamo come cambia la distribuzione delle statistiche F dopo l'aggiunta successiva di predittori al modello:

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

Grafico con asse y logaritmico:

A quanto pare, sì...

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

Grazie! Non sapevo ancora come farlo. Ma in realtà, si dovrebbe tenere conto dei calcoli il più possibile. Sarà più veloce. Buon kung fu...

Con le tue modifiche:

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

Il tempo di esecuzione è di 47 secondi. Voglio dire, il codice è più bello e più compatto, ma non c'è differenza di velocità... Disegno, sì, molto lungo. 1000 linee con trasparenza...

 
Alexey Burnakov:

Grazie! Non sapevo ancora come farlo. Ma in realtà, si dovrebbe tenere conto dei calcoli il più possibile. Sarà più veloce. Buon kung fu...

Tempo di esecuzione 47 secondi. Voglio dire, il codice è più bello e più compatto, ma non c'è differenza di velocità... Il disegno, sì, è molto lungo. 1000 linee con trasparenza...

Il mio calcolo prende

# tempo di esecuzione in secondi

# min lq media mediana uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Ma non è così importante. Si trattava di leggibilità del codice.

Buona fortuna

PS. E parallelizzare il calcolo lm(). Questo è esattamente il caso in cui avete bisogno di

 
Vladimir Perervenko:

Ho un calcolo che prende

#-tempo di esecuzione in secondi

# min lq media mediana uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Ma non è così importante. Si trattava di leggibilità del codice.

Buona fortuna

PS. E parallelizzare il calcolo lm(). Questo è esattamente il caso in cui è necessario.

No. Stai dando il tempo per una parte del codice prima dei grafici. L'ho indicato, insieme ai grafici.

Ho 1,5 secondi prima delle classifiche. Il vostro metodo è di 1,15 secondi.

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

Si scopre che il tuo metodo è più veloce...