
Переосмысливаем классические стратегии (Часть 12): Стратегия пробоев на паре EURUSD
В настоящей статье мы вместе построим торговую стратегию на MQL5. Мы будем внедрять торговую стратегию пробоев и последовательно совершенствовать ее, чтобы полностью раскрыть ее потенциал. Обсудим некоторые характеристики нашей стратегии.
Сосредоточимся на паре EURUSD и будем отслеживать ее движение на таймфрейме H1. Наша стратегия пробоев сначала зафиксирует текущие максимумы и минимумы цен, предлагаемых по паре EURUSD. Со временем мы будем ждать, когда ценовые уровни полностью откроются и закроются за пределами канала, созданного первоначальными максимумами и минимумами, которые мы зафиксировали.
Когда это произойдет, наша торговая стратегия обнаружит предубеждение в том, что рынки, скорее всего, продолжат двигаться в определенном направлении. Это не та точка, в которой происходит заход на наши позиции. Мы зайдем на свои позиции, когда подтвердится наше смещение. Как только цены полностью откроются и закроются за пределами экстремума свечи, которая пробила наш начальный канал, мы откроем длинные позиции, если окажемся выше канала, а в противном случае - короткие позиции.
Пока что указанная нами система будет открывать слишком много сделок. Нам нужно указать другие показатели силы или слабости, которые помогут нам отфильтровать убыточные сделки, которые мы потенциально можем совершить. Скользящая средняя может помочь нам быстро определить рыночные тенденции.
Мы разработаем нашу систему таким образом, чтобы сначала отслеживать текущие цены, предлагаемые на рынке, на котором мы находимся, а затем наблюдать, в каком направлении цена выходит из канала и поддерживается ли этот пробой ценовым движением в будущем. Если наблюдаемый нами пробой соответствует ценовому движению, которое мы наблюдаем после пробоя, мы будем использовать наши скользящие средние для определения времени исполнения нашего ордера.
Мы предпочтем открывать длинные позиции, когда быстрая скользящая средняя будет выше медленной, а обратное имеет значение true для наших коротких позиций. Все наши сделки будут активно обновляться с использованием индикатора средний истинный диапазон для расчета наших настроек стоп-лосса и тейк-профита.
Мы протестируем нашу торговую стратегию в период с 1 января 2020 года по 30 ноября 2024 года на таймфрейме H1.
Наши технические индикаторы будут настроены следующим образом:
- Быстрая скользящая средняя: 5-периодная экспоненциальная скользящая средняя, применяемая к цене закрытия.
- Медленная скользящая средняя: 60-периодная экспоненциальная скользящая средняя, применяемая к цене закрытия.
- Средний истинный диапазон (Average True Range): 14-периодный индикатор ATR.
Рис. 1: Начальное состояние нашего торгового приложения.
Через некоторое время ценовые уровни, наконец, откроются и закроются за пределами канала. Эта крайняя точка будет нашим смещением, той стороной, за которой, по нашему мнению, последуют рынки. Наше смещение подтвердится, если впоследствии уровни цен закроются ниже этого смещения. В противном случае мы не будем заключать какие-либо сделки.
Рис. 2: Наше торговое приложение обнаружило отклонение на рынке.
Если уровни цен подтвердят наше смещение, то у нас будет достаточно уверенности, чтобы открыть позицию на рынке. Изначально наша стратегия будет заключаться в следовании тренду. Поэтому, если цены пробьются выше канала, мы будем искать возможности для покупки.
Рис. 3: Наши позиции открываются после подтверждения нашего смещения.
Начинаем на MQL5
Наше торговое приложение собрано воедино с использованием торговой логики и фундаментальных концепций технического анализа. Выделим ключевые элементы, содержащиеся в коде.
Часть системы | Предполагаемая цель |
---|---|
Постоянные и параметры | Мы исправим некоторые аспекты нашего торгового алгоритма для обеспечения согласованности во всех наших тестах, такие как периоды скользящих средних, размер лота и ширина наших стоп-лоссов и тейк-профитов. |
Глобальные переменные | Эти переменные используются в разных частях нашего кода, и важно, чтобы при их использовании мы каждый раз указывали на одно и то же значение. Некоторые из глобальных переменных в нашем приложении включают максимум и минимум канала, направление, в котором, по нашему мнению, будет двигаться рынок (смещение), а также другие значения технических индикаторов. |
Нам также нужно будет определить другие важные переменные в нашем торговом приложении, которые помогут нам отслеживать состояние рынка. Давайте ознакомимся с самыми важными из них.
Переменная | Предполагаемая цель |
---|---|
Смещение | Параметр bias (смещение) символизирует направление, в котором движутся цены, его допустимое значение равно 1, если тренд бычий, и -1, если тренд медвежий. В противном случае он будет установлен на 0. . |
Скользящие средние | Быстрая скользящая средняя (ma_f) и медленная скользящая средняя (ma_s) определяют тенденцию. Если ma_f[0] > ma_s[0] и цена (c) находится выше быстрой скользящей средней, открывается сделка на покупку. В противном случае, если ma_f[0] < ma_s[0], а цена ниже медленной скользящей средней, открывается сделка на продажу. |
Пробой | При пробое уровня канала (верхней или нижней границы) устанавливается направление движения (смещение). |
Уровни пробоев | Уровень пробоев покажет нам, в каком направлении, по нашему мнению, продолжат двигаться рынки в будущем. Если рынки пробьют верхнюю границу, наши настроения будут бычьими. |
Подтверждение сигнала | Наши сделки не будут совершаться без подтверждения сигнала. Сигнал подтверждается, если рынок сохраняет свое направление после пробоя. Если подтверждение утеряно, позиция может быть скорректирована или закрыта. |
Управление ордерами | Сделки, которые мы будем заключать, будут зависеть от смещения, которое мы в настоящее время наблюдаем на рынке. В случае восходящего тренда (смещение == 1) отправляется команда: Trade.Buy(vol, Symbol(), ask, channel_low, 0, "Volatility Doctor AI"); В противном случае, в случае нисходящего тренда (смещение == -1), будет отправлена команда: Trade.Sell(vol, Symbol(), bid, channel_high, 0, "Volatility Doctor AI"); |
Стоп-лосс | Изначально задавалось значение channel_low для сделок на покупку и channel_high для сделок на продажу, а в будущем обновлялось с использованием значения ATR. |
Теперь, когда у нас есть концептуальное расположение движущихся фигур в нашей стратегии, вместе приступим к созданию нашей торговой стратегии. Сначала необходимо определить детали нашего торгового приложения.
//+------------------------------------------------------------------+ //| MTF Channel 2.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00"
Теперь загрузим торговую библиотеку.
//+------------------------------------------------------------------+ //| Library | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh> CTrade Trade;
Определим константы для нашего торгового приложения, такие как периоды некоторых наших технических индикаторов.
//+------------------------------------------------------------------+ //| Constants | //+------------------------------------------------------------------+ const int ma_f_period = 5; //Slow MA const int ma_s_period = 60; //Slow MA
Теперь определим входные данные, которые может настроить наш конечный пользователь. Поскольку мы поддерживаем неизменными наши технические индикаторы, наш конечный пользователь не перегружен многочисленными параметрами.
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input group "Money Management" input int lot_multiple = 5; //Lot Multiple input int atr_multiple = 5; //ATR Multiple
Глобальные переменные, которые мы будем использовать в большей части нашей программы.
//+------------------------------------------------------------------+ //| Global varaibles | //+------------------------------------------------------------------+ double channel_high = 0; double channel_low = 0; double o,h,l,c; int bias = 0; double bias_level = 0; int confirmation = 0; double vol,bid,ask,initial_sl; int atr_handler,ma_fast,ma_slow; double atr[],ma_f[],ma_s[]; double bo_h,bo_l;
Когда наше торговое приложение будет загружено в первый раз, мы вызовем специализированную функцию для загрузки наших технических индикаторов и подготовки других необходимых рыночных данных для нас.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- setup(); //--- return(INIT_SUCCEEDED); }
Если мы более не используем советник, следует освободить ресурсы, которые больше не используем.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); }
Всякий раз при получении обновленных цен, мы будем обновлять наши глобальные переменные, а затем проверять наличие новых возможностей для торговли.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If we have positions open if(PositionsTotal() > 0) manage_setup(); //--- Keep track of time static datetime timestamp; datetime time = iTime(Symbol(),PERIOD_CURRENT,0); if(timestamp != time) { //--- Time Stamp timestamp = time; if(PositionsTotal() == 0) find_setup(); } }
Следующая функция будет отвечать за загрузку наших технических индикаторов и получение рыночных данных.
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { channel_high = iHigh(Symbol(),PERIOD_M30,1); channel_low = iLow(Symbol(),PERIOD_M30,1); vol = lot_multiple * SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); atr_handler = iATR(Symbol(),PERIOD_CURRENT,14); ma_fast = iMA(Symbol(),PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA(Symbol(),PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); }
Когда наша стратегия будет загружена в первый раз, мы отметим текущие высокие и низкие цены, предлагаемые на рынке. Таким образом, все будущие цены, которые мы будем наблюдать, можно рассматривать в контексте, мы можем сравнить их с начальными уровнями цен, которые мы видели, когда впервые зашли сюда.
//+---------------------------------------------------------------+ //| Update channel | //+---------------------------------------------------------------+ void update_channel(double new_high, double new_low) { channel_high = new_high; channel_low = new_low; ObjectDelete(0,"Channel High"); ObjectDelete(0,"Channel Low"); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); }
Если у нас есть открытые позиции, нам необходимо соответствующим образом обновить наши значения стоп-лосса и тейк-профита. Мы скорректируем наши настройки риска, используя значение, кратное среднему истинному диапазону таким образом, чтобы наши настройки риска были связаны с текущими уровнями волатильности на рынке.
//+---------------------------------------------------------------+ //| Manage setup | //+---------------------------------------------------------------+ void manage_setup(void) { bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); CopyBuffer(atr_handler,0,0,1,atr); Print("Managing Position"); if(PositionSelect(Symbol())) { Print("Position Found"); initial_sl = PositionGetDouble(POSITION_SL); } if(bias == 1) { Print("Position Buy"); double new_sl = (ask - (atr[0] * atr_multiple)); Print("Initial: ",initial_sl,"\nNew: ",new_sl); if(initial_sl < new_sl) { Trade.PositionModify(Symbol(),new_sl,0); Print("DONE"); } } if(bias == -1) { Print("Position Sell"); double new_sl = (bid + (atr[0] * atr_multiple)); Print("Initial: ",initial_sl,"\nNew: ",new_sl); if(initial_sl > new_sl) { Trade.PositionModify(Symbol(),new_sl,0); Print("DONE"); } } }
Если у нас нет открытых позиций, мы будем следовать правилам, которые изложили ранее, чтобы определить торговые возможности. Напомним, что мы ожидаем увидеть сильное ценовое движение, выходящее за пределы первоначального канала, в котором мы найдем цену. После этого мы обретем достаточную уверенность, чтобы совершить сделку, если ценовые уровни продолжат двигаться в том же направлении и не пересекут открытый канал, который они только что создали.
//+---------------------------------------------------------------+ //| Find Setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- We are updating the system o = iOpen(Symbol(),PERIOD_CURRENT,1); h = iHigh(Symbol(),PERIOD_CURRENT,1); l = iLow(Symbol(),PERIOD_CURRENT,1); c = iClose(Symbol(),PERIOD_CURRENT,1); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); CopyBuffer(atr_handler,0,0,1,atr); CopyBuffer(ma_fast,0,0,1,ma_f); CopyBuffer(ma_slow,0,0,1,ma_s); //--- If we have no market bias if(bias == 0) { //--- Our bias is bullish if ( (o > channel_high) && (h > channel_high) && (l > channel_high) && (c > channel_high) ) { bias = 1; bias_level = h; bo_h = h; bo_l = l; mark_bias(h); } //--- Our bias is bearish if ( (o < channel_low) && (h < channel_low) && (l < channel_low) && (c < channel_low) ) { bias = -1; bias_level = l; bo_h = h; bo_l = l; mark_bias(l); } } //--- Is our bias valid? if(bias != 0) { //--- Our bearish bias has been violated if ( (o > channel_high) && (h > channel_high) && (l > channel_high) && (c > channel_high) && (bias == -1) ) { forget_bias(); } //--- Our bullish bias has been violated if ( (o < channel_low) && (h < channel_low) && (l < channel_low) && (c < channel_low) && (bias == 1) ) { forget_bias(); } //--- Our bullish bias has been violated if ( ((o < channel_high) && (c > channel_low)) ) { forget_bias(); } //--- Check if we have confirmation if((confirmation == 0) && (bias != 0)) { //--- Check if we are above the bias level if ( (o > bias_level) && (h > bias_level) && (l > bias_level) && (c > bias_level) && (bias == 1) ) { confirmation = 1; } //--- Check if we are below the bias level if ( (o < bias_level) && (h < bias_level) && (l < bias_level) && (c < bias_level) && (bias == -1) ) { confirmation = 1; } } } //--- Check if our confirmation is still valid if(confirmation == 1) { //--- Our bias is bullish if(bias == 1) { //--- Confirmation is lost if we fall beneath the breakout level if ( (o < bias_level) && (h < bias_level) && (l < bias_level) && (c < bias_level) ) { confirmation = 0; } } //--- Our bias is bearish if(bias == -1) { //--- Confirmation is lost if we rise above the breakout level if ( (o > bias_level) && (h > bias_level) && (l > bias_level) && (c > bias_level) ) { confirmation = 0; } } } //--- Do we have a setup? if((confirmation == 1) && (bias == 1)) { if(ma_f[0] > ma_s[0]) { if(c > ma_f[0]) { Trade.Buy(vol,Symbol(),ask,channel_low,0,"Volatility Doctor AI"); initial_sl = channel_low; } } } if((confirmation == 1) && (bias == -1)) { if(ma_f[0] < ma_s[0]) { if(c < ma_s[0]) { Trade.Sell(vol,Symbol(),bid,channel_high,0,"Volatility Doctor AI"); initial_sl = channel_high; } } } Comment("O: ",o,"\nH: ",h,"\nL: ",l,"\nC:",c,"\nC H: ",channel_high,"\nC L:",channel_low,"\nBias: ",bias,"\nBias Level: ",bias_level,"\nConfirmation: ",confirmation,"\nMA F: ",ma_f[0],"\nMA S: ",ma_s[0]); }
Когда ценовые уровни пробьются за пределы канала, который у нас был изначально, мы отметим экстремальный ценовой уровень, созданный свечой, которая выбилась за пределы канала. Этот экстремальный уровень и есть уровень предвзятости.
//+---------------------------------------------------------------+ //| Mark our bias levels | //+---------------------------------------------------------------+ void mark_bias(double f_level) { ObjectCreate(0,"Bias",OBJ_HLINE,0,0,f_level);the }
Наконец, если ценовые уровни вернутся в торговый канал после предыдущего пробоя, мы будем считать старый канал недействительным и обновим новую позицию канала до уровней, созданных свечой пробоя.
//+---------------------------------------------------------------+ //| Forget our bias levels | //+---------------------------------------------------------------+ void forget_bias() { update_channel(bo_h,bo_l); bias = 0; bias_level = 0; confirmation = 0; ObjectDelete(0,"Bias"); } //+------------------------------------------------------------------+
Теперь мы готовы к бэк-тестированию торговой стратегии пробоев. Я назвал приложение "MTF Channel 2", что означает канал с множеством таймфреймов (Multiple Time Frame Channel). Мной выбран символ EURUSD на таймфрейме H1. Даты наших тестов совпадают с датами, указанными нами ранее. Читатель заметит, что эти 3 конкретные настройки зафиксированы во всех 3 тестах.
Рис. 4: Первый пакет настроек, использованный для нашего первоначального бэк-теста.
Это далеко не все настроенные нами параметры. Мы выбрали настройки случайной задержки, чтобы имитировать торговые сценарии в реальном времени, в результате чего наблюдаемая задержка может варьироваться. Мы также решили смоделировать тест на основе реальных тиков, чтобы попытаться получить достоверный опыт реальной торговли.
Рис. 5: Второй пакет настроек, отобранных для тестирования нашей стратегии.
Мы исправим настройки, используемые в нашем советнике, чтобы они были одинаковыми во всех тестах, которые мы будем выполнять. Сохранение равнозначности этих настроек поможет нам определить причину прибыльности, выбрав лучшие правила торговли.
Рис. 6: Наши настройки управления капиталом.
Посмотрим на нашу стратегию в действии. На рисунке 7 ниже, в правой части скриншота, мы видим внутренние переменные, которые наше приложение использует для принятия решений. Обратите внимание, что все наши сделки будут заключены только в том случае, если для подтверждения установлено значение 1.
Рис. 7: Бэк-тестирование нашей торговой стратегии на паре EURUSD.
К сожалению, мы видим, что наша стратегия приносила убытки. Это сигнал, что есть место для улучшения.
Рис. 8: Просмотр графика, связанного с нашим бэк-тестированием.
Давайте подробнее остановимся на только что проведенном нами тесте. Очевидно, что наша стратегия выявила в общей сложности 53 сделки, и 70% из них были убыточными. Наш коэффициент Шарпа является отрицательным. Это плохие показатели эффективности.
С другой стороны, наша средняя прибыль больше, чем наш средний убыток, и это хороший признак. Теперь давайте посмотрим, как можно получить лучшие результаты. Мы хотим лучше контролировать наши валовые и средние убытки, одновременно максимизируя нашу среднюю прибыль и долю прибыльных сделок.
Рис. 9: Подробности нашего бэк-теста.
Улучшаем наши первые результаты
Когда я наблюдал за бэк-тестированием, мне было неприятно наблюдать, как советник неоднократно совершает одну и ту же ошибку. Большая часть наших убытков была понесена из-за того, что мы заключали сделки на бессмысленных колебаниях цен, которые, как оказалось, удовлетворяли всем нашим условиям. Единственным решением для этого является выбор лучших условий, которые могут естественным образом различать слабые и сильные движения на рынке.
Один из вариантов, который у нас есть, - это сравнить динамику EUR и USD с общим эталоном. Для этого мы можем использовать фунты стерлингов (GBP). Мы сравним динамику пары EURGBP и GBPUSD, прежде чем совершим открытие позиции. То есть, если на нашем графике мы видим, что пара EURUSD находится в сильном бычьем тренде, мы также хотели бы видеть, что пара EURGBP движется в том же тренде, и мы надеемся, что пара GBPUSD также будет в бычьем тренде.
Другими словами, если уровни цен EURUSD создают у нас впечатление, что евро дорожает по отношению к доллару, то мы обретем уверенность только в том случае, если увидим, что евро дорожает по отношению к британскому фунту, в то время как доллар одновременно дешевеет по отношению к британскому фунту. Мы надеемся, что этот трехсторонний обменный курс поможет нам выявить ложные пробои. Мы исходим из того, что колебания, которые влияют сразу на все 3 рынка, могут быть действительно сильными движениями, в результате которых мы можем получить прибыль.
Мы добавим несколько строк кода, чтобы изменить первоначальную торговую стратегию, которую мы разработали до сих пор. Чтобы реализовать изменения, о которых мы думаем, сначала создадим новые глобальные переменные для отслеживания цен на пары EURGBP и GBPUSD. Нам также нужно будет применить технические индикаторы к двум другим нашим рынкам, чтобы можно было отслеживать тенденции на этих соответствующих рынках.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double channel_high = 0; double channel_low = 0; double o,h,l,c; int bias = 0; double bias_level = 0; int confirmation = 0; double vol,bid,ask,initial_sl; int atr_handler,ma_fast,ma_slow; double atr[],ma_f[],ma_s[]; double bo_h,bo_l; int last_trade_state,current_state; int eurgbp_willr, gbpusd_willr; string symbols[] = {"EURGBP","GBPUSD"};
Когда наш советник загружается в первый раз, нужно будет выполнить несколько дополнительных шагов, чтобы отслеживать ценовое движение, происходящее с нашими эталонными символами. Эти обновления будут реализованы в функции настройки.
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { //--- Select the symbols we need SymbolSelect("EURGBP",true); SymbolSelect("GBPUSD",true); //--- Reset our last trade state last_trade_state = 0; //--- Mark the current high and low channel_high = iHigh("EURUSD",PERIOD_M30,1); channel_low = iLow("EURUSD",PERIOD_M30,1); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); //--- Our trading volums vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN); //--- Our technical indicators atr_handler = iATR("EURUSD",PERIOD_CURRENT,14); eurgbp_willr = iWPR(symbols[0],PERIOD_CURRENT,wpr_period); gbpusd_willr = iWPR(symbols[1],PERIOD_CURRENT,wpr_period); ma_fast = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); }
Аналогичным образом, когда наше торговое приложение больше не используется, можно выпустить несколько дополнительных технических индикаторов.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(eurgbp_willr); IndicatorRelease(gbpusd_willr); IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); }
Наша функция OnTick останется прежней. Однако функции, которые она будет вызывать, будут изменены. Во-первых, всякий раз, когда мы обновляем наш канал, мы должны обновлять 3 канала на рынках, за которыми мы следим. Один на EURUSD, второй на EURGBP и последний на GBPUSD.
//+---------------------------------------------------------------+ //| Update channel | //+---------------------------------------------------------------+ void update_channel(double new_high, double new_low) { channel_high = new_high; channel_low = new_low; ObjectDelete(0,"Channel High"); ObjectDelete(0,"Channel Low"); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); }
Большая часть программы осталась прежней, но самым существенным изменением, которое мы внесли, было то, что теперь мы требовали, чтобы наше торговое приложение проверяло 2 других рынка, прежде чем оно решит совершить сделку. Если наши фундаментальные показатели дадут нам уверенность в том, что пробой, который мы наблюдаем на паре EURUSD, может быть подкреплен реальной силой, тогда мы возьмем эту позицию. Эти обновления будут отражены в функции настройки поиска.
Вы также заметите, что эта функция вызывает новую функцию, которую мы не определяли в предыдущей версии приложения стратегии пробоев. Дополнительная функция подтверждения проверит 2 эталонных рынка на предмет соответствия нашим основным торговым условиям.
//+---------------------------------------------------------------+ //| Find Setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- I have omitted code pieces that were unchanged //--- Do we have a setup? if((confirmation == 1) && (bias == 1) && (current_state != last_trade_state)) { if(ma_f[0] > ma_s[0]) { if(c > ma_f[0]) { if(additional_confirmation(1)) { Trade.Buy(vol,"EURUSD",ask,channel_low,0,"Volatility Doctor"); initial_sl = channel_low; last_trade_state = 1; } } } } if((confirmation == 1) && (bias == -1) && (current_state != last_trade_state)) { if(ma_f[0] < ma_s[0]) { if(c < ma_s[0]) { if(additional_confirmation(-1)) { Trade.Sell(vol,"EURUSD",bid,channel_high,0,"Volatility Doctor"); initial_sl = channel_high; last_trade_state = -1; } } } } }
Эта функция должна помочь нам отличить рыночный шум от истинной силы. Во время поиска подтверждения на других смежных рынках, мы надеемся всегда выбирать наиболее выгодные сделки из возможных.
//+---------------------------------------------------------------+ //| Check for true strength | //+---------------------------------------------------------------+ bool additional_confirmation(int flag) { //--- Do we have additional confirmation from our benchmark pairs? //--- Record the average change in the EURGBP and GBPUSD Market vector eurgbp_willr_f = vector::Zeros(1); vector gbpusd_willr_f = vector::Zeros(1); eurgbp_willr_f.CopyIndicatorBuffer(eurgbp_willr,0,0,1); gbpusd_willr_f.CopyIndicatorBuffer(gbpusd_willr,0,0,1); if((flag == 1) && (eurgbp_willr_f[0] > -50) && (gbpusd_willr_f[0] < -50)) return(true); if((flag == -1) && (eurgbp_willr_f[0] < -50) && (gbpusd_willr_f[0] > -50)) return(true); Print("EURGBP WPR: ",eurgbp_willr_f[0],"\nGBPUSD WPR: ",gbpusd_willr_f[0]); return(false); }
Эта версия нашего приложения будет называться "MTF EURUSD Channel». Первая версия, которую мы создали, была более обобщенной и могла быть легко использована для торговли любым другим символом в нашем терминале. Однако в этой версии в качестве эталонов будут использоваться пары EURGBP и GBPUSD, и поэтому она более специализирована и предназначена только для торговли по паре EURUSD. Читатель заметит, что все наши условия тестирования идентичны первому тесту. Мы проведем этот бэк-тест, используя те же таймфреймы и те же периоды времени, что и при первом тестировании, с 1 января 2020 года по 30 ноября 2024 года.
Рис. 10: Первый пакет настроек для нашего бэк-тестирования стратегии пробоев канала EURUSD.
Если вы намерены следовать настройкам, которые я здесь показываю, то имейте в виду, что установка параметра моделирования "Каждый тик основан на реальных тиках" может занять много времени в зависимости от вашего интернет-соединения, поскольку терминал MT5 будет запрашивать у вашего брокера обширные данные для максимально реалистичного моделирования рынка. Поэтому не пугайтесь, если выполнение этого процесса может занять несколько минут, и не выключайте компьютер в середине процесса.
Рис. 11: Нам нужно, чтобы второй пакет настроек был идентичен настройкам, которые мы использовали в первом тесте.
Использование лота, кратного 1, означает, что все мои сделки будут совершаться с минимальным размером лота. Если мы сможем сделать нашу систему прибыльной при минимальном размере лота, то увеличение кратности лота пойдет нам на пользу. Однако, если наша система не приносит прибыли при минимальном лоте, мы ничего не выиграем, увеличив размер лота.
Рис. 12: Параметры, которые мы будем использовать для управления поведением нашего приложения.
Теперь мы можем увидеть, как наша торговая система работает на исторических данных. Обратите внимание, что эта версия нашей системы отслеживает сразу 3 рынка. Во-первых, мы всегда будем следить за парой EURUSD, чтобы по ней можно было судить о нашем смещении.
Рис. 13: Наша система в действии на паре EURUSD.
Наши позиции могут быть открыты только в том случае, если мы наблюдаем за парами EURGBP и GBPUSD, которые движутся в противоположных направлениях, как показано на рис. 14 и 15 ниже. Мы будем оценивать тенденцию на двух рынках, используя индикатор «Процентный диапазон Уильямса» (Williams Percent Range, WPR). Если WPR находится выше уровня 50, мы считаем тренд бычьим.
Рис. 14: Наша первая подтверждающая пара - GBPUSD
В данном случае мы нашли торговую возможность купить EURUSD. Мы определили эту возможность, потому что показатели WPR на двух рынках находились по разные стороны от уровня 50. За этим дисбалансом, вероятно, последуют волатильные рыночные условия, идеальные для любой стратегии пробоев.
Рис. 15: Наша вторая эталонная пара.
На рисунке 9 ниже показано, как баланс нашего имитируемого торгового счета меняется с течением времени. Наша цель - глубоко понимать, почему наша стратегия терпит неудачу, чтобы мы могли попытаться исправить ее недостатки.
Рис. 16: Представление графика баланса нашего счета с течением времени.
К сожалению, изменения, которые мы внесли в нашу систему, снизили прибыльность нашего торгового приложения. Наши средние убытки и прибыль увеличились на те же суммы. А доля прибыльных сделок незначительно снизилась.
Рис. 17: Подробные результаты нашего бэк-теста.
Последняя попытка улучшения
Нам не удалось добиться улучшения в том, что имеет самое большое значение, - в прибыльности. Вместо того чтобы пытаться навязать свои взгляды на рынок, мы позволим компьютеру научиться использовать скользящие средние лучше, чем это способны сделать мы. Наши взгляды на то, как эффективно торговать, в определенной степени предвзяты.
С другой стороны, если мы позволим нашему компьютеру изучить взаимосвязь между ценой закрытия и скользящей средней, то наш компьютер сможет создавать свои собственные торговые правила и торговать, основываясь на том, что по его ожиданиям произойдет дальше, в отличие от реактивной формы торговли, которую мы практиковали до сих пор.
Для начала я создал скрипт, который поможет нам извлекать исторические рыночные данные. Чтобы можно было начать работу, просто перетащите скрипт на тот рынок, на котором вы хотите торговать. Скрипт будет извлекать рыночные данные для вас, а также две скользящие средние, которые нужны для нашей стратегии, в том же формате, который используется в нашем торговом приложении.
//+------------------------------------------------------------------+ //| ProjectName | //| Copyright 2020, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" #property script_show_inputs //+------------------------------------------------------------------+ //| Script Inputs | //+------------------------------------------------------------------+ input int size = 100000; //How much data should we fetch? //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ int ma_f_handler,ma_s_handler; double ma_f_reading[],ma_s_reading[]; //+------------------------------------------------------------------+ //| On start function | //+------------------------------------------------------------------+ void OnStart() { //--- Load indicator ma_s_handler = iMA(Symbol(),PERIOD_CURRENT,60,0,MODE_EMA,PRICE_CLOSE); ma_f_handler = iMA(Symbol(),PERIOD_CURRENT,5,0,MODE_EMA,PRICE_CLOSE); //--- Load the indicator values CopyBuffer(ma_f_handler,0,0,size,ma_f_reading); CopyBuffer(ma_s_handler,0,0,size,ma_s_reading); ArraySetAsSeries(ma_f_reading,true); ArraySetAsSeries(ma_s_reading,true); //--- File name string file_name = "Market Data " + Symbol() +" MA Cross" + " As Series.csv"; //--- Write to file int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,","); for(int i= size;i>=0;i--) { if(i == size) { FileWrite(file_handle,"Time","Open","High","Low","Close","MA 5","MA 60"); } else { FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i), iOpen(Symbol(),PERIOD_CURRENT,i), iHigh(Symbol(),PERIOD_CURRENT,i), iLow(Symbol(),PERIOD_CURRENT,i), iClose(Symbol(),PERIOD_CURRENT,i), ma_f_reading[i], ma_s_reading[i] ); } } //--- Close the file FileClose(file_handle); } //+------------------------------------------------------------------+
Анализ данных на Python
Теперь, когда у вас есть рыночные данные в формате CSV, можно приступить к созданию модели ИИ, которая, как мы надеемся, поможет нам прогнозировать ложные пробои и избегать их.
import pandas as pd import numpy as np from sklearn.model_selection import TimeSeriesSplit,cross_val_score from sklearn.linear_model import Ridge from sklearn.metrics import mean_squared_error import matplotlib.pyplot as plt import seaborn as sns
Ознакомимся с рыночными данными, извлеченными нами ранее. Обратите внимание на столбец «время» (Time) в моем фрейме данных, обратите внимание, что последняя запись, которая у меня есть, датирована 18 апреля 2019 года. Это делается намеренно. Напомним, что даты начала по обоим предыдущим тестам были 1 января 2020 года. Это означает, что мы не обманываем себя, предоставляя модели все ответы на наш тест для нее.
#Define the forecast horizon look_ahead = 24 #Read in the data data = pd.read_csv('Market Data EURUSD MA Cross As Series.csv') #Drop the last 4 years data = data.iloc[:(-24 * 365 * 4),:] data.reset_index(drop=True,inplace=True) #Label the data data['Target'] = data['Close'].shift(-look_ahead) data['MA 5 Target'] = data['MA 5'].shift(-look_ahead) data['MA 5 Close Target'] = data['Target'] - data['MA 5 Target'] data['MA 60 Target'] = data['MA 60'].shift(-look_ahead) data['MA 60 Close Target'] = data['Target'] - data['MA 60 Target'] data.dropna(inplace=True) data.reset_index(drop=True,inplace=True) data
Рис. 18: Наши исторические рыночные данные.
Проверим, действительно ли на рынке EURUSD по скользящим средним все еще легче предсказать саму цену. Чтобы проверить нашу гипотезу, мы обучим 30 идентичных нейронных сетей прогнозировать три цели одну за другой. Сначала спрогнозируем будущую цену, 5-периодную скользящую среднюю и 60-периодную скользящую среднюю. Все цели будут спроецированы на 24 шага в будущее. Во-первых, мы напрямую зафиксируем нашу точность прогнозирования цены.
#Classical error classical_error = [] epochs = 1000 for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs') classical_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close']],data.loc[:,'Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Далее мы зафиксируем нашу точность, прогнозируя 5-периодную скользящую среднюю.
#MA Cross Over error ma_5_error = [] for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs') ma_5_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 5']],data.loc[:,'MA 5 Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Наконец, мы зафиксируем нашу точность, прогнозируя 60-периодную скользящую среднюю.
#New error ma_60_error = [] for i in np.arange(0,30): model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=10000,early_stopping=False,solver='lbfgs') ma_60_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 60']],data.loc[:,'MA 60 Target'],cv=tscv,scoring='neg_mean_squared_error'))))
Когда мы представляем графически наши результаты. Как видно из приведенного ниже рисунка 12, прогнозирование 60-периодной скользящей средней привело к наибольшей ошибке в нашей системе, а прогнозирование 5-периодной скользящей средней привело к меньшей ошибке, чем прямое прогнозирование цены.
plt.plot(classical_error) plt.plot(ma_5_error) plt.plot(ma_60_error) plt.legend(['OHLC','MA 5 ','MA 60']) plt.axhline(np.mean(classical_error),color='blue',linestyle='--') plt.axhline(np.mean(ma_5_error),color='orange',linestyle='--') plt.axhline(np.mean(ma_60_error),color='green',linestyle='--') plt.grid() plt.ylabel('Cross Validated Error') plt.xlabel('Iteration') plt.title('Comparing Different The Error Associated With Different Targets') plt.show()
Рис. 19: Визуализация ошибки, связанной с различными целями.
Теперь попробуем экспортировать модель для нашего торгового приложения. Импортируем необходимые нам библиотеки.
import onnx from skl2onnx import convert_sklearn from skl2onnx.common.data_types import FloatTensorType from sklearn.neural_network import MLPRegressor
Укажем нужные нам модели. Для этой задачи я буду использовать 2 модели, поскольку скользящую среднюю за короткий период легко прогнозировать, я буду использовать простую модель Ridge для ее прогнозирования. Однако наша 60-периодная скользящая средняя оказалась сложной задачей. Поэтому я использую нейронную сеть для прогнозирования долгосрочной скользящей средней.
ma_5_model = Ridge() ma_5_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Target']) ma_5_height_model = Ridge() ma_5_height_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Close Target']) ma_60_model = Ridge() ma_60_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Target']) ma_60_height_model = Ridge() ma_60_height_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Close Target'])
Подготовка к экспортированию в ONNX.
initial_type = [('float_input', FloatTensorType([1, 5]))] ma_5_onx = convert_sklearn(ma_5_model, initial_types=initial_type, target_opset=12 ) ma_5_height_onx = convert_sklearn(ma_5_height_model, initial_types=initial_type, target_opset=12 ) ma_60_height_onx = convert_sklearn(ma_60_height_model, initial_types=initial_type, target_opset=12 ) ma_60_onx = convert_sklearn(ma_60_model, initial_types=initial_type, target_opset=12 )
Сохраним в формат ONNX.
onnx.save(ma_5_onx,'eurchf_ma_5_model.onnx') onnx.save(ma_60_onx,'eurchf_ma_60_model.onnx') onnx.save(ma_5_height_onx,'eurusd_ma_5_height_model.onnx') onnx.save(ma_60_height_onx,'eurusd_ma_60_height_model.onnx')
Окончательные обновления в MQL5
Применим наши новые модели, чтобы посмотреть, смогут ли они помочь нам отфильтровать ложные пробои на рынке. Первое обновление, которое нам нужно сделать, - это импортировать только что созданные модели ONNX.
//+------------------------------------------------------------------+ //| MTF Channel 2.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| ONNX Resources | //+------------------------------------------------------------------+ #resource "\\Files\\eurusd_ma_5_model.onnx" as const uchar eurusd_ma_5_buffer[]; #resource "\\Files\\eurusd_ma_60_model.onnx" as const uchar eurusd_ma_60_buffer[]; #resource "\\Files\\eurusd_ma_5_height_model.onnx" as const uchar eurusd_ma_5_height_buffer[]; #resource "\\Files\\eurusd_ma_60_height_model.onnx" as const uchar eurusd_ma_60_height_buffer[];
Далее нам нужно создать несколько новых переменных, связанных с нашими моделями.
//+------------------------------------------------------------------+ //| Global varaibles | //+------------------------------------------------------------------+ int bias = 0; int state = 0; int confirmation = 0; int last_cross_over_state = 0; int atr_handler,ma_fast,ma_slow; int last_trade_state,current_state; long ma_5_model; long ma_60_model; long ma_5_height_model; long ma_60_height_model; double channel_high = 0; double channel_low = 0; double o,h,l,c; double bias_level = 0; double vol,bid,ask,initial_sl; double atr[],ma_f[],ma_s[]; double bo_h,bo_l; vectorf ma_5_forecast = vectorf::Zeros(1); vectorf ma_60_forecast = vectorf::Zeros(1); vectorf ma_5_height_forecast = vectorf::Zeros(1); vectorf ma_60_height_forecast = vectorf::Zeros(1);
Необходимо расширить процедуру инициализации, чтобы теперь она настраивала наши ONNX-модели для нас.
//+---------------------------------------------------------------+ //| Load our technical indicators and market data | //+---------------------------------------------------------------+ void setup(void) { //--- Reset our last trade state last_trade_state = 0; //--- Mark the current high and low channel_high = iHigh("EURUSD",PERIOD_M30,1); channel_low = iLow("EURUSD",PERIOD_M30,1); ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high); ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low); //--- Our trading volums vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN); //--- Our technical indicators atr_handler = iATR("EURUSD",PERIOD_CURRENT,14); ma_fast = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE); ma_slow = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE); //--- Setup our ONNX models //--- Define our ONNX model ulong input_shape [] = {1,5}; ulong output_shape [] = {1,1}; //--- Create the model ma_5_model = OnnxCreateFromBuffer(eurusd_ma_5_buffer,ONNX_DEFAULT); ma_60_model = OnnxCreateFromBuffer(eurusd_ma_60_buffer,ONNX_DEFAULT); ma_5_height_model = OnnxCreateFromBuffer(eurusd_ma_5_height_buffer,ONNX_DEFAULT); ma_60_height_model = OnnxCreateFromBuffer(eurusd_ma_60_height_buffer,ONNX_DEFAULT); //--- Store our models in a list long onnx_models[] = {ma_5_model,ma_5_height_model,ma_60_model,ma_60_height_model}; //--- Loop over the models and set them up for(int i = 0; i < 4; i++) { if(onnx_models[i] == INVALID_HANDLE) { Comment("Failed to load AI module correctly: Invalid handle"); } //--- Validate I/O if(!OnnxSetInputShape(onnx_models[i],0,input_shape)) { Comment("Failed to set input shape correctly: Wrong input shape ",GetLastError()," Actual shape: ",OnnxGetInputCount(ma_5_model)); } if(!OnnxSetOutputShape(onnx_models[i],0,output_shape)) { Comment("Failed to load AI module correctly: Wrong output shape ",GetLastError()," Actual shape: ",OnnxGetOutputCount(ma_5_model)); } } }
Если наша система больше не используется, необходимо освободить ресурсы, которые больше не используем.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Free the resources we don't need IndicatorRelease(atr_handler); IndicatorRelease(ma_fast); IndicatorRelease(ma_slow); OnnxRelease(ma_5_model); OnnxRelease(ma_5_height_model); OnnxRelease(ma_60_model); OnnxRelease(ma_60_height_model); }
Когда мы получаем обновленные цены, единственное существенное отличие заключается в том, что мы также будем стремиться получить прогноз с помощью наших моделей ИИ.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Keep track of time static datetime timestamp; datetime time = iTime(Symbol(),PERIOD_CURRENT,0); if(timestamp != time) { //--- Time Stamp timestamp = time; //--- Update system variables update(); //--- Make a new prediction model_predict(); if(PositionsTotal() == 0) { state = 0; find_setup(); } } //--- If we have positions open if(PositionsTotal() > 0) manage_setup(); }
Необходимо определить функцию, ответственную за получение прогноза из наших ONNX-моделей в MQL5.
//+------------------------------------------------------------------+ //| Get a prediction from our model | //+------------------------------------------------------------------+ void model_predict(void) { //--- Moving average inputs float a = (float) ma_f[0]; float b = (float) ma_s[0]; //--- Price quotes float op = (float) iOpen("EURUSD",PERIOD_H1,0); float hi = (float) iHigh("EURUSD",PERIOD_H1,0); float lo = (float) iLow("EURUSD",PERIOD_H1,0); float cl = (float) iClose("EURUSD",PERIOD_H1,0); //--- ONNX inputs vectorf fast_inputs = {op,hi,lo,cl,a}; vectorf slow_inputs = {op,hi,lo,cl,b}; Print("Fast inputs: ",fast_inputs); Print("Slow inputs: ",slow_inputs); //--- Inference OnnxRun(ma_5_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_forecast); OnnxRun(ma_5_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_height_forecast); OnnxRun(ma_60_model,ONNX_DEFAULT,slow_inputs,ma_60_forecast); OnnxRun(ma_60_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_60_height_forecast); }
Последнее внесенное нами изменение влияет на то, каким образом наша стратегия будет выбирать сделки. Вместо того чтобы просто действовать с оглядкой, наша стратегия теперь будет заключать сделки на основе изученной взаимосвязи между ценой и скользящей средней. Наше торговое приложение теперь обладает гибкостью для покупки и продажи, даже если это идет вразрез с предвзятостью, которая, по нашему мнению, существует на рынке.
Обратите внимание, что вызывается новая функция, valid setup, эта функция просто возвращает значение true, если наши условия пробоя верны.
//+---------------------------------------------------------------+ //| Find a setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- I have skipped parts of the code that remained the same if(valid_setup()) { //--- Both models are forecasting rising prices if((c < (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c < (ma_5_forecast[0] + ma_5_height_forecast[0]))) { if(last_trade_state != 1) { Trade.Buy(vol,"EURUSD",ask,0,0,"Volatility Doctor"); initial_sl = channel_low; last_trade_state = 1; last_cross_over_state = current_state; } } //--- Both models are forecasting falling prices if((c > (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c > (ma_5_forecast[0] + ma_5_height_forecast[0]))) { if(last_trade_state != -1) { Trade.Sell(vol,"EURUSD",bid,0,0,"Volatility Doctor"); initial_sl = channel_high; last_trade_state = -1; last_cross_over_state = current_state; } } }
Проверим, не вышли ли мы запределы канала. Если вышли, функция вернет значение true, в противном случае — false.
//+---------------------------------------------------------------+ //| Do we have a valid setup? | //+---------------------------------------------------------------+ bool valid_setup(void) { return(((confirmation == 1) && (bias == -1) && (current_state != last_cross_over_state)) || ((confirmation == 1) && (bias == 1) && (current_state != last_cross_over_state))); }
Я полагаю, что к настоящему времени вы уже ознакомились с настройками, которые мы зададим для бэк-тестирования. Напомним, что важно поддерживать согласованность этих настроек, чтобы мы могли выявить изменения в прибыльности, связанные с изменениями, которые мы вносим в наши торговые правила.
Рис. 20: Некоторые настройки, которые мы будем использовать для бэк-тестирования нашей последней торговой стратегии.
Напомним, что наша модель обучалась только до 2019 года, но наше тестирование начнётся в 2020 году. Поэтому мы тщательно моделируем то, что на самом деле произошло бы, если бы мы разработали эту систему в прошлом.
Рис. 21: Второй пакет настроек, которые мы будем использовать для бэк-тестирования нашей последней торговой стратегии.
Ещё раз, наши настройки одинаковы во всех трех тестах.
Рис. 22: Настройки, которые мы будем использовать для управления нашим приложением в последнем тесте.
Теперь видно в действии на паре EURUSD наше основанное на модели торговое приложение со стратегией пробоев. Напомним, что ни одни из этих данных не были показаны моделям при их обучении.
Рис. 23: Наша окончательная основанная на модели версия стратегии пробоев в действии.
Из приведенного ниже рисунка 23 видно, что нам наконец-то удалось исправить характерный отрицательный наклон, который был у нашей модели с самого начала, и теперь мы становимся более прибыльными.
Рис. 24: Результаты бэк-тестирования в результате тестирования нашей новой основанной на модели стратегии.
Нашей целью было увеличить среднюю прибыль и уменьшить долю убыточных сделок, что мы и сделали. Наш общий убыток составил 498 долларов в первом тесте, 403 доллара во втором, а сейчас он составляет 298 долларов. В то же время наша общая прибыль составила 378 долларов в первом тесте и составляет 341 доллар в этом последнем тесте. Таким образом, очевидно, что внесенные нами изменения привели к сокращению наших общих убытков при сохранении общей прибыли практически на прежнем уровне. В нашей первой системе 70% всех наших сделок были убыточными. Однако при использовании нашей новой системы только 55% всех наших сделок были убыточными.
Рис. 25: Подробные результаты бэк-тестирования нашей основанной на модели стратегии.
Заключение
Пробои - потенциально лучшее время суток для торговли. К задаче, связанной с их корректной идентификацией, не следует относиться легкомысленно. В настоящей статье мы вместе работали над созданием нашей собственной торговой стратегии пробоев. Мы добавили больше фильтров в свою стратегию, чтобы сделать ее более прибыльной. Возможно, это тот случай, когда стратегии пробоев не идеальны для рынка EURUSD, и нам, возможно, придется подойти к этому рынку с другой стороны. Однако для успешного построения торговой стратегии пробоев потребуется больше времени и усилий, чем описано в этой статье, но идеи, которыми мы поделились здесь, могут оказаться полезными на вашем пути к успеху.
Название файла | Описание |
---|---|
MQL5 EURUSD AI | Для построения нашей модели рынка EURUSD использовался ноутбук Jupyter. |
EURUSD MA 60 Model | Модель ONNX использовалась для прогнозирования скользящей средней за 60 периодов. |
EURUSD MA 60 Height Model | Модель ONNX, используемая для прогнозирования разницы между будущей ценой закрытия и будущей скользящей средней за 60 периодов |
EURUSD MA 5 Model | Модель ONNX предназначенная для прогнозирования 5-периодной скользящей средней. |
EURUSD MA 5 Height Model | Модель ONNX, используемая для прогнозирования разницы между будущей ценой закрытия и будущей скользящей средней за 5 периодов |
MTF Channel 2 | Первая реализация нашей стратегии пробоев. |
MTF Channel 2 EURUSD | Вторая реализация нашей стратегии пробоев, которая использовала подтверждение от эталонных пар. |
MTF Channel 2 EURUSD AI | Третья реализация нашей основанной на модели стратегии пробоев. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16569





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования