Aprendizado de máquina no trading: teoria, prática, negociação e não só - página 213

 

Outro exemplo para uma simulação.

Construímos 20.000 modelos lineares (1.000 observações em toda parte, número de preditores de 1 a 20 (1.000 modelos para cada número), mais uma variável independente). Dados i.i.d., N(0,1).

O objetivo da simulação é garantir que a estatística F não exceda o valor crítico quando a regressão do MNA é construída sobre dados independentes (não contendo dependências), satisfazendo os requisitos do modelo lin.lin. Portanto, pode ser usado como um indicador do treinamento modelo.

############### 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 de funcionamento do código: 1,35 minutos.

 

Código útil. Visualiza sequências de transacções em três hipóstases.

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


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

Funciona durante cerca de 45 segundos. Arrasta durante cerca de 1,5 minutos.

 
Alexey Burnakov:


Lindo, obrigado.
 
Alexey Burnakov:

O objetivo da simulação é assegurar que a estatística F não exceda um valor crítico quando a regressão do MNA é construída sobre dados independentes (não contendo dependências), satisfazendo os requisitos de um modelo linear. Portanto, pode ser usado como um indicador do treinamento modelo.

Eu não percebi bem o que é fstatistic. Os dados aqui são aleatórios, mas o modelo é treinado para alguma coisa, então você pode concluir que o modelo é adequado e super-treinado. O que significa que a avaliação do modelo deve ser má. Ou seja, eu estava esperando uma fstatis negativa, ou alguma outra indicação de que as coisas estão ruins no gráfico.
Como interpretar corretamente o resultado desse exemplo?
Eu entendo que o primeiro preditor pode ser considerado mais qualitativo do que o primeiro + segundo. E 1+2 é melhor do que 1+2+3. Ai sim? É razoável selecionar por genética o conjunto de preditores que dará a maior fstatistic?
 
Dr. Trader:
Eu não entendi completamente sobre a fstatistic. Os dados aqui são aleatórios, mas o modelo aprendeu alguma coisa, então você pode concluir que o modelo é adequado e super-treinado. O que significa que a avaliação do modelo deve ser má. Ou seja, eu estava esperando uma fstatis negativa, ou alguma outra indicação de que as coisas estão ruins no gráfico.
Como interpretar corretamente o resultado desse exemplo?
Eu entendo que o primeiro preditor pode ser considerado mais qualitativo do que o primeiro + segundo. E 1+2 é melhor do que 1+2+3. Ai sim? É razoável selecionar por genética o conjunto de preditores que dará a maior fstatistic?

Olha para a tabela de distribuição F.http://www.socr.ucla.edu/applets.dir/f_table.html

A estatística F é um valor que depende dos graus de liberdade. É sempre positivo, porque temos uma distribuição unilateral.

Mas o modelo não aprende nada, porque um modelo treinado deve ter uma estatística F alta (maior ou igual à crítica em um determinado alfa - como soa quando se testa a hipótese nula).

Em todos os casos, não excedendo o valor crítico em alfa = 0,01, mas você poderia defini-lo como 0,0001, por exemplo.

Dito isto, eu queria ter certeza (não estudei isto na universidade) que adicionando variáveis de ruído o modelo linear não mostraria um aumento na aprendizagem. Como podem ver...

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

Código útil. Visualiza sequências de transacções em três hipóstases.

Em relação ao código acima. Por favor, escreva pelo menos breves comentários no código. Especialmente quando você usa expressões complexas. Nem todos conhecem e usam o pacote "data.table", não é supérfluo explicar o que o dcast.data.table faz, derreter o que é .N, .SD. Não se afixa o código para mostrar o quanto se sabe sobre isso. Na minha opinião, o código publicado deve ajudar outros usuários (mesmo com nível elementar de treinamento) a entender o script.

É ótimo que R permite que você programe a ação de várias maneiras, mas é desejável que você não perca a legibilidade do código.

Algumas sugestões sobre o código:

- As variáveis intermediárias x, x.cast, x.cum não são necessárias nos cálculos e só ocupam memória. Todos os cálculos que não exijam resultados intermédios de poupança devem ser efectuados preferencialmente através de tubos.

Por exemplo

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

Claro, demora muito tempo a construir gráficos.

Não é uma crítica.

Boa sorte.

 
Dr. Trader:
Eu não entendi completamente sobre a fstatistic. Os dados aqui são aleatórios, mas o modelo aprendeu alguma coisa, então você pode concluir que o modelo é adequado e super-treinado. O que significa que a avaliação do modelo deve ser má. Ou seja, eu estava esperando uma fstatis negativa, ou alguma outra indicação de que as coisas estão ruins no gráfico.
Como interpretar corretamente o resultado desse exemplo?
Eu entendo que o primeiro preditor pode ser considerado mais qualitativo do que o primeiro + segundo. E 1+2 é melhor do que 1+2+3. Ai sim? É razoável selecionar por genética o conjunto de preditores que dará a maior fstatistic?

E aqui está um exemplo onde assumimos que o modelo totalmente treinado incluirá 20 variáveis, com peso crescente (1 variável - peso 1, a 20 variável - peso 20). E vejamos como a distribuição das estatísticas F irá mudar após a adição sucessiva de preditores ao modelo:

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

Gráfico com eixo y logarítmico:

Aparentemente, sim...

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

Obrigado! Eu ainda não sabia como fazer isso. Mas na verdade, você deve manter os cálculos em mente o máximo possível. Vai ser mais rápido. Bom kung fu...

Com os teus afinações:

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

O tempo de execução é de 47 segundos. Então o código é mais bonito e compacto, mas não há diferença na velocidade... A desenhar, sim, muito tempo. 1000 linhas com transparência - por causa delas...

 
Alexey Burnakov:

Obrigado! Eu ainda não sabia como fazer isso. Mas na verdade, você deve manter os cálculos em mente o máximo possível. Vai ser mais rápido. Bom kung fu...

Tempo de execução: 47 segundos. Quero dizer, o código é mais bonito e compacto, mas não há diferença na velocidade... O desenho, sim, é muito longo. 1000 linhas com transparência - por causa delas...

O meu cálculo leva

# tempo de execução em segundos

# min lq média mediana uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Mas não é assim tão importante. Era sobre a legibilidade do código.

Boa sorte.

PS. E fazer o cálculo lm() em paralelo. Este é exatamente o caso quando você precisa

 
Vladimir Perervenko:

Eu tenho um cálculo que leva

#tempo de execução em segundos

# min lq média mediana uq max neval

# 2.027561 2.253354 2.254134 2.275785 2.300051 2.610649 100

Mas não é assim tão importante. Era sobre a legibilidade do código.

Boa sorte.

PS. E fazer o cálculo lm() em paralelo. Este é exactamente o caso quando é necessário.

Não. Você está dando tempo para parte do código antes dos gráficos. Eu já o indiquei, juntamente com os gráficos.

Eu tenho 1,5 segundos antes dos gráficos. O seu método é 1,15 segundos.

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

Acontece que o seu método é mais rápido...

Razão: