
Торговая стратегия обратного разрыва справедливой стоимости
Введение
Обратный разрыв справедливой стоимости (IFVG) возникает, когда цена возвращается к ранее выявленному разрыву справедливой стоимости и, вместо того чтобы продемонстрировать ожидаемую поддержку или сопротивление, не справляется с ним. Этот сбой может сигнализировать о потенциальном изменении направления движения рынка и обеспечить противоположное торговое преимущество. В настоящей статье мы представим собственный подход к количественной оценке и использованию обратного разрыва справедливой стоимости в качестве стратегии для советников MetaTrader 5.
Стратегическая мотивация
В первую очередь - понимание концепции разрывов справедливой стоимости (FVG)
Чтобы в полной мере оценить интуитивное понимание "обратного разрыва справедливой стоимости", полезно начать с того, что представляет собой стандартный разрыв справедливой стоимости (FVG). Разрыв справедливой стоимости обычно определяется в рамках ценовой модели из трех свечей.
FVG возникает, когда тело свечи B (и часто фитили) резко толкает рыночную цену вверх или вниз таким образом, что после этого остается “разрыв”. Более конкретно, если минимум свечи С выше максимума свечи А при сильном восходящем движении, промежуток между этими двумя ценовыми точками считается разрывом справедливой стоимости. Этот разрыв отражает зону неэффективности или дисбаланса на рынке — область, где сделки не были должным образом организованы с двух сторон, поскольку цена слишком быстро двигалась в одном направлении. Трейдеры часто предполагают, что поток институциональных ордеров вызвал это смещение, оставив “следы” крупной денежной активности.
Общая логика заключается в том, что цена в какой-то момент часто возвращается к этим разрывам, чтобы “заполнить” их. Заполнение этого разрыва можно рассматривать как способ рынка сбалансировать поток ордеров, который ранее оставался односторонним. Трейдеры, следующие этому принципу, часто ждут, когда цена вновь преодолеет этот разрыв, ожидая реакции, которая подтвердит продолжение движения в первоначальном направлении, а иногда и разворот.
Что такое «Обратный разрыв справедливой стоимости»?
Концепция “обратного разрыва справедливой стоимости” основана на этой идее, но подходит к ней с противоположной точки зрения или с перспективы обратного инжиниринга. Вместо того чтобы использовать разрыв справедливой стоимости в качестве зоны для подтверждения продолжения движения в первоначальном направлении, стратегия обратного FVG может использовать этот самый разрыв, чтобы предвидеть, где рынок может не выдержать и, возможно, развернуться вспять.
Например, чтобы определить обратный медвежий разрыв справедливой стоимости, можно выполнить следующие действия:
- Определить бычий FVG.
- Цена возвращается в зону FVG.
- Вместо того чтобы рассматривать это как поддержку, понаблюдайте за поведением цены. Если она не сможет продвинуться вверх и вместо этого будет торговаться через разрыв, как будто не оказывает значимой поддержки, эта неудача может сигнализировать о смене импульса.
- Занимайте короткие позиции, предвидя, что невозможность использовать FVG в качестве отправной точки для повышения цен означает, что рынок может сейчас пойти вниз.
Интуиция, стоящая за обратным разрывом справедливой стоимости
- Институциональные факторы и точки сбоя: В основе разрывов справедливой стоимости лежит предположение о том, что первоначальный дисбаланс создается крупными, опытными игроками. Когда цена возвращается в эти зоны, это часто становится проверкой: если крупные игроки по-прежнему видят ценность при этих ценах, их отложенные ордера могут поддерживать цену или сопротивляться ей, вызывая реакцию. Если цена вместо этого проходит прямо через FVG без сильного отскока или продолжения, это говорит о том, что эти крупные ордера, возможно, были заполнены, отменены или больше не защищают эту зону. Это может свидетельствовать об изменении намерений рынка.
- Раннее обнаружение слабости или усиления: Сосредоточившись на том, чего не происходит, когда цена возвращается к разрыву, трейдеры могут получить тонкие подсказки о лежащей в основе силе или слабости. Если бычий рынок не может вырваться из известной зоны неэффективности (бычий FVG), это может быть ранним предупреждением о том, что бычий настрой может ослабнуть.
- Дополняет традиционные стратегии FVG: Традиционные стратегии FVG основаны на предположении о восстановлении баланса с последующим продолжением в первоначальном направлении. Однако рынки динамичны, и не каждое заполнение разрывов приводит к возобновлению предыдущего тренда. Обратный подход FVG может дать трейдеру дополнительное “преимущество”, выявляя ситуации, в которых обычные правила игры не работают, и, таким образом, противоположный шаг может иметь более высокую вероятность и соотношение риска/прибыли.
Концепция обратных разрывов справедливой стоимости основана на признании того факта, что рынки постоянно проверяют и перепроверяют области прежнего дисбаланса. В то время как традиционная торговля FVG фокусируется на успешном восстановлении баланса и его продолжении, обратный подход дает преимущество, определяя, когда этот процесс восстановления баланса не приводит к ожидаемому результату. Этот сдвиг в перспективе превращает то, что могло бы быть упущенной возможностью — или даже проигрышным предложением — в противоположную ситуацию с потенциально высокими преимуществами. В рыночной среде, где предвидение неожиданностей часто вознаграждается, концепция обратного FVG добавляет дополнительный инструмент в арсенал методов технического анализа трейдера.
Разработка стратегии
Подобно тому, как дискреционные трейдеры используют разрывы справедливой стоимости, обратные разрывы справедливой стоимости также активно торгуются благодаря сложным критериям, необходимым для выявления достоверных паттернов. Торговля каждым обратным разрывом справедливой стоимости без какой-либо дискриминации, скорее всего, приведет к случайному блужданию, поскольку большинство разрывов не согласуются со стратегической интуицией, о которой я говорил ранее. Стремясь количественно оценить настройки функций, которые учитывают дискреционные трейдеры, я провел обширное тестирование функций и установил следующие правила:
-
Соответствие макротренду: Цена должна следовать за общим макротрендом, который определяется ее положением относительно 400-периодной скользящей средней.
-
Правильный выбор таймфрейма: Следует использовать низкий таймфрейм, например, от 1 до 5 минут, поскольку концепция «исполнения ордеров" реализуется в течение короткого периода времени. Для целей данной статьи используется 3-минутный таймфрейм.
-
Сосредоточьтесь на самом последнем разрыве справедливой стоимости: Рассматривается только самый последний разрыв справедливой стоимости (FVG), поскольку он имеет наибольшее значение для отражения текущих рыночных условий.
-
Проверка размера разрыва справедливой стоимости: FVG не должен быть ни слишком большим, ни слишком маленьким по сравнению с окружающими свечами. Слишком маленький разрыв недостаточно значим, чтобы выступать в качестве надежного уровня поддержки или сопротивления, в то время как слишком большой разрыв, скорее всего, вызван новостным событием, что может задержать сигнал разворота. Чтобы гарантировать, что FVG является значимым, для проверки каждого разрыва установлены определенные пороговые значения.
-
Контролируемый размер свечи пробоя: Аналогично, свеча пробоя не должна быть слишком большой, поскольку входы основаны на закрытиях свечи. Большие свечи пробоя могут привести к запоздалым сигналам, которых стратегия стремится избегать.
-
Своевременный разворот цены и пробой: В течение определенного времени после формирования FVG цена должна развернуться обратно к разрыву и пробиться с противоположного края закрытой свечой. Это достигается за счет изучения только самых последних FVG в течение короткого периода ретроспективы.
-
Подтверждение силы пробоя: FVG должен соответствовать предыдущему уровню отбраковки, гарантируя, что пробой FVG сигнализирует о повышении силы в соответствующем направлении.
Давайте пройдемся по коду.
Во-первых, мы объявляем необходимые глобальные переменные. Эти глобальные переменные содержат ключевые данные для отслеживания разрывов справедливой стоимости (FVG), текущих открытых сделок и состояния системы. Такие переменные, как previousGapHigh, previousGapLow и lastGapIndex, помогают отслеживать самый последний выявленный разрыв. В handleMa будет храниться хэндл скользящей средней. Позиции на покупку и продажу отслеживают открытые торговые тикеты, в то время как currentFVGstatus и newFVGformed отслеживают состояние последнего идентифицированного FVG.
string previousGapObjName = ""; double previousGapHigh = 0.0; double previousGapLow = 0.0; int LastGapIndex = 0; double gapHigh = 0.0; double gapLow = 0.0; double gap = 0.0; double lott= 0.1; ulong buypos = 0, sellpos = 0; double anyGap = 0.0; double anyGapHigh = 0.0; double anyGapLow = 0.0; int barsTotal = 0; int newFVGformed = 0; int currentFVGstatus = 0; int handleMa; #include <Trade/Trade.mqh> CTrade trade;
Далее мы объявляем о следующих функциях для совершения сделок с тейк-профитом и стоп-лоссом, а также для отслеживания тикета ордера на каждую сделку.
//+------------------------------------------------------------------+ //| Store order ticket number into buypos/sellpos variables | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if (trans.type == TRADE_TRANSACTION_ORDER_ADD) { COrderInfo order; if (order.Select(trans.order)) { if (order.Magic() == Magic) { if (order.OrderType() == ORDER_TYPE_BUY) { buypos = order.Ticket(); } else if (order.OrderType() == ORDER_TYPE_SELL) { sellpos = order.Ticket(); } } } } } //+------------------------------------------------------------------+ //| Execute sell trade function | //+------------------------------------------------------------------+ void executeSell() { if (IsWithinTradingHours()){ double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bid = NormalizeDouble(bid,_Digits); double tp = bid - tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = bid + slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Sell(lott,_Symbol,bid,sl,tp); sellpos = trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Execute buy trade function | //+------------------------------------------------------------------+ void executeBuy() { if (IsWithinTradingHours()){ double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); ask = NormalizeDouble(ask,_Digits); double tp = ask + tpp * _Point; tp = NormalizeDouble(tp, _Digits); double sl = ask - slp * _Point; sl = NormalizeDouble(sl, _Digits); trade.Buy(lott,_Symbol,ask,sl,tp); buypos= trade.ResultOrder(); } } //+------------------------------------------------------------------+ //| Check if is trading hours | //+------------------------------------------------------------------+ bool IsWithinTradingHours() { datetime currentTime = TimeTradeServer(); MqlDateTime timeStruct; TimeToStruct(currentTime, timeStruct); int currentHour = timeStruct.hour; if (currentHour >= startHour && currentHour < endHour) return true; else return false; }
Затем мы используем эти две функции для проверки разрыва справедливой стоимости. IsReacted() проверяет, что в течение периода ретроспективного анализа в ценовом диапазоне текущего FVG есть по крайней мере две свечи с фитилями, которые мы интерпретируем как признак предыдущего отклонения FVG. IsGapValid() затем проверяет, находится ли размер разрыва в пределах желаемого диапазона, возвращая значение true или false.
//+------------------------------------------------------------------+ //| Function to validate the FVG gap | //+------------------------------------------------------------------+ bool IsGapValid(){ if (anyGap<=gapMaxPoint*_Point && anyGap>=gapMinPoint*_Point&&IsReacted()) return true; else return false; } //+------------------------------------------------------------------+ //| Check for gap reaction to validate its strength | //+------------------------------------------------------------------+ bool IsReacted(){ int count1 = 0; int count2 = 0; for (int i = 4; i < lookBack; i++){ double aLow = iLow(_Symbol,PERIOD_CURRENT,i); double aHigh = iHigh(_Symbol,PERIOD_CURRENT,i); if (aHigh<anyGapHigh&&aHigh>anyGapLow&&aLow<anyGapLow){ count1++; } else if (aLow<anyGapHigh&&aLow>anyGapLow&&aHigh>anyGapHigh){ count2++; } } if (count1>=2||count2>=2) return true; else return false; }
После этого мы используем эти функции, чтобы проверить, есть ли в данный момент пробой на последнем FVG.
//+------------------------------------------------------------------+ //| Check if price broke out to the upside of the gap | //+------------------------------------------------------------------+ bool IsBrokenUp(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen < gapHigh && lastClose > gapHigh&&(lastClose-gapHigh)<maxBreakoutPoints*_Point) { if(currentFVGstatus==-1){ return true;} } return false; } //+------------------------------------------------------------------+ //| Check if price broke out to the downside of the gap | //+------------------------------------------------------------------+ bool IsBrokenLow(){ int lastClosedIndex = 1; double lastOpen = iOpen(_Symbol, PERIOD_CURRENT, lastClosedIndex); double lastClose = iClose(_Symbol, PERIOD_CURRENT, lastClosedIndex); if (lastOpen > gapLow && lastClose < gapLow&&(gapLow -lastClose)<maxBreakoutPoints*_Point) { if(currentFVGstatus==1){ return true;} } return false; }
Наконец, мы используем эти две функции для проверки допустимости разрывов с помощью IsGapValid() и, если они допустимы, она обновляет глобальные переменные, отмечает FVG как новый и отображает его на графике. Функция getFVG() необходима для кодирования всей стратегии. Мы вызываем её на каждом новом баре, чтобы проверить, есть ли действительный FVG. Если FVG действителен, проверяем, отличается ли он от последнего, который мы сохранили, и если да, то сохраняем его в глобальной переменной, чтобы обновить статус.
//+------------------------------------------------------------------+ //| To get the most recent Fair Value Gap (FVG) | //+------------------------------------------------------------------+ void getFVG() { // Loop through the bars to find the most recent FVG for (int i = 1; i < 3; i++) { datetime currentTime = iTime(_Symbol,PERIOD_CURRENT, i); datetime previousTime = iTime(_Symbol,PERIOD_CURRENT, i + 2); // Get the high and low of the current and previous bars double currentLow = iLow(_Symbol,PERIOD_CURRENT, i); double previousHigh = iHigh(_Symbol,PERIOD_CURRENT, i+2); double currentHigh = iHigh(_Symbol,PERIOD_CURRENT, i); double previousLow = iLow(_Symbol,PERIOD_CURRENT, i+2); anyGap = MathAbs(previousLow - currentHigh); // Check for an upward gap if (currentLow > previousHigh) { anyGapHigh = currentLow; anyGapLow = previousHigh; //Check for singular if (LastGapIndex != i){ if (IsGapValid()){ gapHigh = currentLow; gapLow = previousHigh; gap = anyGap; currentFVGstatus = 1;//bullish FVG DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } // Check for a downward gap else if (currentHigh < previousLow) { anyGapHigh = previousLow; anyGapLow = currentHigh; if (LastGapIndex != i){ if(IsGapValid()){ gapHigh = previousLow; gapLow = currentHigh; gap = anyGap; currentFVGstatus = -1; DrawGap(previousTime,currentTime,gapHigh,gapLow); LastGapIndex = i; newFVGformed =1; return; } } } } } //+------------------------------------------------------------------+ //| Function to draw the FVG gap on the chart | //+------------------------------------------------------------------+ void DrawGap(datetime timeStart, datetime timeEnd, double gaphigh, double gaplow) { // Delete the previous gap object if it exists if (previousGapObjName != "") { ObjectDelete(0, previousGapObjName); } // Generate a new name for the gap object previousGapObjName = "FVG_" + IntegerToString(TimeCurrent()); // Create a rectangle object to highlight the gap ObjectCreate(0, previousGapObjName, OBJ_RECTANGLE, 0, timeStart, gaphigh, timeEnd, gaplow); // Set the properties of the rectangle ObjectSetInteger(0, previousGapObjName, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, previousGapObjName, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, previousGapObjName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, previousGapObjName, OBJPROP_RAY, false); // Update the previous gap information previousGapHigh = gaphigh; previousGapLow = gaplow; }
И мы интегрируем все правила стратегии вместе в функцию OnTick(), вот так, и готово.
//+------------------------------------------------------------------+ //| OnTick function | //+------------------------------------------------------------------+ void OnTick() { int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars; double ma[]; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); CopyBuffer(handleMa,BASE_LINE,1,1,ma); if (IsBrokenLow()&&sellpos == buypos&&newFVGformed ==1&&bid<ma[0]){ executeSell(); newFVGformed =0; } else if (IsBrokenUp()&&sellpos == buypos&&newFVGformed ==1&&ask>ma[0]){ executeBuy(); newFVGformed =0; } getFVG(); if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; } } }
Некоторые дополнительные замечания: мы вызываем её в начале функции OnTick(), чтобы она обрабатывала остальные строки только после формирования нового бара. Эта мера экономит вычислительные мощности.
int bars = iBars(_Symbol,PERIOD_CURRENT); if (barsTotal!= bars){ barsTotal = bars;
Кроме того, поскольку нам нужна только одна сделка за раз, мы можем открыть сделку только тогда, когда для обоих тикетов установлено значение 0 с помощью этой логики, которая проверяет, нет ли текущих позиций, открытых данным конкретным советником.
if(buypos>0&&(!PositionSelectByTicket(buypos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ buypos = 0; } if(sellpos>0&&(!PositionSelectByTicket(sellpos)|| PositionGetInteger(POSITION_MAGIC) != Magic)){ sellpos = 0; }
Краткое резюме:
- Глобальные заявления и вводные данные: Настройка среды, переменных и настраиваемых пользователем параметров.
- Инициализация (OnInit): Подготовка фильтра скользящих средних и установка магических чисел.
- Логика OnTick: Основной рабочий процесс — проверка новых баров, обнаружение FVG, проверка пробоев и совершение сделок при соблюдении условий.
- Обнаружение FVG (getFVG, IsGapValid, IsReacted): Выявление и подтверждение разрывов справедливой стоимости и реакции рынка на них.
- Проверки разрывов (IsBrokenUp, IsBrokenLow): Подтверждение направления пробоев для входа в сделки.
- Управление сделками (OnTradeTransaction, executeBuy, executeSell): Обработка тикетов ордеров и гарантирование корректного размещения сделок.
- Составление графиков (DrawGap): Визуализация идентифицированных FVG.
- Временные фильтры (IsWithinTradingHours): Ограничение торговли определенными часами.
Тестирование стратегии
Стратегия лучше всего работает на фондовых индексах из-за их относительно низких спредов и высокой волатильности, что выгодно для розничной внутридневной торговли. Мы протестируем эту стратегию, торгуя индексом Nasdaq 100 с 1 января 2020 года по 1 декабря 2024 года на 3-минутном таймфрейме (M3). Вот параметры, выбранные нами для этой стратегии.
Вот несколько рекомендаций по выбору значений параметров для стратегии:
- Устанавливайте время торговли в периоды высокой волатильности рынка, как правило, когда фондовый рынок открыт. Этот период зависит от серверного времени вашего брокера. Например, по времени моего сервера (GMT+0) фондовый рынок открыт примерно с 14:00 до 19:00.
- Рекомендуется использовать соотношение прибыли к риску, превышающее единицу, поскольку мы движемся в русле макроэкономического тренда на крайне волатильном рынке. Кроме того, избегайте установки слишком высоких или слишком низких уровней тейк-профита и стоп-лосса (TPSL). Если уровень TPSL слишком велик, он не сможет эффективно улавливать краткосрочные сигналы паттернов, а если он слишком мал, то спреды могут негативно сказаться на сделке.
- Не следует чрезмерно настраивать значения порогов разрыва, порогов свечей пробоя и ретроспективного периода. Во избежание переобучения поддерживайте эти параметры в разумных пределах относительно ценового диапазона торгуемой ценной бумаги.
Ниже представлен результат бэк-тестирования:
Мы видим, что за последние пять лет стратегия работала очень стабильно, что свидетельствует о ее потенциальной прибыльности.
Типичная сделка в части визуализации тестера стратегий выглядела бы примерно так:
Я призываю читателей опираться на эту стратегию и проявлять творческий подход к ее совершенствованию. Вот некоторые из моих предложений:
- Сила IFVG определяется количеством паттернов rejection candle вокруг области FVG. Разницу в этих цифрах можно использовать, как правило, для оценки.
- В настоящей статье мы сосредоточились только на максимальных точках пробоя. Однако иногда свеча пробоя может быть слишком маленькой, указывая на слабую силу пробоя. Это может негативно сказаться на продолжении тренда. Вы также можете рассмотреть возможность добавления порогового значения для точек минимального пробоя.
- Правило выхода определяется тейк-профитом и стоп-лоссом. В качестве альтернативы вы можете установить уровень выхода на основе соответствующих ключевых уровней для обоих направлений за заданный ретроспективный период или установить фиксированное время выхода.
Заключение
В настоящей статье мы представили собственный подход к количественной оценке и использованию обратного разрыва справедливой стоимости в качестве стратегии для советников MetaTrader 5, охватывая вопросы мотивации, разработки и тестирования стратегии. Данная стратегия демонстрирует высокий потенциал прибыльности, стабильно работая на протяжении последних пяти лет с более чем 400 сделками. Для адаптирования этой стратегии к различным ценным бумагам и таймфреймам, в неё можно вносить дальнейшие изменения. Полный код приведен ниже и вы можете интегрировать его в собственные торговые разработки.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16659
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





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