L'apprentissage automatique dans la négociation : théorie, modèles, pratique et algo-trading - page 213

 

Un autre exemple pour une simulation.

Nous construisons 20 000 modèles linéaires (1 000 observations partout, nombre de prédicteurs de 1 à 20 (1 000 modèles pour chaque nombre), plus une variable indépendante). Données i.i.d., N(0,1).

Le but de la simulation est de s'assurer que la statistique F ne dépasse pas la valeur critique lorsque la régression MNA est construite sur des données indépendantes (ne contenant pas de dépendances), satisfaisant ainsi aux exigences du modèle lin. Il peut donc être utilisé comme un indicateur de la formation du modèle.

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

Temps de fonctionnement du code : 1,35 minutes.

 

Code utile. Visualise les séquences de transactions en trois hypostases.

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


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

Fonctionne pendant environ 45 secondes. Le tirage au sort dure environ 1,5 minute.

 
Alexey Burnakov:


Magnifique, merci.
 
Alexey Burnakov:

Le but de la simulation est de s'assurer que la statistique F ne dépasse pas une valeur critique lorsque la régression MNA est construite sur des données indépendantes (ne contenant pas de dépendances), satisfaisant ainsi aux exigences d'un modèle linéaire. Il peut donc être utilisé comme un indicateur de la formation du modèle.

Je n'ai pas tout à fait compris ce qu'était fstatistic. Ici, les données sont aléatoires, mais le modèle a appris quelque chose, vous pouvez donc en conclure que le modèle est adapté et réentraîné. Ce qui signifie que l'évaluation du modèle doit être mauvaise. En d'autres termes, je m'attendais à un fstatistis négatif, ou à une autre indication que les choses vont mal sur le graphique.
Comment interpréter correctement le résultat de cet exemple ?
Je comprends que le premier prédicteur peut être considéré comme plus qualitatif que le premier + le second. Et 1+2 est mieux que 1+2+3. C'est vrai ? Est-il raisonnable de sélectionner par la génétique l'ensemble des prédicteurs qui donneront la fstatistique la plus élevée ?
 
Dr. Trader:
Je n'ai pas tout compris au sujet de la statistique F. Ici, les données sont aléatoires, mais le modèle a appris quelque chose, vous pouvez donc en conclure que le modèle est adapté et surentraîné. Ce qui signifie que l'évaluation du modèle doit être mauvaise. En d'autres termes, je m'attendais à un fstatistis négatif, ou à une autre indication que les choses vont mal sur le graphique.
Comment puis-je interpréter correctement le résultat de cet exemple ?
Je comprends que le premier prédicteur peut être considéré comme plus qualitatif que le premier + le second. Et 1+2 est mieux que 1+2+3. C'est vrai ? Est-il raisonnable de sélectionner par la génétique l'ensemble des prédicteurs qui donneront la fstatistique la plus élevée ?

Regardez le tableau de distribution F.http://www.socr.ucla.edu/applets.dir/f_table.html

La statistique F est une valeur qui dépend des degrés de liberté. Elle est toujours positive, car nous avons une distribution unilatérale.

Mais le modèle n'apprend rien, parce qu'un modèle entraîné doit avoir une statistique F élevée (supérieure ou égale à la critique à un alpha donné - comme cela se présente lorsqu'on teste l'hypothèse nulle).

Dans tous les cas, ne pas dépasser la valeur critique à alpha = 0,01, mais vous pourriez la fixer à 0,0001, par exemple.

Cela dit, je voulais m'assurer (je n'ai pas étudié cela à l'université) qu'en ajoutant des variables de bruit, le modèle linéaire ne montrerait pas une augmentation de l'apprentissage. Comme vous pouvez le voir...

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

Code utile. Visualise les séquences de transactions en trois hypostases.

En ce qui concerne le code ci-dessus. Veuillez écrire au moins de brefs commentaires dans le code. Surtout lorsque vous utilisez des expressions complexes. Tout le monde ne connaît pas et n'utilise pas le paquet "data.table", il n'est pas superflu d'expliquer ce que fait dcast.data.table, fondre ce que sont .N, .SD. Vous ne postez pas le code pour montrer à quel point vous le connaissez. À mon avis, le code publié devrait aider les autres utilisateurs (même avec un niveau de formation élémentaire) à comprendre le script.

C'est bien que R vous permette de programmer l'action de plusieurs façons, mais il est souhaitable que vous ne perdiez pas la lisibilité du code.

Quelques suggestions sur le code :

- Les variables intermédiaires x, x.cast, x.cum ne sont pas nécessaires aux calculs et ne font qu'occuper de la mémoire. Tous les calculs ne nécessitant pas la sauvegarde des résultats intermédiaires doivent de préférence être effectués par l'intermédiaire du tuyau

Par exemple

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

Bien sûr, il faut beaucoup de temps pour construire des graphiques.

Ce n'est pas une critique.

Bonne chance

 
Dr. Trader:
Je n'ai pas tout compris au sujet de la statistique F. Ici, les données sont aléatoires, mais le modèle a appris quelque chose, vous pouvez donc en conclure que le modèle est adapté et surentraîné. Ce qui signifie que l'évaluation du modèle doit être mauvaise. En d'autres termes, je m'attendais à un fstatistis négatif, ou à une autre indication que les choses vont mal sur le graphique.
Comment puis-je interpréter correctement le résultat de cet exemple ?
Je comprends que le premier prédicteur peut être considéré comme plus qualitatif que le premier + le second. Et 1+2 est mieux que 1+2+3. C'est vrai ? Est-il raisonnable de sélectionner par la génétique l'ensemble des prédicteurs qui donneront la fstatistique la plus élevée ?

Et voici un exemple où nous supposons que le modèle entièrement entraîné comprendra 20 variables, avec des poids croissants (1 variable - poids 1, la 20e variable - poids 20). Et voyons comment la distribution des statistiques F va changer après l'ajout successif de prédicteurs au modèle :

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

Graphique avec axe des y logarithmique :

Apparemment, oui...

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

Merci ! Je ne savais pas encore comment faire. Mais en réalité, vous devez garder les calculs à l'esprit autant que possible. Ce sera plus rapide. Bon kung fu...

Avec vos modifications :

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

La durée du film est de 47 secondes. Je veux dire, le code est plus joli et plus compact, mais il n'y a pas de différence de vitesse... Dessin, oui, très long. 1000 lignes avec transparence - grâce à elles...

 
Alexey Burnakov:

Merci ! Je ne savais pas encore comment faire. Mais en réalité, vous devez garder les calculs à l'esprit autant que possible. Ce sera plus rapide. Bon kung fu...

Durée : 47 secondes. Je veux dire, le code est plus joli et plus compact, mais il n'y a pas de différence de vitesse... Le dessin, oui, est très long. 1000 lignes avec transparence - grâce à elles...

Mon calcul prend

# temps d'exécution en secondes

# min lq moyenne médiane uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Mais ce n'est pas si important. Il s'agissait de la lisibilité du code.

Bonne chance

PS. Et paralléliser le calcul de lm(). C'est exactement le cas lorsque vous avez besoin

 
Vladimir Perervenko:

J'ai un calcul qui prend

#Temps d'exécution en secondes

# min lq moyenne médiane uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Mais ce n'est pas si important. Il s'agissait de la lisibilité du code.

Bonne chance

PS. Et paralléliser le calcul de lm(). C'est exactement le cas lorsque c'est nécessaire.

Non. Vous donnez le temps pour une partie du code avant les graphiques. Je l'ai indiqué, ainsi que les graphiques.

J'ai 1,5 seconde avant les graphiques. Votre méthode est de 1,15 seconde.

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

Il s'avère que votre méthode est plus rapide...