Машинное обучение в трейдинге: теория, модели, практика и алготорговля - страница 2
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
Вы упускаете торговые возможности:
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Регистрация
Вход
Вы принимаете политику сайта и условия использования
Если у вас нет учетной записи, зарегистрируйтесь
Уточню условия для получения денежного приза:
5 кредитов получит первый разгадавший задачу
Крайний срок рассмотрения решений: 30 июня 2016 г.
Есть пример применения алгоритма отобора информативных признаков для осуществления торговой стратегии.
Наверное, вы читали мои блоги о Большом эксперименте: https://www.mql5.com/ru/blogs/post/661895
И вот такая картинка:
Я пытался найти одну закономерность для пяти пар и задетектировать процент правильно угаданных сделок на валидационной выборке в 25 лет примерно. Не получилось с ходу. Для любого взятого горизонта прогноза не достиг желаемой точности.
Далее, возьмем лишь одну пару eurusd. У нее я обнаружил зависимость исхода движения цены на 3 часа вперед от некоторого поднабора моих предикторов.
Я привел предикторы к категориальному виду и запустил свою функцию поиска значимых предикторов. Сделал прямо сейчас, пока на работе, за 20 минут.
[1] "1.51%"
> final_vector <- c((sao$par >= threshold), T)
> names(sampleA)[final_vector]
[1] "lag_diff_45_var" "lag_diff_128_var" "lag_max_diff_8_var" "lag_max_diff_11_var" "lag_max_diff_724_var" "lag_sd_362_var"
[7] "output"
Сходимости так быстро не добился, но некоторый результат на уровне полтора процента объясняющей способности получил.
График сходимости (минимизация).
Далее - построение модели.
У нас несколько категориальных предикторов. Строим "книгу правил" или то, какая именно зависимость есть между предикторами и output - лонг или шорт в перспективе 3х часов.
Как она выглядит в результате:
Видим перекос количества бай и селл в каждой строке и соответствующее таким цифрам значение p-value критерия хи-квадрат на соответствие распределению 50/50. Выбираем только те строки, где вероятность ниже 0.01.
И код всего эксперимента, начиная с момента, когда входы уже отобраны:
dat_test <- sampleA[, c("lag_diff_45_var"
, "lag_diff_128_var"
, "lag_max_diff_8_var"
, "lag_max_diff_11_var"
, "lag_max_diff_724_var"
, "lag_sd_362_var"
, "output")]
dat_test$concat <- do.call(paste0, dat_test[1:(ncol(dat_test) - 1)])
x <- as.data.frame.matrix(table(dat_test$concat
, dat_test$output))
x$pval <- NA
for (i in 1:nrow(x)){
x$pval[i] <- chisq.test(x = c(x$`0`[i], x$`1`[i])
, p = c(0.5, 0.5))$p.value
}
trained_model <- subset(x
, x$pval < 0.01)
trained_model$concat <- rownames(trained_model)
trained_model$direction <- NA
trained_model$direction [trained_model$`1` > trained_model$`0`] <- 1
trained_model$direction [trained_model$`0` > trained_model$`1`] <- 0
### test model
load('C:/Users/aburnakov/Documents/Private/big_experiment/many_test_samples.R')
many_test_samples_eurusd_categorical <- list()
for (j in 1:49){
dat <- many_test_samples[[j]][, c(1:108, 122)]
disc_levels <- 3
for (i in 1:108){
naming <- paste(names(dat[i]), 'var', sep = "_")
dat[, eval(naming)] <- discretize(dat[, eval(names(dat[i]))], disc = "equalfreq", nbins = disc_levels)[,1]
}
dat$output <- NA
dat$output [dat$future_lag_181 > 0] <- 1
dat$output [dat$future_lag_181 < 0] <- 0
many_test_samples_eurusd_categorical[[j]] <- subset(dat
, is.na(dat$output) == F)[, 110:218]
many_test_samples_eurusd_categorical[[j]] <- many_test_samples_eurusd_categorical[[j]][(nrow(dat) / 5):(2 * nrow(dat) / 5), ]
}
correct_validation_results <- data.frame()
for (i in 1:49){
dat_valid <- many_test_samples_eurusd_categorical[[i]][, c("lag_diff_45_var"
, "lag_diff_128_var"
, "lag_max_diff_8_var"
, "lag_max_diff_11_var"
, "lag_max_diff_724_var"
, "lag_sd_362_var"
, "output")]
dat_valid$concat <- do.call(paste0, dat_valid[1:(ncol(dat_valid) - 1)])
y <- as.data.frame.matrix(table(dat_valid$concat
, dat_valid$output))
y$concat <- rownames(y)
valid_result <- merge(x = y, y = trained_model[, 4:5], by.x = 'concat', by.y = 'concat')
correct_sell <- sum(subset(valid_result
, valid_result$direction == 0)[, 2])
correct_buys <- sum(subset(valid_result
, valid_result$direction == 1)[, 3])
correct_validation_results[i, 1] <- correct_sell
correct_validation_results[i, 2] <- correct_buys
correct_validation_results[i, 3] <- sum(correct_sell
, correct_buys)
correct_validation_results[i, 4] <- sum(valid_result[, 2:3])
correct_validation_results[i, 5] <- correct_validation_results[i, 3] / correct_validation_results[i, 4]
}
hist(correct_validation_results$V5, breaks = 10)
plot(correct_validation_results$V5, type = 's')
sum(correct_validation_results$V3) / sum(correct_validation_results$V4)
Далее, есть 49 валидационных выборок, каждая покрывающая 5 лет примерно. Вот на них валидируем модель, будем считать процент правильно угаданных направлений торговли.
Смотрим - процент правильно угаданных сделок по выборкам и гистограмма этого значения:
И считаем сколько всего на всех выборках мы угадываем направление сделки:
> sum(correct_validation_results$`total correct deals`) / sum(correct_validation_results$`total deals`)
[1] 0.5361318
Около 54%. Но без учета того, что нужно преодолеть расстояние между Ask & Bid. То есть, пороговая величина согласно выше приведенному графику примерно на уровне 53% при условии, что спред = 1 пункт.
То есть, мы за 30 минут состряпали простую модель, которую легко захардкодить в терминале, например. И это даже не комитет. И искал зависимости я 20 минут вместо 20 часов. В общем, что-то есть.
И все благодаря правильному отбору информативных признаков.
И детальная статистика по каждой валид. выборке.
Все исходные данные доступны по ссылкам в блоге.
И эта модель едва ли сильно прибыльна. МО на уровне пол пункта. Но направления придерживаюсь именно такого.
Всегда учимся на прошлом.
Смотрим столетиями на график. Оба на и видим "три солдата", потом видим "голова и плечи". Сколько уже таких фигур увидели и верим в эти фигуры, торгуем...
А если задачу поставить так:
1. Автоматические найти такие фигуры, причем не вообще ко всем графикам, а к конкретной валютной паре, те которые встречались недавно, а не три века назад у японцев при торговле рисом.
2. А годятся ли у нас те исходные данные, на которых мы автоматически ищем такие фигуры - паттерны.
Для ответа на первый вопрос рассмотрим алгоритм, который называется "лес случайных деревьев" - random forest. Алгоритм в качестве исходных данных для своей работы берет котировку одной или нескольких валют, индикаторы, приращения цен - все, что напридумывал человек. 10-5-100-200 ... входных переменных. Затем берет весь набор значений переменных, относящихся к одному моменту времени, соответствующих одному бару и ищет такую комбинацию этих входных переменных, которая бы соответствовала на исторических данных вполне определенному результату, например, ордеру BUY. А другой набор комбинаций другому ордеру - SELL. Каждому такому набору соответствует отдельное дерево. Опыт показывает, что для входного набора часовика из 18000 баров (около 3-х лет) алгоритм находит 200-300 деревьев. Это и есть набор паттернов, почти аналоги "голов и плеч", и целых рот солдатов.
Проблема в этом алгоритме состоит в том, что такие деревья могут ухватить некоторую конкретику, которая не встретится в будущем. Это называется здесь на форуме "сверхподгонка", в машинном обучении "переобучение". Известно, что весь большой набор исходных переменных можно поделить на две части: относящиеся к выходной переменной и не имеющие отношение шум. Вот Бурнаков и пытается отсеять те, которые не имеют отношения к результату.
ПС.
При построении трендовых ТС (BUY, SELL) любые разновидности машек относятся к шуму!
То что вы видите это малая часть рынка и то не самая важная. Никто не строит пирамиду вверх ногами.
То что вы видите это малая часть рынка и то не самая важная. Никто не строит пирамиду вверх ногами.
Попробовал обучить нейронку на входных данных, потом посмотрел на веса. Если у входных данных малый вес, то он видимо не нужен. Делал через R (Rattle), спасибо СанСанычу за его статью https://www.mql5.com/ru/articles/1165.
На практике я такой подход не проверял, интересно получилось или нет. Я бы взял input_1 input_3 input_5 input_7 input_9 input_11
Попробовал обучить нейронку на входных данных, потом посмотрел на веса. Если у входных данных малый вес, то он видимо не нужен. Делал через R (Rattle), спасибо СанСанычу за его статью https://www.mql5.com/ru/articles/1165.
На практике я такой подход не проверял, интересно получилось или нет. Я бы взял input_1 input_3 input_5 input_7 input_9 input_11
) хм. очень интересно получилось.
Уточняющий вопрос. А почему вы не включите тогда еще несколько входов, где вес небольшой, например, 13, 14, 16? Могли бы вы показать диаграмму входов и весов в упорядочиванием по весу?
Сори, не понял сначала. Да у указанных входов большой по модулю вес, как и должно быть.
Визуально все веса делятся на две группы. Если нужно их поделить по принципу значимые/не значимые, то 5,11,7,1,3,9 явно выделяются, такого набора думаю хватит.