СОПРОВОЖДЕНИЕ ЭКСПЕРИМЕНТА ПО АНАЛИЗУ ДАННЫХ ФОРЕКСА: прелюдия

СОПРОВОЖДЕНИЕ ЭКСПЕРИМЕНТА ПО АНАЛИЗУ ДАННЫХ ФОРЕКСА: прелюдия

7 февраля 2016, 20:02
Alexey Burnakov
8
578

Начало здесь: https://www.mql5.com/ru/blogs/post/659572


Код на текущий момент:

#setwd('C:/R_study/fx/')
options(scipen=999)


### getting data
dat_eurusd <- read.table('C:/R_study/fx/EURUSD1.csv', header = T, sep = ',', dec = '.')
dat_audusd <- read.table('C:/R_study/fx/AUDUSD1.csv', header = T, sep = ',', dec = '.')
dat_gbpusd <- read.table('C:/R_study/fx/GBPUSD1.csv', header = T, sep = ',', dec = '.')
dat_usdcad <- read.table('C:/R_study/fx/USDCAD1.csv', header = T, sep = ',', dec = '.')
dat_usdchf <- read.table('C:/R_study/fx/USDCHF1.csv', header = T, sep = ',', dec = '.')


### selecting train
dat_train <- head(dat_audusd, nrow(dat_audusd) / 3 * 2)[, 3]
dat_train <- append(dat_train
                        , c(head(dat_eurusd, nrow(dat_eurusd) / 3 * 2)[, 3]
                        , head(dat_gbpusd, nrow(dat_gbpusd) / 3 * 2)[, 3]
                        , head(dat_usdcad, nrow(dat_usdcad) / 3 * 2)[, 3]
                        , head(dat_usdchf, nrow(dat_usdchf) / 3 * 2)[, 3]))


### selecting test
dat_test <- tail(dat_audusd, nrow(dat_audusd) / 3 * 1)[, 3]
dat_test <- append(dat_test
                    , c(tail(dat_eurusd, nrow(dat_eurusd) / 3 * 1)[, 3]
                        , tail(dat_gbpusd, nrow(dat_gbpusd) / 3 * 1)[, 3]
                        , tail(dat_usdcad, nrow(dat_usdcad) / 3 * 1)[, 3]
                        , tail(dat_usdchf, nrow(dat_usdchf) / 3 * 1)[, 3]))


write.table(dat_train, file = 'C:/R_study/fx/dat_train.csv', sep = ',', dec = '.', row.names = F)
write.table(dat_test, file = 'C:/R_study/fx/dat_test.csv', sep = ',', dec = '.', row.names = F)


### loading working data
dat_train <- read.csv('C:/R_study/fx/dat_train.csv'
                        , sep = ','
                        , dec = '.'
                        , colClasses = 'numeric')

dat_test <- read.csv('C:/R_study/fx/dat_test.csv'
                      , sep = ','
                      , dec = '.'
                      , colClasses = 'numeric')


################################## feature engineering

dat_train_working <- dat_train

max_lag_power <- 9.5

### train set

# difference

for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_diff', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), "x"] - 
                dat_train_working[(2 ^ max_lag_power + 1 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag), 'x']   # p - p
        
#       dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
#               log(dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), "x"]) - 
#               log(dat_train_working[(2 ^ max_lag_power + 1 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag), 'x'])   # log(p) - log(p)
#       
#       dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
#               dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), 'x'] / 
#               lag(dat_train_working[(2 ^ max_lag_power + 1 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag), 'x']) - 1   # p / p - 1
        
}

write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_differences.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

require(zoo)
# differences from mean
dat_train_working <- dat_train

start <- Sys.time()
for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_mean_diff', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), 'x'] -
                rollapply(dat_train_working$x, lag, mean)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)]

        
}
Sys.time() - start
write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_diffmean.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

# differences from max
dat_train_working <- dat_train
start <- Sys.time()
for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_max_diff', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), 'x'] -
                rollapply(dat_train_working$x, lag, max)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)]
        
        
}
Sys.time() - start
write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_diffmax.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

# differences from min
dat_train_working <- dat_train
start <- Sys.time()
for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_min_diff', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), 'x'] -
                rollapply(dat_train_working$x, lag, min)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)]
        
        
}
Sys.time() - start
write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_diffmin.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

# rolling sd
dat_train_working <- dat_train
start <- Sys.time()
for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_sd', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                rollapply(dat_train_working$x, lag, sd)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)]
        
        
}
Sys.time() - start
write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_sd.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

# rolling range
dat_train_working <- dat_train
start <- Sys.time()
for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('lag_range', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                rollapply(dat_train_working$x, lag, max)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)] -
                rollapply(dat_train_working$x, lag, min)[(2 ^ max_lag_power + 2 - lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power - lag + 1)]
        
        
}
Sys.time() - start
write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_range.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()

# output = future difference

dat_train_working <- dat_train

for (lags in seq(from = 1, to = max_lag_power, by = 0.5)){
        
        lag <- round(2 ^ lags)
        newcolname_lag <- paste('future_lag', lag, sep = '_')
        
        dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
                dat_train_working[(2 ^ max_lag_power + 1 + lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power + lag), 'x'] - 
                dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), "x"]
                   # p - p
        
        #       dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
        #               log(dat_train_working[(2 ^ max_lag_power + 1 + lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power + lag), 'x']) - 
        #               log(dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), "x"])    # log(p) - log(p)
        #                  
        #       
        #       dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), eval(newcolname_lag)] <- 
        #               dat_train_working[(2 ^ max_lag_power + 1 + lag) : (dim(dat_train_working)[1] - 2 ^ max_lag_power + lag), 'x'] / 
        #               dat_train_working[(2 ^ max_lag_power + 1) : (dim(dat_train_working)[1] - 2 ^ max_lag_power), 'x'] - 1   # p / p - 1
        #               
        
}

write.table(dat_train_working[, 2:19], file = 'C:/R_study/fx/dat_train_future_lag.csv', sep = ',', dec = '.', row.names = F)
rm(dat_train_working)
gc()


### create final training sample
set.seed(10)

start <- round(runif(1, min = 2 ^ max_lag_power, max = 1440))
nrows <- numeric()
nrows[1] <- start
counter <- 2
while (start <= (nrow(dat_train) - (2 ^ max_lag_power) * 2)){
        
        start <- start + round(2 ^ max_lag_power + runif(1, min = -50, max = 50))
        nrows[counter] <- start
        counter <- counter + 1
        
}


######################### unite inputs and outputs

dat_train_diff <- read.table('C:/R_study/fx/dat_train_differences.csv'
                                , header = T
                                , sep = ','
                                , dec = '.'
                                , colClasses = rep('numeric', 18)
                                , nrows = 19000000)

dat_train_final <- subset(dat_train_diff, rownames(dat_train_diff) %in% nrows)
rm(dat_train_diff)
gc()

dat_train_diff <- read.csv('C:/R_study/fx/dat_train_diffmean.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()

dat_train_diff <- read.csv('C:/R_study/fx/dat_train_diffmax.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()


dat_train_diff <- read.csv('C:/R_study/fx/dat_train_diffmin.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()


dat_train_diff <- read.csv('C:/R_study/fx/dat_train_sd.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()


dat_train_diff <- read.csv('C:/R_study/fx/dat_train_range.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()


dat_train_diff <- read.csv('C:/R_study/fx/dat_train_future_lag.csv'
                           , header = T
                           , sep = ','
                           , dec = '.'
                           , colClasses = rep('numeric', 18)
                           , nrows = 19000000)

dat_train_final <- cbind(dat_train_final, subset(dat_train_diff, rownames(dat_train_diff) %in% nrows))
rm(dat_train_diff)
gc()


#observe train set
str(dat_train_final)
summary(dat_train_final)
plot(dat_train_final$lag_diff_724, type = 'l')

nrow(subset(dat_train_final, dat_train_final$lag_diff_724 > 0.05 | dat_train_final$lag_diff_724 < -0.05))

dat_train_final <- subset(dat_train_final, dat_train_final$lag_diff_724 < 0.05 & dat_train_final$lag_diff_724 > -0.05)

write.table(dat_train_final, file = 'C:/R_study/fx/dat_train_final.csv', sep = ',', dec = '.', row.names = F)




### test set
#same logc applies here

 

Что я делаю в этой части кода:

Берем выгрузку минутной истории по 5 валютным парам из терминала MT4; я пользовался этим скриптом для облегчения работы - https://www.mql5.com/ru/code/11147. Спасибо автору!

Получаем файлы следующего формата:

 

 

 В третьем столбце идут цены открытия.

 Валютные пары, принимающие участие в тестировании видны в коде. Это мажоры. 

Далее, задумка в том, чтобы их соединить, неважно в какой последовательности, и сделать одну большую историю в 80 лет. В дальнейшем обучение правилам торговли будет идти на этой объединенной истории. Иными словами говоря, я хочу построить систему торговли, которая будет с одинаковыми параметрами работать на данных пяти парах. Ищем универсальные для них закономерности. 

 

Выложенный код целиком и полностью посвящен созданию входных данных и того, что будет предсказываться. Я не хочу подробно все описывать, но скажу, что входы можно разделить на 6 блоков:

1) приращения ряда взятые с разным лагом

2) разница последней цены со скользящей средней

3) разница последней цены со скользящим максимумом

4) разница последней цены со скользящим минимумом

5) скользящее стандартное отклонение

6) скользящий размах данных. 

Для каждого блока берутся лаги с шагом 2 ^ (1 ... 9.5, step 0.5). То есть, сначала 2, затем 3, 4, 6, ... 724. Машина заглядывает в прошлое на 12 часов. Предсказывать мы будем приращения цены в будущее с таким же шагом до 12 часов.

 

В коде реализована наколенная система управления ОЗУ. Изначально, мои 16 Гб были полностью потрачены и R крэшнулся. После того, как я организовал последовательную обработку файлов, на моем твердотельном диске было занято около 50 Гб для всего эксперимента. 2/3 которых составили данные для обучения и тестирования (dat_train), и остаток для валидации и предпусковой оценки модели -dat_test.

 

Всего 108 входов и 18 выходов (для каждого выхода будет обучена своя машина). 

 

Вы можете удивиться, но из всего этого массива, который уже никак не помещается в оперативу, мне придется для построения модели использовать лишь одну 724-ю часть. Почему?

 У меня около 19 000 000 записей для обучения модели. Каждый входной вектор заглядывает на 724 (2 ^ 9.5) минуты в прошлое. Для удовлетворения требования к независимости наблюдений, мне придется брать данные для обучения с некоторым шагом, чтобы, проще говоря, мои вектора прошлого и будущего взаимно не пересекались. Именно поэтому моя финальная выборка для обучения машины будет примерно в 724 раза меньше, чем количество наблюдений, а именно, 26 127 наблюдений. Для валидации выборка составит 13 059 наблюдений.

 

Внимательный читатель может задаться вопросом, что я сделал в случаях, когда входной вектор заглядывал в прошлое другой валютной пары - на стыковки историй. 

Выглядело это так, для приращения в 724 минуты:  

Эти 4 выбороса есть следствие эффекта стыковки историй. В силу того, что здесь представлена финальная обучающая выборка, следующие за выбросами наблюдения идут далеко и не накладываются на историю другой валютной пары. Эти примеры были удалены. Все чисто.

Для валидационной выборки:

 

Также были удалены эти 5 наблюдений.

 

Далее, еще более внимательный читатель может спросить, сильно ли различались данные между 5 парами. Да, статистически сильно.


Это PDF для приращений 60 минут, для 5 валютных пар. Они разные и тест на принадлежность к одному распределению не проходят. С этим осложнением я буду бороться в будущем методами робастной статистики.

 

В результате 1,5 суток работы я получил два хороших файла для обучения и валидации размером не более 60 мегабайт.

 

 В следующий раз я проведу анализ входов, их взаимозависимостей, корреляций с выходами. Сделаю тесты на принадлежность распределениям. И затем приступлю к машинному обучению. Обучу 2-4 модели, в зависимости от наличия свободного времени.

 Мой блог можно найти по тэгу "большойэксперимент". Следите за ходом, пробуйте запускать код.

 

Задавайте вопросы!