Начало здесь: 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 модели, в зависимости от наличия свободного времени.
Мой блог можно найти по тэгу "большойэксперимент". Следите за ходом, пробуйте запускать код.
Задавайте вопросы!