English Deutsch 日本語
preview
Рыночные секреты Ларри Уильямса (Часть 9): Торговые паттерны для получения прибыли

Рыночные секреты Ларри Уильямса (Часть 9): Торговые паттерны для получения прибыли

MetaTrader 5Торговые системы |
70 0
Chacha Ian Maroa
Chacha Ian Maroa

Введение

Рынки всегда побуждали трейдеров искать смысл в движении цены. За десятилетия бесчисленные графические паттерны получили названия, были классифицированы; их описывали и преподавали так, будто они обладают универсальной прогностической силой. "Голова и плечи", треугольники, флаги и множество других вариантов сформировали то, как многие трейдеры интерпретируют графики.

Ларри Уильямс оспаривает эту традицию. Он не отвергает паттерны полностью, но ставит под сомнение их практическую ценность, когда они расплывчаты, субъективны и растянуты на длинные горизонты. Классические формации часто требуют субъективной трактовки, нередко распознаются лишь задним числом и разворачиваются в периоды, когда слишком много переменных может вмешаться в результат. К тому моменту, когда долгосрочный паттерн завершается, рыночная среда уже может измениться.

Вместо этого Ларри Уильямс сосредотачивается на краткосрочном поведении цены. Краткосрочные паттерны быстро формируются, быстро отрабатываются и могут быть измерены с высокой точностью. Они строятся на простых соотношениях между ценами открытия, закрытия, максимумами и минимумами. Они отражают текущие эмоции рынка, а не долгосрочное мнение. Самое важное — их можно объективно тестировать. Этот переход от визуальных рыночных «историй» к измеримому поведению меняет всё. Когда паттерны четко определены, их можно преобразовать в правила. Когда правила точны, их можно автоматизировать. Когда автоматизация возможна, эффективность можно изучать на тысячах сделок, а не вспоминать по нескольким примерам.

В этой статье мы начинаем этот процесс. Мы берем несколько краткосрочных паттернов, представленных в его книге "Долгосрочные секреты краткосрочной торговли", и превращаем их в исследовательский торговый советник. Каждый паттерн реализован как независимая стратегия. Каждую стратегию можно тестировать отдельно. Каждая сделка управляется с использованием согласованных правил риска и выхода. Цель не в том, чтобы доказать совершенство какого-либо одного паттерна, а в том, чтобы понять, как структура, эмоция, время и волатильность взаимодействуют в реальных рыночных данных.

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

От теории к реализации, от паттернов к прибыли — теперь мы начинаем строить исследовательский механизм краткосрочных паттернов в MQL5. 


Краткосрочные паттерны, выбранные для автоматизации

Почему эти паттерны важны

Ларри Уильямс не искал сложные формации. Он искал простые события, отражающие краткосрочную эмоцию и истощение. Каждый описанный здесь паттерн быстро формируется, быстро отрабатывается и может быть измерен без интерпретации. Именно это делает их пригодными для автоматизации.

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

Покупка на открытии

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

Эта стратегия не пытается предсказывать направление. Она просто измеряет, демонстрирует ли рынок естественную склонность к росту с течением времени. Она служит эталоном для сравнения со всеми остальными стратегиями. Если более избирательные паттерны превосходят это простое правило, значит, дополнительная логика оправдана.

Покупка после закрытия вниз

Этот паттерн вводит первый эмоциональный фильтр. Мы покупаем только тогда, когда предыдущий день закрылся ниже, чем открылся. Идея проста. Одно закрытие вниз часто отражает краткосрочное давление продавцов. На растущем рынке это может быть всего лишь короткая пауза. Покупая после закрытия вниз, мы пытаемся войти во время небольшой коррекции, а не на силе. Цель — проверить, улучшает ли умеренная слабость качество входа.

Покупка после трех последовательных закрытий вниз

Этот паттерн развивает ту же идею. Вместо одного нисходящего бара мы требуем несколько последовательных медвежьих баров. Ларри Уильямс заметил, что короткие серии продаж часто быстро истощаются. Когда три или более закрытия вниз появляются подряд, рынок уже может находиться рядом с краткосрочной точкой разворота.

Здесь мы покупаем только после заданного количества медвежьих баров. Этот паттерн проверяет, создает ли повторяющаяся слабость возможность, а не опасность.

Покупка после объективно измеренного отката

Это самый структурированный трендовый паттерн. Сначала мы подтверждаем, что цена находится выше закрытия бара, расположенного далеко в прошлом. Это устанавливает восходящий тренд. Затем мы подтверждаем, что цена находится ниже закрытия более близкого бара. Это определяет контролируемый откат. Когда оба условия выполнены, мы покупаем при следующем допустимом входе.

Этот паттерн избегает визуальных оценок. Тренд и откат измеряются с помощью фиксированных опорных баров. Логика остается одинаковой на всех рынках и во всех тестах. Она отражает одну из важнейших идей Ларри Уильямса: сильные тренды часто вознаграждают тех, кто покупает слабость, а не силу.

Покупка после медвежьего внешнего бара

Это классический пример эмоционального истощения. Внешний бар выходит за пределы максимума и минимума предыдущего дня. Когда такой бар закрывается около своего минимума, рынок выглядит крайне медвежьим. Страх виден на графике. Большинство трейдеров интерпретируют это как сигнал к продаже.

Ларри Уильямс заметил обратное. Во многих случаях этот паттерн отмечает конец паники, а не ее начало. Когда продажи достигают экстремума, покупатели часто вступают на следующий день. Здесь мы покупаем после медвежьего внешнего бара с закрытием ниже предыдущего минимума.

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

Продажа против трех последовательных бычьих закрытий

Это единственная короткая стратегия в группе. Мы ищем три последовательных бычьих закрытия. Такие последовательности часто привлекают поздних покупателей. Сила становится заметной. Оптимизм растет. Вместо того чтобы присоединяться к движению, мы продаем против этого импульса. Когда цена показывает потерю импульса и пересекает наш уровень входа вниз, мы продаем.

Этот паттерн проверяет, означает ли краткосрочная сила чаще истощение, а не продолжение.

Каждый паттерн можно торговать во все дни или только в выбранные дни. Ларри Уильямс показал, что многие краткосрочные паттерны ведут себя очень по-разному в течение недели. Понедельник часто отражает реакцию на новости выходных. Пятница часто отражает сокращение позиций. Четверг часто показывает давление перед завершением недели. Включая фильтр торгового дня, мы можем измерить, как каждая стратегия работает в конкретные дни. Это позволяет изучать не только поведение цены, но и временные закономерности.

Эти паттерны представляют разные силы. Одни следуют слабости внутри силы. Другие работают против эмоционального экстремума. Одни ориентированы на продолжение движения, другие — на истощение импульса. Помещая их в единую систему с одной и той же моделью риска и правилами выхода, мы можем сравнивать их справедливо.

Цель не в том, чтобы объявить победителя. Цель — понять, какие идеи выдерживают столкновение с историческими данными, а какие остаются привлекательными только на бумаге. Определив паттерны, мы теперь приступаем к техническому построению советника, чтобы тестировать их дисциплинированно и ясно.


Основные торговые правила и архитектура системы

Единая система с одной активной стратегией

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

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

Логика входа и подтверждение на основе волатильности

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

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

Настраиваемое размещение стоп-лосса

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

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

Гибкие модели фиксации прибыли

Логика выхода играет центральную роль в этой системе. Мы поддерживаем три режима фиксации прибыли. В первом режиме позиция закрывается на первом открытии нового бара, показывающем плавающую прибыль. Быстрая реакция этой модели отражает предпочтение Ларри Уильямса к быстрым выходам.

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

В третьем режиме тейк-профит рассчитывается с использованием фиксированного соотношения риск/прибыль. Расстояние до стопа определяет риск. Расстояние до цели рассчитывается как кратное этому риску.

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

Фильтрация по торговым дням

Каждый сигнал можно фильтровать по дню недели. Мы можем разрешить торговлю во все дни или ограничить исполнение только выбранными днями. Это правило применяется до открытия любой сделки. Фильтр позволяет изучать временные закономерности независимо от поведения цены. Один и тот же паттерн может работать очень по-разному в разные дни. Изолируя эти эффекты, мы получаем понимание, когда паттерн наиболее силен и когда его следует избегать.

Размер позиции и контроль риска

Размер позиции может задаваться двумя способами. В ручном режиме для каждой сделки используется фиксированный объем лота. В автоматическом режиме размер позиции рассчитывается как процент от баланса счета на основе расстояния до стоп-лосса. Это сохраняет риск пропорциональным и стабильным по мере изменения капитала.

Это правило гарантирует, что все стратегии тестируются при одинаковых условиях риска. Различия в эффективности тогда отражают качество логики, а не артефакты расчета размера позиции.

Дисциплина исполнения и правила безопасности

Все ценовые уровни пересчитываются только на открытии каждого нового бара. Сделка не открывается, если какая-либо позиция от этого советника уже активна. Все ордера помечаются уникальным magic number, чтобы предотвратить вмешательство других систем. Эти меры защиты предотвращают чрезмерную торговлю, конфликтующие сигналы и непреднамеренное наращивание позиций.

Философия проектирования

Архитектура следует простому принципу.

  • Одна идея за раз.
  • Одна сделка за раз.
  • Одна модель риска за раз.

Каждый компонент изолирован, настраиваем и пригоден для тестирования.

Теперь, когда правила четко определены, мы можем перейти к техническому построению советника и преобразовать эти концепции в рабочий код MQL5.


Закладываем основу для советника

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

Что нужно, чтобы работать с материалом по ходу статьи

Чтобы получить максимальную пользу от этого раздела, требуется несколько базовых навыков. Во-первых, важно уверенное знакомство с языком MQL5. Основные понятия, такие как переменные, функции, условные операторы, циклы, перечисления и использование стандартных библиотек, уже должны быть привычными. Если эти темы пока новы, официальная справка MQL5 станет отличной отправной точкой перед продолжением.

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

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

Работа вместе с эталонной реализацией

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

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

Создание начального исходного файла

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

//+------------------------------------------------------------------+
//|                                           lwPatternsToprofit.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+

enum ENUM_LW_STRATEGY_MODE
{
   LW_STRATEGY_BASELINE_BUY_OPEN,
   LW_STRATEGY_CONSECUTIVE_BEARISH_BARS,
   LW_STRATEGY_UPTREND_WITH_PULLBACK,
   LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE,
   LW_STRATEGY_THIRD_BULLISH_DAY_FADE
};

enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_BAR_CLOSE_STATE
{
   BAR_CLOSE_UP, 
   BAR_CLOSE_DOWN
};

enum ENUM_STOP_LOSS_MODE
{
   SL_BY_RANGE_PERCENT,
   SL_AT_PREVIOUS_BAR
};

enum ENUM_TAKE_PROFIT_MODE
{
   TP_FIRST_PROFITABLE_OPEN,
   TP_AFTER_N_CANDLES,
   TP_BY_RISK_REWARD 
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber         = 254700680002;                 
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;

input group "Strategy Configuration"
input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN;
input int requiredConsecutiveBearishBars   = 3;
input int requiredConsecutiveBullishBars   = 3;
input int uptrendLookbackBars              = 30;
input int pullbackLookbackBars             = 9;

input group "Volatility Breakout Parameters"
input double inpBuyRangeMultiplier   = 0.50;   
input double inpSellRangeMultiplier  = 0.50;   
input double inpStopRangeMultiplier  = 0.50;

input group "TDW filter"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE stopLossMode      = SL_BY_RANGE_PERCENT;
input ENUM_TAKE_PROFIT_MODE takeProfitMode  = TP_BY_RISK_REWARD;
input double riskRewardRatio                = 3.0;
input int exitAfterCandles                  = 3;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- To hep track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

//--- Holds all price levels derived from Larry Williams' volatility breakout calculations
struct MqlLwVolatilityLevels
{
   double yesterdayRange;      
   double buyEntryPrice;       
   double sellEntryPrice;   
   double bullishStopLoss;   
   double bearishStopLoss;    
   double bullishTakeProfit;
   double bearishTakeProfit;
   double bullishStopDistance;
   double bearishStopDistance;
};

MqlLwVolatilityLevels lwVolatilityLevels;

//--- To store minutes data
double closePriceMinutesData [];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Reset Larry Williams' volatility levels 
   ZeroMemory(lwVolatilityLevels);
   
   //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar)
   ArraySetAsSeries(closePriceMinutesData, true);

   return(INIT_SUCCEEDED);
}
  
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();
   
   //--- Get some minutes data
   if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){
      Print("Error while copying minutes datas ", GetLastError());
      return;
   }
}

//--- UTILITY FUNCTIONS
  
//+------------------------------------------------------------------+

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

Идентификатор программы и базовые свойства

//+------------------------------------------------------------------+
//|                                           lwPatternsToprofit.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"

Начальные строки задают имя программы, информацию об авторе и номер версии. Эти свойства документируют происхождение исходного файла и позволяют платформе идентифицировать советник во время тестирования и исполнения. На этом этапе логика не вводится. Мы просто устанавливаем формальный идентификатор программы.

Подключение торговой библиотеки

Далее подключается торговая библиотека.

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

Эта библиотека предоставляет CTrade — класс, который предоставляет надежный и единообразный интерфейс для открытия и закрытия позиций, изменения ордеров и чтения результатов исполнения.

Определение перечислений стратегии и конфигурации

Теперь мы вводим набор перечислений, описывающих все выбираемые варианты поведения системы.

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+

enum ENUM_LW_STRATEGY_MODE
{
   LW_STRATEGY_BASELINE_BUY_OPEN,
   LW_STRATEGY_CONSECUTIVE_BEARISH_BARS,
   LW_STRATEGY_UPTREND_WITH_PULLBACK,
   LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE,
   LW_STRATEGY_THIRD_BULLISH_DAY_FADE
};

enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_BAR_CLOSE_STATE
{
   BAR_CLOSE_UP, 
   BAR_CLOSE_DOWN
};

enum ENUM_STOP_LOSS_MODE
{
   SL_BY_RANGE_PERCENT,
   SL_AT_PREVIOUS_BAR
};

enum ENUM_TAKE_PROFIT_MODE
{
   TP_FIRST_PROFITABLE_OPEN,
   TP_AFTER_N_CANDLES,
   TP_BY_RISK_REWARD 
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

Перечисление стратегий перечисляет каждый торговый паттерн, который можно тестировать. В любой момент активной будет только одна стратегия. Такая конструкция позволяет тестировать каждый паттерн независимо и объективно сравнивать их. Перечисление режима стоп-лосса определяет, как размещаются защитные выходы. Перечисление режима тейк-профита определяет, как будет фиксироваться прибыль. Перечисление режима размера лота определяет, как рассчитывается объем позиции. Перечисление состояния закрытия бара дает удобный способ классифицировать направление свечи. Перечисление режима торгового дня готовит систему к опциональной фильтрации по дням. Эти перечисления формируют основу конфигурационной модели. Они превращают торговые идеи в структурированные состояния, которые программа может обрабатывать системно.

Входные параметры пользователя и элементы управления стратегией

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber         = 254700680002;                 
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;

input group "Strategy Configuration"
input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN;
input int requiredConsecutiveBearishBars   = 3;
input int requiredConsecutiveBullishBars   = 3;
input int uptrendLookbackBars              = 30;
input int pullbackLookbackBars             = 9;

input group "Volatility Breakout Parameters"
input double inpBuyRangeMultiplier   = 0.50;   
input double inpSellRangeMultiplier  = 0.50;   
input double inpStopRangeMultiplier  = 0.50;

input group "TDW filter"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE stopLossMode      = SL_BY_RANGE_PERCENT;
input ENUM_TAKE_PROFIT_MODE takeProfitMode  = TP_BY_RISK_REWARD;
input double riskRewardRatio                = 3.0;
input int exitAfterCandles                  = 3;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;

Далее мы определяем все настраиваемые пользователем входные параметры. Этот раздел представляет публичный интерфейс советника. Каждый объявленный здесь параметр становится видимым в платформе и может быть изменен без редактирования исходного кода. Группа конфигурации стратегии позволяет выбрать активный паттерн и тонко настроить специфические значения паттернов, например количество последовательных баров, период опорного трендового сравнения и период опорного сравнения для отката. Группа параметров волатильностного пробоя управляет тем, как расстояния входа и стопа рассчитываются на основе диапазона предыдущего бара. Группа фильтра торговых дней определяет, в какие дни недели допускается исполнение. Группа управления сделками и риском определяет логику стоп-лосса и фиксации прибыли, правила расчета размера позиции и риск.

На этом этапе мы еще не применяем никакую логику. Мы только описываем полный набор управляющих параметров системы, которая позже будет направлять каждое торговое решение.

Создание основных торговых объектов и контейнеров цен

Теперь мы создаем CTrade — объект, который будет управлять всеми торговыми операциями.

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

Этот объект становится единым каналом исполнения для открытия и закрытия позиций. Каждый ордер, отправленный программой, пройдет через этот интерфейс.

Затем мы объявляем переменные для хранения текущей цены Ask, цены Bid и текущего времени.

//--- To hep track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

Эти значения будут обновляться на каждом тике и позже послужат базовыми входными данными для всех расчетов, основанных на цене.

Подготовка структуры уровней волатильности

Центральной концепцией этой системы является проекция уровней входа, стопа и цели на основе недавних рыночных диапазонов. Чтобы аккуратно управлять этими значениями, мы определяем структуру, которая хранит все ценовые уровни, полученные из волатильности.

//--- Holds all price levels derived from Larry Williams' volatility breakout calculations
struct MqlLwVolatilityLevels
{
   double yesterdayRange;      
   double buyEntryPrice;       
   double sellEntryPrice;   
   double bullishStopLoss;   
   double bearishStopLoss;    
   double bullishTakeProfit;
   double bearishTakeProfit;
   double bullishStopDistance;
   double bearishStopDistance;
};

MqlLwVolatilityLevels lwVolatilityLevels;

Эта структура хранит диапазон предыдущего бара, спроецированные цены входа, спроецированные цены стопа, спроецированные цены тейк-профита и измеренные расстояния до стопа.

Группируя эти значения в единую структуру, мы сохраняем связанные данные организованными и удобными для сопровождения. Позднее расчеты смогут обновлять эту структуру один раз на бар, а логика входа и выхода сможет читать данные из нее.

Подготовка отслеживания внутридневных цен

Мы также объявляем массив для хранения последних цен закрытия на одноминутном таймфрейме.

//--- To store minutes data
double closePriceMinutesData [];

Позже этот массив позволит точно обнаруживать события пересечения вверх и пересечения вниз относительно спроецированных уровней входа. Без этого массива сигналы входа задерживались бы до закрытия бара, теряя предполагаемую точность по времени.

Во время инициализации массив настраивается как временной ряд, так что индекс ноль всегда относится к самой последней цене.

Инициализация советника

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Reset Larry Williams' volatility levels 
   ZeroMemory(lwVolatilityLevels);
   
   //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar)
   ArraySetAsSeries(closePriceMinutesData, true);

   return(INIT_SUCCEEDED);
}

Внутри функции инициализации мы выполняем три важных действия. Во-первых, назначаем торговому объекту уникальный magic number. Это позволяет программе позже определить, какие позиции принадлежат именно этому экземпляру советника. Во-вторых, обнуляем структуру уровней волатильности. Это гарантирует, что при появлении первого бара не будут использоваться неинициализированные значения. В-третьих, настраиваем массив цен как временной ряд. Это обеспечивает согласованную индексацию цен во всей программе.

На этом этапе советник полностью инициализирован, но еще не содержит торгового интеллекта. Теперь он готов безопасно получать тики и сохранять рыночные данные.

Деинициализация и завершение программы

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

Функция деинициализации выполняет простую задачу отчетности. Когда программа прекращает работу, она выводит код причины завершения, предоставленный платформой. Эта информация полезна при тестировании для диагностики неожиданных остановок или ручного удаления.

Начальный обработчик тиков

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();
   
   //--- Get some minutes data
   if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){
      Print("Error while copying minutes datas ", GetLastError());
      return;
   }
}

Первая версия функции обработки тика выполняет только две задачи. Она обновляет текущие цены Bid и Ask, а также текущее время. Она копирует последние одноминутные цены закрытия во внутридневной массив. Торговые решения еще не принимаются. Сигналы не оцениваются. Ордера не отправляются. Такая простота намеренна. Перед вводом логики стратегии мы сначала подтверждаем, что ценовые данные обновляются корректно, время синхронизировано правильно, а внутридневные массивы заполняются без ошибок. Только после устойчивости этой основы мы начинаем накладывать обнаружение паттернов, фильтры и правила исполнения.

На этом этапе советник не содержит торговой логики, но уже выражает полную архитектуру системы. Мы определили все стратегии, схемы выхода, фильтры, средства контроля риска и основные контейнеры данных. Каждая будущая функция теперь естественно присоединится к существующей структуре.

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


Обнаружение паттернов и проекция уровней волатильности

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

На этом этапе цель пока не в открытии сделок, а в построении надежной среды, в которой действительные возможности могут быть обнаружены, измерены, отфильтрованы и позже исполнены с точностью.

Почему важно обнаружение нового бара

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

По этой причине программе нужен надежный способ обнаруживать открытие нового бара на выбранном таймфрейме. Мы вводим небольшую служебную функцию, единственная роль которой — обнаруживать это событие.

//+------------------------------------------------------------------+
//| Function to check if there's a new bar on a given chart timeframe|
//+------------------------------------------------------------------+
bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm){

   datetime currentTm = iTime(symbol, tf, 0);
   if(currentTm != lastTm){
      lastTm       = currentTm;
      return true;
   }  
   return false;
}

Функция сравнивает время открытия текущего бара с сохраненным временем открытия предыдущего бара. Когда эти значения различаются, сформировался новый бар. Третий параметр передается по ссылке, чтобы функция могла обновить сохраненное время внутри себя. Это предотвращает повторное обнаружение и гарантирует, что логика выполняется только один раз на бар.

Для поддержки этого механизма мы определяем глобальную переменную, которая хранит время открытия последнего бара.

//--- To help track new bar open
datetime lastBarOpenTime;

Во время инициализации она устанавливается в ноль, чтобы самый первый бар был обнаружен правильно.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- Initialize global variables
   lastBarOpenTime = 0;

   return(INIT_SUCCEEDED);
}

Этот единственный инструмент становится сердцебиением всей системы. Каждая проекция волатильности, обновление счетчика баров и ежедневный сброс исходят из этого момента.

Отслеживание последовательных закрытий свечей

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

//+------------------------------------------------------------------+
//| Checks whether the last N completed bars all closed              |
//| either up or down relative to their open                         |
//+------------------------------------------------------------------+
bool IsConsecutiveBarCloseState(string symbol, ENUM_TIMEFRAMES tf, int barsToCheck, ENUM_BAR_CLOSE_STATE closeState)
{
   // Start from bar index 1 (last fully closed bar)
   for(int i = 1; i <= barsToCheck; i++){
   
      double openPrice  = iOpen (symbol, timeframe, i);
      double closePrice = iClose(symbol, timeframe, i);

      // Safety check (in case of missing data)
      if(openPrice == 0.0 || closePrice == 0.0){
         return false;
      }

      // Validate close direction
      if(closeState == BAR_CLOSE_UP && closePrice <= openPrice){
         return false;
      }         

      if(closeState == BAR_CLOSE_DOWN && closePrice >= openPrice){
         return false;
      }      
   }
   return true;
}

Функция начинает с индекса бара один, то есть с последнего полностью закрытого бара. Для каждого бара она сравнивает цену закрытия с ценой открытия. Если какой-либо бар нарушает требуемое направление, паттерн сразу отклоняется. Если все бары удовлетворяют условию, функция подтверждает последовательность. Эта функция превращает визуальную графическую идею в объективное правило, которое программа может тестировать без неоднозначности.

Обнаружение восходящего тренда с измеренным откатом

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

Функция, реализующая эту логику, получает расстояния просмотра назад как параметры.

//+------------------------------------------------------------------+
//| Detects an uptrend with a small pullback based on Larry Williams'|
//| logic using historical reference bars                            |
//+------------------------------------------------------------------+
bool IsUptrendWithPullback(string symbol,
                           ENUM_TIMEFRAMES tf,
                           int index,
                           int trendLookback,
                           int pullbackLookback)
{
   //--- Today's open
   double todayOpen = iOpen(symbol, tf, index);

   //--- Reference closes
   double closeTrendBar    = iClose(symbol, tf, index + trendLookback);
   double closePullbackBar = iClose(symbol, tf, index + pullbackLookback);

   //--- Validate data
   if(todayOpen == 0.0 || closeTrendBar == 0.0 || closePullbackBar == 0.0)
      return false;

   //--- Condition 1: Uptrend confirmation
   bool isInUptrend = (todayOpen > closeTrendBar);

   if(!isInUptrend)
      return false;

   //--- Condition 2: Pullback confirmation
   bool isPullback = (todayOpen < closePullbackBar);

   if(!isPullback)
      return false;

   return true;
}

Это делает паттерн полностью настраиваемым и пригодным для тестирования в разных рыночных условиях. Такой подход устраняет мнение из процесса. Тренд и откат больше не являются визуальными догадками, а становятся строгими ценовыми соотношениями.

Обнаружение эмоциональных продаж через внешние бары

Еще один важный паттерн, описанный Ларри Уильямсом, — медвежий внешний бар с закрытием вниз. Эта формация фиксирует момент паники, когда цена расширяется в обе стороны, но закрывается около минимума. Функция обнаружения требует выполнения трех условий.

//+-----------------------------------------------------------------------+
//| Detects an outside bar with a bearish close relative to the prior bar |
//+-----------------------------------------------------------------------+
bool IsOutsideBarWithDownClose(string symbol, ENUM_TIMEFRAMES tf, int index){
   
   //--- Current bar data
   double open0  = iOpen (symbol, tf, index);
   double high0  = iHigh (symbol, tf, index);
   double low0   = iLow  (symbol, tf, index);
   double close0 = iClose(symbol, tf, index);

   //--- Previous bar data
   double high1 = iHigh(symbol, tf, index + 1);
   double low1  = iLow (symbol, tf, index + 1);

   //--- Condition 1: Outside bar range
   bool isOutsideBar =
      (high0 > high1) &&
      (low0  < low1);

   if(!isOutsideBar)
      return false;

   //--- Condition 2: Bearish close below previous low
   bool isDownClose = (close0 < low1);

   if(!isDownClose)
      return false;
      
   //--- Condition 3: Bearish bar
   bool isBearishBar = (open0 > close0);
   
   if(!isBearishBar)
      return false;

   return true;
}

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

Измерение волатильности по предыдущему бару

Каждая проекция волатильности в этой системе начинается с простого измерения. Максимум минус минимум предыдущего бара определяет рабочий диапазон. Вспомогательная функция получает этот диапазон и нормализует его к правильной точности символа.

//+------------------------------------------------------------------+
//| Returns the price range (high - low) of a bar at the given index |
//+------------------------------------------------------------------+
double GetBarRange(const string symbol, ENUM_TIMEFRAMES tf, int index){

   double high = iHigh(symbol, tf, index);
   double low  = iLow (symbol, tf, index);

   if(high == 0.0 || low == 0.0){
      return 0.0;
   }

   return NormalizeDouble(high - low, Digits());
}

Это единственное значение служит основой для проекции уровней входа, стопа и позднее тейк-профита. Без этого измерения модель пробоя не может работать.

Проекция цен входа

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

//+--------------------------------------------------------------------------------------+
//| Calculates the bullish breakout entry price using today's open and yesterday's range |
//+--------------------------------------------------------------------------------------+
double CalculateBuyEntryPrice(double todayOpen, double yesterdayRange, double buyMultiplier){

   return todayOpen + (yesterdayRange * buyMultiplier);
}

Для медвежьего сетапа цена входа равна текущему открытию минус доля предыдущего диапазона.

//+--------------------------------------------------------------------------------------+
//| Calculates the bearish breakout entry price using today's open and yesterday's range |
//+--------------------------------------------------------------------------------------+
double CalculateSellEntryPrice(double todayOpen, double yesterdayRange, double sellMultiplier){

   return todayOpen - (yesterdayRange * sellMultiplier);
}

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

Проекция уровней стоп-лосса

Защитные выходы можно строить двумя разными способами. В режиме на основе волатильности стоп-лосс проектируется от цены входа с использованием доли того же предыдущего диапазона. Для бычьих сделок стоп размещается ниже входа.

//+--------------------------------------------------------------------------------------------------+
//| Calculates the stop-loss price for a bullish position based on entry price and yesterday's range |
//+--------------------------------------------------------------------------------------------------+
double CalculateBullishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){

   return entryPrice - (yesterdayRange * stopMultiplier);
}

Для медвежьих сделок стоп размещается выше входа.

//+--------------------------------------------------------------------------------------------------+
//| Calculates the stop-loss price for a bearish position based on entry price and yesterday's range |
//+--------------------------------------------------------------------------------------------------+
double CalculateBearishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){

   return entryPrice + (yesterdayRange * stopMultiplier);
}

Это сохраняет риск пропорциональным недавней волатильности и естественно адаптируется к разным рынкам и таймфреймам.

Проекция уровней тейк-профита с использованием соотношения риск/прибыль

Когда режим тейк-профита установлен на риск/прибыль, цели выводятся напрямую из измеренного расстояния до стопа. Расстояние между входом и стопом становится единицей риска. Это расстояние умножается на выбранное соотношение прибыли.

Результат прибавляется к входу или вычитается из него, чтобы получить целевую цену.

//+--------------------------------------------------------------------------+
//| Calculates take-profit level for a bullish trade using risk-reward logic |                               
//+--------------------------------------------------------------------------+
double CalculateBullishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){

   double stopDistance   = entryPrice - stopLossPrice;
   double rewardDistance = stopDistance * rewardValue;
   return NormalizeDouble(entryPrice + rewardDistance, Digits());
}

//+--------------------------------------------------------------------------+
//| Calculates take-profit level for a bearish trade using risk-reward logic |                               
//+--------------------------------------------------------------------------+
double CalculateBearishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){

   double stopDistance   = stopLossPrice - entryPrice;
   double rewardDistance = stopDistance * rewardValue;
   return NormalizeDouble(entryPrice - rewardDistance, Digits());
}

Такой подход гарантирует согласованную геометрию риска и прибыли во всех сделках.

Обнаружение пересечений уровней в реальном времени

Сигналы в этой системе не запускаются закрытием баров. Они запускаются тогда, когда цена действительно пересекает спроецированный уровень входа. Для этого мы сохраняем последние одноминутные закрытия и сравниваем два последовательных значения. Пересечение вверх происходит, когда цена движется из области ниже уровня в область выше него.

//+------------------------------------------------------------------+
//| To detect a crossover at a given price level                     |                               
//+------------------------------------------------------------------+
bool IsCrossOver(const double price, const double &closePriceMinsData[]){
   if(closePriceMinsData[1] <= price && closePriceMinsData[0] > price){
      return true;
   }
   return false;
}

Пересечение вниз обнаруживается, когда цена движется из области выше уровня в область ниже него.

//+------------------------------------------------------------------+
//| To detect a crossunder at a given price level                    |                               
//+------------------------------------------------------------------+
bool IsCrossUnder(const double price, const double &closePriceMinsData[]){
   if(closePriceMinsData[1] >= price && closePriceMinsData[0] < price){
      return true;
   }
   return false;
}

Это позволяет системе реагировать немедленно, когда импульс подтверждает сетап.

Фильтрация сделок по дню недели

Фильтрация по торговым дням реализуется с помощью двух небольших вспомогательных функций. Первая преобразует текущее время в индекс дня.

//+------------------------------------------------------------------------------------+
//| Returns the day of the week (0 = Sunday, 6 = Saturday) for the given datetime value|                               
//+------------------------------------------------------------------------------------+
int TimeDayOfWeek(datetime time){
   MqlDateTime timeStruct = {};
   if(!TimeToStruct(time, timeStruct)){
      Print("TimeDayOfWeek: TimeToStruct failed");
      return -1;
   }      
   return timeStruct.day_of_week;
}

Вторая проверяет, включен ли этот день в конфигурации.

//+-----------------------------------------------------------------------------------------------------+
//| Determines whether trading is permitted for the given datetime based on the selected trade-day mode |                               
//+-----------------------------------------------------------------------------------------------------+
bool IsTradingDayAllowed(datetime time)
{
   // Baseline mode: no filtering
   if(tradeDayMode == TDW_ALL_DAYS){
      return true;
   }

   int day = TimeDayOfWeek(time);

   switch(day)
   {
      case 0: return tradeSunday;
      case 1: return tradeMonday;
      case 2: return tradeTuesday;
      case 3: return tradeWednesday;
      case 4: return tradeThursday;
      case 5: return tradeFriday;
      case 6: return tradeSaturday;
   }

   return false;
}

Этот механизм позволяет выборочно тестировать паттерны в конкретные дни, что является центральной частью концепции Trade Day Of The Week. Фильтр является опциональным. Когда он отключен, принимаются все дни.

Проекция уровней волатильности при формировании нового бара

Когда все инструменты готовы, мы обновляем функцию обработки тика так, чтобы уровни волатильности проектировались только при формировании нового бара. Внутри нового блока мы выполняем строгую последовательность действий.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      //--- Increment the number of completed bars since the position was opened
      if(barsSinceEntry > 0){
         barsSinceEntry = barsSinceEntry + 1;
      }
      
      //--- Handle exit conditions for the currently active position based on the configured take-profit mode
      if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
         ManageOpenPositionExits();
      }
   
      lwVolatilityLevels.yesterdayRange    = GetBarRange(_Symbol, timeframe, 1);
      
      if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){
         lwVolatilityLevels.buyEntryPrice  = askPrice;
         lwVolatilityLevels.sellEntryPrice = bidPrice;
      }else{
         lwVolatilityLevels.buyEntryPrice  = CalculateBuyEntryPrice (askPrice, lwVolatilityLevels.yesterdayRange, inpBuyRangeMultiplier );
         lwVolatilityLevels.sellEntryPrice = CalculateSellEntryPrice(bidPrice, lwVolatilityLevels.yesterdayRange, inpSellRangeMultiplier);
      }      
      if(stopLossMode == SL_BY_RANGE_PERCENT){
         lwVolatilityLevels.bullishStopLoss = CalculateBullishStopLoss(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.yesterdayRange,  inpStopRangeMultiplier);
         lwVolatilityLevels.bearishStopLoss = CalculateBearishStopLoss(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.yesterdayRange, inpStopRangeMultiplier);
      }
      if(stopLossMode == SL_AT_PREVIOUS_BAR){
         lwVolatilityLevels.bullishStopLoss = iLow (_Symbol, timeframe, 1);
         lwVolatilityLevels.bearishStopLoss = iHigh(_Symbol, timeframe, 1);
      }     
      if(takeProfitMode == TP_BY_RISK_REWARD){
         lwVolatilityLevels.bullishTakeProfit = CalculateBullishTakeProfit(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.bullishStopLoss,  riskRewardRatio);
         lwVolatilityLevels.bearishTakeProfit = CalculateBearishTakeProfit(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.bearishStopLoss, riskRewardRatio);
      }
      
      lwVolatilityLevels.bullishStopDistance = lwVolatilityLevels.buyEntryPrice   - lwVolatilityLevels.bullishStopLoss;
      lwVolatilityLevels.bearishStopDistance = lwVolatilityLevels.bearishStopLoss - lwVolatilityLevels.sellEntryPrice;
      
   }
}

Сначала обновляется счетчик баров, если позиция активна. Затем оцениваются условия выхода для режима первого прибыльного открытия. Далее измеряется диапазон предыдущего бара. Затем проектируются цены входа в зависимости от выбранной стратегии. После этого проектируются цены стоп-лосса с использованием выбранной модели стопа. Затем отображаются спроецированные цены тейк-профита, если активен режим риск/прибыль. Наконец, вычисляются расстояния до стопа для последующего расчета размера позиции.

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

Обеспечение наличия только одной позиции в любой момент времени

Одно из ключевых проектных правил состоит в том, что в любой момент может существовать только одна позиция. Две вспомогательные функции просматривают все открытые позиции и подтверждают, существует ли уже позиция buy или sell для данного экземпляра советника.

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active buy position.  |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveBuyPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
            return true;
         }
      }
   }   
   return false;
}

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active sell position. |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveSellPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
            return true;
         }
      }
   }
   
   return false;
}

Эти проверки применяются перед каждым исполнением сделки. Если найдена любая позиция, новая сделка не открывается. Это правило сохраняет систему простой и предотвращает непреднамеренное наращивание позиций.

Программное закрытие позиций

Когда режимы фиксации прибыли не используют фиксированную цель, система должна закрывать сделки по логике, а не по цене. Общая функция закрытия просматривает все открытые позиции, принадлежащие этому советнику, и немедленно закрывает их.

//+------------------------------------------------------------------+
//| To close all position with a specified magic number              |   
//+------------------------------------------------------------------+
void ClosePositionsByMagic(ulong magic) {
    
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_MAGIC) == magic) {
                ulong positionType = PositionGetInteger(POSITION_TYPE);
                double volume = PositionGetDouble(POSITION_VOLUME);
                if (positionType == POSITION_TYPE_BUY) {
                    Trade.PositionClose(ticket);
                } else if (positionType == POSITION_TYPE_SELL) {
                    Trade.PositionClose(ticket);
                }
            }
        }
    }    
}

Позднее эта функция повторно используется как для выходов по времени, так и для выходов на первом прибыльном открытии.

Расчет автоматического размера позиции

Когда режим размера лота установлен на автоматический, объем позиции выводится из риска по счету.

//+----------------------------------------------------------------------------------+
//| Calculates position size based on a fixed percentage risk of the account balance |
//+----------------------------------------------------------------------------------+
double CalculatePositionSizeByRisk(double stopDistance){
   double amountAtRisk = (riskPerTradePercent / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE);
   double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
   double volume       = amountAtRisk / (contractSize * stopDistance);
   return NormalizeDouble(volume, 2);
}

Функция вычисляет сумму денег, которую можно рискнуть, исходя из выбранного процента. Затем она делит эту сумму на произведение размера контракта и расстояния до стопа. Результатом является объем позиции, который рискует ровно желаемой долей счета. Это сохраняет риск стабильным на разных инструментах и в разных режимах волатильности.

Исполнение ордеров на покупку и продажу

Две функции исполнения обрабатывают размещение сделок.

//+------------------------------------------------------------------+
//| Function to open a market buy position                           |
//+------------------------------------------------------------------+
bool OpenBuy(double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bullishStopDistance);
   }
   
   if(!Trade.Buy(lotSize, _Symbol, askPrice, lwVolatilityLevels.bullishStopLoss)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   
   if(takeProfitMode  == TP_AFTER_N_CANDLES){
      barsSinceEntry = 1;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Function to open a market sell position                          |
//+------------------------------------------------------------------+
bool OpenSel(double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bearishStopDistance);
   }
   
   if(!Trade.Sell(lotSize, _Symbol, bidPrice, lwVolatilityLevels.bearishStopLoss)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   
   if(takeProfitMode  == TP_AFTER_N_CANDLES){
      barsSinceEntry = 1;
   }
   
   return true;
}

Перед отправкой любого ордера размер лота пересчитывается, если активен автоматический расчет. Ордера отправляются с прикрепленным спроецированным стоп-лоссом. Если режим выхода основан на количестве свечей, счетчик баров инициализируется сразу после входа.

Каждая функция сообщает об ошибках исполнения, что позволяет диагностировать сбои во время тестирования.

Управление выходами без фиксированных целей

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

//+-------------------------------------------------------------------------------------------+
//| Manages exit logic for the currently open position based on the selected take-profit mode |
//+-------------------------------------------------------------------------------------------+
void ManageOpenPositionExits(){

   if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
      for(int i = PositionsTotal() - 1; i >= 0; i--){
         ulong ticket = PositionGetTicket(i);
         if(ticket == 0){
            Print("Error while fetching position ticket ", GetLastError());
            continue;
         }else{
            if(PositionGetDouble(POSITION_PROFIT) > 0 ){
               ClosePositionsByMagic(magicNumber);
            }
         }
      }
   }
   
   if(takeProfitMode == TP_AFTER_N_CANDLES){
      if(barsSinceEntry > exitAfterCandles){
         ClosePositionsByMagic(magicNumber);
         barsSinceEntry = 0;
      }
   }   
}

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

Отслеживание баров, прошедших после входа

Некоторые модели выхода зависят от времени, а не от цены. По этой причине система должна отслеживать, сколько завершенных баров прошло с момента открытия позиции.

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

//--- Tracks the number of completed bars elapsed since the current trade was opened
int barsSinceEntry;

Перед безопасным использованием она должна быть инициализирована при запуске советника.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- Initialize bar counter for time-based trade exit tracking
   barsSinceEntry = 0;

   return(INIT_SUCCEEDED);
}

Этот счетчик будет увеличиваться на каждом новом баре после открытия сделки и сбрасываться после закрытия позиции. Позднее он станет ориентиром для всех решений выхода, основанных на времени.

Сборка логики исполнения стратегии

Когда все строительные блоки готовы, мы собираем торговую логику в функции обработки тика. Каждая стратегия изолирована внутри собственного условного блока. Базовая стратегия выполняется только при формировании нового бара, потому что она входит сразу на открытии.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      ...
          
      //---
      if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){
         if(IsThereAnActiveBuyPosition(magicNumber) || IsThereAnActiveSellPosition(magicNumber)){
            ClosePositionsByMagic(magicNumber);
            Sleep(50);
         }
         
         if(tradeDayMode == TDW_SELECTED_DAYS){
            if(IsTradingDayAllowed(currentTime)){
               if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }else{
            if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
               OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
            }
         }
      }      
   }
}

Все остальные стратегии выполняются непрерывно в течение бара, потому что их сигналы зависят от пересечения ценой спроецированных уровней.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //---  
   if(lwStrategyMode == LW_STRATEGY_CONSECUTIVE_BEARISH_BARS){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBearishBars, BAR_CLOSE_DOWN)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_UPTREND_WITH_PULLBACK){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsUptrendWithPullback(_Symbol, timeframe, 1, uptrendLookbackBars, pullbackLookbackBars)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsOutsideBarWithDownClose(_Symbol, timeframe, 1)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_THIRD_BULLISH_DAY_FADE){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBullishBars, BAR_CLOSE_UP)){
            if(IsCrossUnder(lwVolatilityLevels.sellEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize);
                  }
               }else{
                  OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize);
               }
            }
         }
      }
   }
}

Перед исполнением любой сделки всегда должны быть выполнены три условия. Активная позиция отсутствует. Выбранный паттерн действителен. Фильтр торгового дня разрешает исполнение. Только когда все условия совпадают, отправляется сделка. Такая структура позволяет каждой стратегии работать независимо, используя общий движок исполнения.

Размещение логики выхода в правильный момент

Логика выхода оценивается в двух местах. При формировании нового бара проверяются выходы для режима первого прибыльного открытия. На каждом тике проверяются условия выхода режима на основе свечей.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      //--- Increment the number of completed bars since the position was opened
      if(barsSinceEntry > 0){
         barsSinceEntry = barsSinceEntry + 1;
      }
      
      //--- Handle exit conditions for the currently active position based on the configured take-profit mode
      if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
         ManageOpenPositionExits();
      }   
      ...      
   }
   
   //--- Handle exit conditions for the currently active position based on the configured take-profit mode
   if(takeProfitMode == TP_AFTER_N_CANDLES){
      ManageOpenPositionExits();
   }
   
   ...
}

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

Настройка чистого окружения графика

Наконец, мы подготавливаем визуальное рабочее пространство.

//+------------------------------------------------------------------+
//| This function configures the chart's appearance.                 |
//+------------------------------------------------------------------+
bool ConfigureChartAppearance()
{
   if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){
      Print("Error while setting chart background, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){
      Print("Error while setting chart grid, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){
      Print("Error while setting chart mode, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){
      Print("Error while setting chart foreground, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){
      Print("Error while setting bullish candles color, ", GetLastError());
      return false;
   }
      
   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   return true;
}

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- To configure the chart's appearance
   if(!ConfigureChartAppearance()){
      Print("Error while configuring chart appearance", GetLastError());
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

На этом этапе советник завершен. Каждый паттерн может быть обнаружен объективно. Каждый уровень волатильности проектируется согласованно. Каждая сделка фильтруется, рассчитывается по размеру, исполняется и закрывается по правилу. Каждая стратегия работает независимо в рамках единой архитектуры. Система теперь готова к тестированию.


Тестирование паттернов на золоте

Тестовая среда и конфигурация

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

  • Инструмент: золото XAUUSD
  • Начальный баланс: 10 000 долларов
  • Период тестирования: с 1 января 2025 года по 30 декабря 2025 года
  • Таймфрейм: дневной
  • Стоп-лосс: процент от диапазона предыдущего дня
  • Тейк-профит: модель риск/прибыль
  • Торговые дни: разрешены все дни
  • Размер позиции: процент от баланса счета

Эта настройка близко следует духу оригинальных исследований Ларри Уильямса и предоставляет реалистичную торговую среду.

Базовая стратегия: покупка на открытии

Первый тест представляет самую простую возможную модель. Позиция открывается в начале каждого дня и закрывается в конце того же дня.

Результаты:

  • Общая чистая прибыль: 8 187,44 доллара
  • Процент выигрышных сделок: 50,40 процента

Вывод

BaselineStrategyEquityCurve

Этот результат подтверждает важный тезис из книги. Когда рынок сильно трендовый, даже наивная стратегия может приносить прибыль. Золото в 2025 году в целом находилось в бычьем тренде. Покупка на каждом открытии позволила системе извлечь выгоду из этого направленного смещения. Однако процент выигрышных сделок остается близким к случайному. Прибыль здесь возникает главным образом из направления рынка, а не из качества паттерна. Эта стратегия устанавливает полезный ориентир для всех остальных.

Покупка после одного закрытия вниз

Результаты:

  • Общая чистая прибыль: 662,40 доллара
  • Процент выигрышных сделок: 36,36 процента

Вывод

BuyOneCloseEquityCurve

Эта вариация работает слабо. Один медвежий день дает очень мало информации об истощении или развороте. Низкий процент выигрышных сделок показывает, что большинство откатов продолжаются, а не разворачиваются. Это подтверждает, что не все паттерны заслуживают одинакового доверия.

Покупка после трех последовательных закрытий вниз

Результаты:

  • Общая чистая прибыль: 1 309,55 доллара
  • Процент выигрышных сделок: 71,43 процента

Вывод

BuyThreeConsecutiveBearishBarsEquityCurve

Здесь картина резко меняется. Три последовательных медвежьих закрытия представляют эмоциональное давление и краткосрочное истощение. Процент выигрышных сделок резко растет, и прибыльность улучшается. Это поддерживает одну из ключевых идей Ларри Уильямса. Паттерны, отражающие экстремальное поведение, дают реальное преимущество. Избирательность явно повышает эффективность.

Восходящий тренд с откатом

Результаты:

  • Общая чистая прибыль: 1 343,43 доллара
  • Процент выигрышных сделок: 100,00 процента

Вывод

PullBackEquityCurve

Это лучший результат во всем исследовании. Паттерн объединяет две силы:

  • Подтверждение долгосрочного тренда
  • Вход на краткосрочном откате

Сделки согласуются с основным направлением при входе после временной слабости. Хотя размер выборки мал, результат показывает, почему эта модель является одной из любимых у Ларри Уильямса. Тренд плюс откат создает возможности высокого качества. Это подтверждает, что качество структуры важнее частоты сделок.

Внешний день с закрытием вниз

Результаты:

  • Общая чистая прибыль: 0,00 доллара
  • Процент выигрышных сделок: 0,00 процента

Вывод

OutsideDayPatternEquityCurve

За весь год не было сгенерировано ни одной сделки. Этот результат так же важен, как и любой прибыльный итог. Ларри Уильямс уже предупреждал, что этот паттерн появляется редко. Тестирование подтверждает, что редкость реальна. Редкие паттерны могут быть мощными, но они не подходят как самостоятельные системы. Лучше всего они работают как фильтры или модели редких возможностей.

Продажа против трех последовательных бычьих закрытий

Результаты:

  • Общая чистая прибыль: минус 1 043,81 доллара
  • Процент выигрышных сделок: 0,00 процента

Вывод

ConsecutiveBullishBarFadeEquityCurve

Эта стратегия показывает худший результат из всех. Попытка продавать против силы в бычьем году приводит к повторяющимся неудачам. Импульс продолжается, а не разворачивается. Это подтверждает ключевой принцип. Контртрендовые паттерны опасны, если не поддержаны сильным контекстом. Без фильтров истощения или волатильности торговля против движения становится проигрышной игрой. Этот тест ясно объясняет, почему Ларри Уильямс предпочитает торговать вместе с давлением, а не против него.

Наблюдения по стратегиям

Из этих результатов следуют несколько важных выводов.

Направление рынка имеет значение

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

Избирательность повышает качество

По мере того как паттерны становятся более избирательными, эффективность улучшается.

  • Одно закрытие вниз работает слабо
  • Три закрытия вниз работают хорошо
  • Тренд плюс откат работает лучше всего

Фильтрация уменьшает частоту сделок, но повышает их качество. Это поддерживает идею, что меньшее количество лучше структурированных сделок дает лучшие результаты.

Паттерны, основанные на эмоциях, работают лучше

Самые сильные паттерны все отражают эмоциональное поведение.

  • Несколько закрытий вниз
  • Откаты в трендах
  • Истощение после давления

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

Продавать против силы опасно

Проигрышная стратегия показывает риск торговли против сильного импульса. Без свидетельств истощения контртрендовые сделки борются с рыночным давлением. Это объясняет, почему многие разворотные системы терпят неудачу в трендовых условиях.

Чему нас учат эти тесты

Это исследование подтверждает несколько центральных идей Ларри Уильямса.

  • Рынки не случайны в краткосрочном периоде
  • Некоторые дни и паттерны дают реальное преимущество
  • Избирательность создает стабильность
  • Следование тренду повышает устойчивость стратегии

Самое важное — оно показывает, что паттерны нужно тестировать, а не принимать на веру. Некоторые идеи выглядят сильными в теории, но исчезают в реальных данных. Другие спокойно создают устойчивое преимущество.

Почему это важно

Цель этого этапа тестирования не в том, чтобы найти идеальную систему. Она в том, чтобы продемонстрировать дисциплинированный способ изучения рыночного поведения. Каждый паттерн в этой статье теперь можно:

  • Повторно тестировать на других рынках
  • Комбинировать с фильтрами
  • Улучшать условиями волатильности
  • Изучать во временном разрезе

Это формирует основу для системного исследования, а не субъективной веры. Более того, в конечном счете именно это и является настоящим уроком «Паттернов для прибыли».


Заключение

В этой статье мы сделали больше, чем просто перевели идеи из книги в код. Мы построили небольшую исследовательскую систему, которая позволяет тестировать, сравнивать и измерять паттерны дисциплинированным способом. Шаг за шагом мы преобразовали краткосрочные наблюдения Ларри Уильямса в рабочий советник, который можно запускать, повторять и проверять на исторических данных.

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

Настоящая ценность этой работы не только в цифрах. Она заключается в созданной архитектуре. Теперь у нас есть гибкий движок, который может размещать множество идей, применять согласованные правила риска, фильтровать сделки по времени и оценивать эффективность без эмоций. Такой подход позволяет торговым исследованиям перейти от веры к доказательствам.

Отсюда путь открыт. Можно добавлять новые паттерны. Существующие правила можно уточнять. Можно тестировать разные рынки, таймфреймы и модели риска. Каждое изменение становится контролируемым экспериментом, а не догадкой.

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

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21063

Прикрепленные файлы |
Разработка динамического мультивалютного советника (Часть 8): Ротация капитала в зависимости от времени суток Разработка динамического мультивалютного советника (Часть 8): Ротация капитала в зависимости от времени суток
В этой статье представлен механизм ротации капитала по торговым сессиям на языке MQL5, который распределяет риск по торговым сессиям вместо равномерной экспозиции в течение всего дня. Мы подробно разберем бюджеты риска по сессиям в рамках дневного лимита, динамический расчет лота на основе оставшегося риска сессии и автоматические ежедневные сбросы. При исполнении сделок используется специфичная для каждой сессии логика пробоя и торговли против ложного движения с подтверждением волатильности по ATR. В результате читатель получает практический шаблон, который позволяет направлять капитал туда, где условия конкретной сессии статистически наиболее сильны, сохраняя при этом контроль над экспозицией в течение всего дня.
Алгоритм оптимизации шимпанзе: от ChOA к BChimp Алгоритм оптимизации шимпанзе: от ChOA к BChimp
Алгоритм оптимизации шимпанзе (ChOA) подражает групповой охоте приматов с разделением ролей, а его бинарная ветвь BChimp переносит эту механику в задачи отбора признаков. Реализуем непрерывное ядро в C_AO, по пути находим и исправляем унаследованный дефект коэффициента — незаметный за бинаризацией, но разрушающий поиск в непрерывной области. Аннотация даёт готовую реализацию и практические выводы о качестве и устойчивости поиска.
Нейросети в трейдинге: Когнитивная инерция в анализе финансовых рынков (модуль временной согласованности) Нейросети в трейдинге: Когнитивная инерция в анализе финансовых рынков (модуль временной согласованности)
В статье продолжается адаптация фреймворка CogDriver к финансовым временным рядам. Основное внимание уделено модулю временной согласованности TCM, который связывает текущее рыночное состояние с памятью ранее сохранённых запросов (Query). Разбираются ранжируемая память, расчёт оценки (Score) через Flash-Attention, обновление слотов памяти средствами OpenCL и построение слоя CNeuronCogDriverRankTCM в MQL5, что даёт готовый контур временной согласованности для последующих торговых моделей.
Преодоление проблем доступности в торговых инструментах на MQL5 (Часть III): Двунаправленное голосовое взаимодействие между трейдером и советником Преодоление проблем доступности в торговых инструментах на MQL5 (Часть III): Двунаправленное голосовое взаимодействие между трейдером и советником
Создадим локальный двунаправленный голосовой интерфейс для MetaTrader 5 с помощью WebRequest в MQL5 и двух сервисов Python. В статье реализовано автономное распознавание речи с помощью Vosk, обнаружение фразы активации, HTTP‑endpoint для получения команд и сервер преобразования текста в речь на локальном хосте. Вы подключите советника, который будет получать команды, открывать сделки и возвращать голосовые подтверждения для возможности работать без помощи рук.