Рыночные секреты Ларри Уильямса (Часть 3): Проверка неслучайного поведения рынка средствами MQL5
Введение
Действительно ли финансовые рынки случайны или в них существуют закономерности, которые можно измерить и проверить? Этот вопрос лежит в основе каждой когда-либо созданной торговой системы. Если движение цены полностью случайно, то любая попытка построить стратегию, индикатор или автоматическую торговую систему в конечном счете бессмысленна. Не было бы никакого статистического преимущества, которое можно найти, — только случайность.
В этой статье мы возвращаемся к этим идеям с современной практической точки зрения (более подробное обсуждение базовых статистических понятий см. в предыдущей статье). Вместо того чтобы принимать выводы на веру, мы используем MQL5 для воспроизведения и расширения экспериментов Уильямса с помощью кода. Написав советник, мы проверим, возникают ли определенные модели поведения цены чаще, чем можно было бы ожидать случайно. Цель состоит не в том, чтобы предсказывать рынки с уверенностью, а в том, чтобы определить, существуют ли небольшие измеримые статистические перекосы.
Это важно, потому что даже небольшой статистический перекос может стать основой торгового преимущества. Если прошлое поведение цены никак не связано с будущими результатами, системная торговля не может работать. Однако если рынки хотя бы частично неслучайны, то структура, логика и вероятность становятся значимыми инструментами для трейдеров.
Как мы тестируем неслучайное поведение рынка
Во введении мы сформулировали простую, но важную идею. Если бы рынки были действительно случайными, поведение цены напоминало бы подбрасывание монеты. Каждая свеча была бы независима от предыдущей, а вероятность закрытия вверх или вниз оставалась бы фиксированной на уровне пятидесяти процентов независимо от того, что произошло ранее.
Ларри Уильямс поставил это предположение под сомнение, задав практический вопрос. Что происходит после того, как цена закрывается вверх или вниз? Если прошлое не влияет на будущее поведение цены, результат следующей свечи не должен был бы меняться. Его исследования показали, что на практике происходит иначе.
Чтобы проверить эту концепцию практически и измеримо, мы используем автоматизированный подход. Вместо того чтобы опираться на предположения или выборочные примеры, мы позволим коду проанализировать исторические ценовые данные и посчитать, как часто возникают определенные условия. Каждый тест предназначен для ответа на конкретный вероятностный вопрос о поведении цены.
Эксперименты, которые мы проведем, делятся на три широкие категории. Сначала мы проверяем, существует ли общий направленный перекос внутри одной свечи. Это отвечает на фундаментальный вопрос: закрывается ли цена выше открытия чаще, чем можно было бы ожидать случайно.
Во-вторых, мы тестируем условную вероятность. Здесь мы спрашиваем, ведет ли себя рынок иначе после одной или нескольких последовательных бычьих и медвежьих свечей. Если бы рынки были случайными, вероятность того, что следующая свеча будет бычьей или медвежьей, оставалась бы неизменной. Если вероятности сдвигаются, значит цена явно реагирует на свое недавнее прошлое.
В-третьих, мы проверяем простую форму рыночной структуры. Ларри Уильямс описывал краткосрочный минимум как трехбарный паттерн, при котором цена формирует минимум, а затем не продолжает снижение. Если у этого паттерна есть предсказательная ценность, следующая свеча должна закрываться выше чаще, чем предполагала бы одна лишь случайность.
Чтобы охватить эти идеи, мы автоматизируем следующие восемь тестовых сценариев:
Направленный перекос от открытия к закрытию
Мы измеряем, как часто свеча закрывается бычьей, то есть когда цена закрытия выше цены открытия. На действительно случайном рынке бычьи и медвежьи закрытия должны происходить примерно по пятьдесят процентов времени каждое.
Бычья реакция после одного медвежьего закрытия
Мы проверяем, как часто следующая свеча закрывается бычьей после одной медвежьей свечи. Это помогает понять, привлекает ли краткосрочный откат покупательское давление.
Бычья реакция после двух последовательных медвежьих закрытий
Здесь мы проверяем, повышается ли вероятность бычьего закрытия после двух медвежьих свечей подряд. Это исследует, склонны ли краткосрочные снижения создавать условия для отскока.
Бычья реакция после трех последовательных медвежьих закрытий
Этот тест развивает ту же идею дальше, изучая поведение цены после более глубокого краткосрочного снижения.
Бычья реакция после одного бычьего закрытия
Мы проверяем, стремится ли бычий импульс продолжаться или угасать после одной бычьей свечи.
Бычья реакция после двух последовательных бычьих закрытий
Этот эксперимент проверяет, следует ли сила за силой или цена останавливается после краткого ралли.
Бычья реакция после трех последовательных бычьих закрытий
Этот тест проверяет, приводит ли продолжительное краткосрочное давление покупателей к продолжению движения или к истощению.
Бычья реакция после краткосрочного рыночного минимума
На основе определения Ларри Уильямса мы выявляем краткосрочный минимум с помощью трехбарного ценового паттерна и измеряем, как часто следующая свеча закрывается бычьей.
Каждый из этих тестов отвечает на простой вопрос. Влияет ли поведение предыдущих свечей на вероятность следующей? Если ответ всегда был бы равен пятидесяти процентам, рынки вели бы себя как подбрасывание монеты. Любое устойчивое отклонение от этого уровня указывает на неслучайное поведение.
Когда эти тестовые сценарии четко определены, мы можем перейти к технической части. В следующем разделе мы реализуем советник, который позволит запускать каждый эксперимент отдельно на разных рынках и наблюдать результаты непосредственно по историческим данным.
Реализация экспериментов в MQL5
Чтобы проверить, действительно ли такие модели рыночного поведения носят неслучайный характер, нам нужна не только статистика. Мы хотим смоделировать реальные торговые условия. Это означает открытие позиции на открытии свечи, закрытие ее на закрытии свечи и запись того, что фактически происходит. Такой подход позволяет оценивать вероятности, а также отвечать на более практический вопрос: можно ли позже превратить эти поведения в торговые идеи?
Для начала откройте MetaEditor 5, создайте новый советник, выберите пустой шаблон и назовите файл larryWilliamsNonRandomMarketBehaviorTester.mq5. После создания вставьте предоставленный исходный код в файл и сохраните его.
//+------------------------------------------------------------------+ //| larryWilliamsNonRandomMarketBehaviorTester.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ru/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ru/users/chachaian" #property version "1.00" #property description "This Expert Advisor is designed to run one statistical experiment at a time." #property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart." //+------------------------------------------------------------------+ //| Стандартные библиотеки | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Глобальные переменные | //+------------------------------------------------------------------+ //--- Создать объект CTrade для обработки торговых операций CTrade Trade; //--- Bid и Ask double askPrice; //+------------------------------------------------------------------+ //| Входные переменные пользователя | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; //+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ //--- Назначить уникальный магический номер для идентификации сделок, открытых этим советником Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Функция деинициализации советника | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Сообщить причину остановки программы Print("Program terminated! Reason code: ", reason); } //+------------------------------------------------------------------+ //| Функция обработки тиков советника | //+------------------------------------------------------------------+ void OnTick(){ //--- Получить текущие рыночные цены для выполнения сделок askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); } //+------------------------------------------------------------------+ //| Функция TradeTransaction | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { } //+------------------------------------------------------------------+
Этот начальный код является фундаментом, на котором мы будем строить дальше. Он еще не запускает никаких экспериментов, но аккуратно и контролируемо настраивает все, что нам понадобится. Чтобы вам было проще следовать статье, мы прикрепили итоговый исходный код: larryWilliamsNonRandomMarketBehaviorTester.mq5. Вы можете скачать файл и сравнить его со своей версией, чтобы убедиться, что все настроено правильно.
Заголовок файла и директивы свойств
//+------------------------------------------------------------------+ //| larryWilliamsNonRandomMarketBehaviorTester.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ru/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ru/users/chachaian" #property version "1.00" #property description "This Expert Advisor is designed to run one statistical experiment at a time." #property description "The test to be executed is selected through a dropdown input when attaching the EA to the chart."
Первый раздел определяет имя файла, сведения об авторе, версию и описание. Эти свойства важны, потому что они описывают назначение советника при прикреплении к графику. В данном случае описание ясно указывает, что советник запускает один статистический эксперимент за раз, а эксперимент выбирается с помощью выбора из выпадающего списка входных параметров. Это напрямую соответствует целям статьи.
Подключение стандартной библиотеки
//+------------------------------------------------------------------+ //| Стандартные библиотеки | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
Далее мы подключаем стандартную торговую библиотеку. Эта библиотека предоставляет доступ к классу CTrade, который обеспечивает безопасное и структурированное выполнение ордеров. Использование этого класса рекомендуется, поскольку он упрощает управление сделками и снижает риск ошибок выполнения.
Пользовательские входные настройки//+------------------------------------------------------------------+ //| Входные переменные пользователя | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
Раздел входных параметров определяет параметры, которыми пользователь может управлять при прикреплении советника к графику. Мы задаем магический номер для уникальной идентификации сделок, открытых этим советником, чтобы они не мешали другим стратегиям. Мы также позволяем пользователю выбрать таймфрейм эксперимента. Это делает советник гибким и пригодным для повторного использования на разных настройках графика.
Глобальные торговые объекты и переменные
//+------------------------------------------------------------------+ //| Глобальные переменные | //+------------------------------------------------------------------+ //--- Создать объект CTrade для обработки торговых операций CTrade Trade; //--- Bid и Ask double askPrice;
Затем мы создаем глобальный объект CTrade. Этот объект будет использоваться во всем советнике для открытия и закрытия позиций. Мы также объявляем переменные для рыночных цен. На этом этапе мы получаем только цену Ask, чего достаточно для открытия сделок на покупку во время наших экспериментов.
Логика инициализации
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ //--- Назначить уникальный магический номер для идентификации сделок, открытых этим советником Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); }
Функция инициализации выполняется один раз, когда советник прикрепляется к графику. Здесь мы назначаем магический номер объекту CTrade. Этот шаг важен, потому что он гарантирует, что каждую сделку, открытую советником, позже можно будет корректно отслеживать и управлять ею.
Логика деинициализации
//+------------------------------------------------------------------+ //| Функция деинициализации советника | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Сообщить причину остановки программы Print("Program terminated! Reason code: ", reason); }
Когда советник удаляется или останавливается, вызывается функция деинициализации. В нашем случае она просто выводит сообщение, указывающее, почему программа остановилась. Это полезно для отладки и для понимания того, когда и как завершается жизненный цикл советника.
Обработка тиков
//+------------------------------------------------------------------+ //| Функция обработки тиков советника | //+------------------------------------------------------------------+ void OnTick(){ //--- Получить текущие рыночные цены для выполнения сделок askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); }
Функция обработки тиков выполняется непрерывно по мере поступления новых рыночных данных. Пока она получает только текущую цену Ask. Позже эта функция станет центром управления нашими экспериментами. Она будет обнаруживать новые свечи, оценивать тестовые условия, открывать сделки и закрывать их в нужный момент.
Обработчик торговых транзакций
//+------------------------------------------------------------------+ //| Функция TradeTransaction | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { }
Наконец, мы определяем функцию торговой транзакции. Эта функция срабатывает всякий раз, когда происходит торговое действие, например открытие или закрытие позиции. Хотя сейчас она пустая, позже она поможет отслеживать результаты сделок и собирать результаты эксперимента.
На этом этапе советник не показывает видимой активности на графике. Это сделано намеренно. Мы построили чистый и надежный каркас, соответствующий лучшим практикам. В следующем разделе мы начнем добавлять логику для обнаружения новых свечей, применения наших неслучайных тестовых сценариев и выполнения сделок, чтобы наблюдать реальное поведение рынка в действии.
Теперь, когда каркас советника готов, следующий этап — научить нашу программу распознавать поведение рынка. На этом этапе мы еще не торгуем. Наша цель проста и важна: мы хотим, чтобы советник корректно обнаруживал каждый паттерн, представляющий один из наших неслучайных тестовых сценариев.
Прежде чем советник сможет что-либо тестировать, он должен четко понимать, какой эксперимент выполняется. Поэтому первый шаг — определить механизм переключения между тестовыми сценариями чистым и контролируемым способом.
Определение режимов тестирования
Сразу под подключением стандартной библиотеки мы вводим пользовательское перечисление, в котором перечислены все неслучайные эксперименты, которые мы хотим запустить. Каждое значение представляет отдельный тип рыночного поведения, который мы хотим изучить.
//--- ПОЛЬЗОВАТЕЛЬСКИЕ ПЕРЕЧИСЛЕНИЯ //+------------------------------------------------------------------+ //| Режимы тестирования неслучайного поведения рынка | //+------------------------------------------------------------------+ enum ENUM_NON_RANDOM_TEST_MODE { TEST_OPEN_TO_CLOSE_BIAS, TEST_AFTER_ONE_DOWN_CLOSE, TEST_AFTER_TWO_DOWN_CLOSES, TEST_AFTER_THREE_DOWN_CLOSES, TEST_AFTER_ONE_UP_CLOSE, TEST_AFTER_TWO_UP_CLOSES, TEST_AFTER_THREE_UP_CLOSES, TEST_AFTER_SHORT_TERM_LOW };
Это перечисление позволяет советнику работать в одном тестовом режиме за раз. Когда советник прикрепляется к графику, пользователь выбирает эксперимент из выпадающего списка, заданного входным параметром. Такой дизайн сохраняет логику простой и не смешивает результаты разных тестов.
На этом этапе мы еще не тестируем вероятности. Мы только определяем структуру. Эта структура будет направлять все последующее обнаружение паттернов и принятие решений.
Обнаружение момента открытия бара
Все наши эксперименты оцениваются один раз — когда открывается новая свеча. Это критически важно. Если реагировать на каждый тик, мы внесем шум и дублирующие сигналы. Чтобы избежать этого, мы обнаруживаем паттерны только при формировании нового бара.
Для этого мы определяем небольшую, но мощную служебную функцию, которая проверяет, открылся ли новый бар на выбранном таймфрейме.
//--- СЛУЖЕБНЫЕ ФУНКЦИИ //+------------------------------------------------------------------+ //| Функция проверки появления нового бара на заданном таймфрейме графика| //+------------------------------------------------------------------+ bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm) { datetime currentTm = iTime(symbol, tf, 0); if(currentTm != lastTm){ lastTm = currentTm; return true; } return false; }
Функция сравнивает время открытия текущего бара с последним записанным временем бара. Если время изменилось, значит сформировался новый бар.
Чтобы поддержать эту логику, мы также объявляем глобальную переменную, которая хранит время открытия самого последнего бара.
//+------------------------------------------------------------------+ //| Глобальные переменные | //+------------------------------------------------------------------+ ... //--- Для отслеживания открытия нового бара datetime lastBarOpenTime;
Эта переменная инициализируется нулем внутри функции инициализации, чтобы первый бар всегда обнаруживался корректно.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Инициализировать глобальные переменные lastBarOpenTime = 0; return(INIT_SUCCEEDED); }
С этого момента каждая проверка паттерна в советнике будет выполняться только после подтверждения нового бара. Это обеспечивает согласованность во всех экспериментах.
Универсальная функция для поведения последовательных баров
Большинство наших тестовых сценариев основаны на простом вопросе: что происходит после определенного количества бычьих или медвежьих баров?
Вместо того чтобы писать отдельную логику для каждого случая, мы создаем одну гибкую функцию, которая обрабатывает их все. Эта функция проверяет, закрылись ли последние N завершенных свечей в одном направлении относительно их открытия.
//+------------------------------------------------------------------+ //| Проверяет, закрылись ли последние N завершенных баров | //| вверх или вниз относительно цены открытия | //+------------------------------------------------------------------+ bool IsConsecutiveBarCloseState(string symbol, ENUM_TIMEFRAMES tf, int barsToCheck, ENUM_BAR_CLOSE_STATE closeState) { // Начинаем с бара с индексом 1 (последний полностью закрытый бар) for(int i = 1; i <= barsToCheck; i++){ double openPrice = iOpen (symbol, timeframe, i); double closePrice = iClose(symbol, timeframe, i); // Проверка безопасности на случай отсутствия данных if(openPrice == 0.0 || closePrice == 0.0){ return false; } // Проверить направление закрытия if(closeState == BAR_CLOSE_UP && closePrice <= openPrice){ return false; } if(closeState == BAR_CLOSE_DOWN && closePrice >= openPrice){ return false; } } return true; }
Чтобы функция была понятной и пригодной для повторного использования, мы передаем четыре входных параметра: тестируемый символ, таймфрейм, количество последовательных баров для оценки и направление этих баров. Чтобы четко описать направление бара, мы вводим небольшое перечисление для бычьих и медвежьих закрытий.
//--- ПОЛЬЗОВАТЕЛЬСКИЕ ПЕРЕЧИСЛЕНИЯ //+------------------------------------------------------------------+ //| Режимы тестирования неслучайного поведения рынка | //+------------------------------------------------------------------+ ... //+------------------------------------------------------------------+ //| Направление закрытия свечи относительно открытия | //+------------------------------------------------------------------+ enum ENUM_BAR_CLOSE_STATE { BAR_CLOSE_UP, BAR_CLOSE_DOWN };
Функция проходит только по завершенным свечам. Она начинает с последнего закрытого бара и движется назад. Для каждого бара она сравнивает цены открытия и закрытия и проверяет, соответствуют ли они ожидаемому направлению. Если какой-либо бар не проходит условие, функция возвращает false немедленно. Если все бары совпадают, функция возвращает true.
Эта единственная функция поддерживает шесть из восьми наших экспериментов. Это основной строительный блок наших неслучайных тестов, и позже он будет управлять реальными сделками.
Обнаружение краткосрочного минимума Ларри Уильямса
Последний паттерн, который нам нужно обнаруживать, более структурный. Ларри Уильямс определяет краткосрочный минимум как трехбарную формацию, где средний бар формирует свинг-минимум, а по обе стороны от него находятся более высокие минимумы.
Однако он также добавляет два важных фильтра. Свинг-бар не должен быть внешним баром (outside bar), а самый последний бар не должен быть внутренним баром (inside bar). Эти условия помогают исключить слабые или вводящие в заблуждение сигналы.
Чтобы обнаружить этот паттерн, мы создаем специальную функцию, которая анализирует последние три завершенных бара.
//+------------------------------------------------------------------+ //| Обнаруживает краткосрочный минимум Ларри Уильямса на последних трех барах | //| Бар с индексом 2 должен быть свинг-минимумом с более высокими минимумами по обе стороны | //| Бар 2 НЕ должен быть внешним баром | //| Бар 1 НЕ должен быть внутренним баром | //+------------------------------------------------------------------+ bool IsLarryWilliamsShortTermLow(string symbol, ENUM_TIMEFRAMES tf){ //--- Ценовые данные трех баров double high1 = iHigh(symbol, tf, 1); double low1 = iLow (symbol, tf, 1); double high2 = iHigh(symbol, tf, 2); double low2 = iLow (symbol, tf, 2); double high3 = iHigh(symbol, tf, 3); double low3 = iLow (symbol, tf, 3); //--- Условие 1: бар 2 должен быть свинг-минимумом bool isSwingLow = (low2 < low1) && (low2 < low3); if(!isSwingLow){ return false; } //--- Условие 2: бар 2 НЕ должен быть внешним баром относительно бара 3 bool isOutsideBar = (high2 > high3) && (low2 < low3); if(isOutsideBar){ return false; } //--- Условие 3: бар 1 НЕ должен быть внутренним баром относительно бара 2 bool isInsideBar = (high1 < high2) && (low1 > low2); if(isInsideBar){ return false; } //--- Все условия выполнены return true; }
Сначала она проверяет, имеет ли средний бар самый низкий минимум. Затем проверяет, что средний бар не является внешним баром относительно предыдущего. Наконец, она подтверждает, что самый последний бар не является внутренним баром относительно свинг-бара.
Только если все условия выполнены, функция возвращает true.
Выбор активного эксперимента
Чтобы упростить тестирование, мы вводим входной параметр пользователя, позволяющий трейдеру выбрать, какой неслучайный тестовый режим запускать. Этот параметр использует перечисление, определенное ранее, и отображается в виде выпадающего списка при прикреплении советника к графику.
//+------------------------------------------------------------------+ //| Входные переменные пользователя | //+------------------------------------------------------------------+ ... input group "Trade And Risk Management" input ENUM_NON_RANDOM_TEST_MODE nonRandomTestMode = TEST_OPEN_TO_CLOSE_BIAS;
Такой подход позволяет использовать один и тот же советник для всех экспериментов без изменения кода каждый раз.
Проверка обнаружения паттернов внутри OnTick
Когда вся логика обнаружения готова, мы соединяем все внутри функции обработки тиков.
//+------------------------------------------------------------------+ //| Функция обработки тиков советника | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Выполнять этот блок только при обнаружении нового бара на выбранном таймфрейме if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Открыть покупку, если выбран режим тестирования перекоса от открытия к закрытию if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ Print("EA should open a long position"); } //--- Открыть покупку для проверки смещения вероятности роста после одного медвежьего закрытия if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ Print("Previous bearish bar"); } } //--- Открыть покупку для проверки смещения вероятности роста после двух последовательных медвежьих закрытий if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ Print("Two consecutive bearish bars"); } } //--- Открыть покупку для проверки смещения вероятности роста после трех последовательных медвежьих закрытий if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ Print("Three consecutive bearish bars"); } } //--- Открыть покупку для проверки смещения вероятности роста после одного бычьего закрытия if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ Print("Previous bullish bar"); } } //--- Открыть покупку для проверки смещения вероятности роста после двух последовательных бычьих закрытий if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ Print("Two consecutive bullish bars"); } } //--- Открыть покупку для проверки смещения вероятности роста после трех последовательных бычьих закрытий if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ Print("Three consecutive bullish bars"); } } //--- Открыть покупку при обнаружении краткосрочного минимума по определению Ларри Уильямса if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ Print("Short-term low"); } } } }
Сначала советник проверяет, сформировался ли новый бар. Если нет, ничего не происходит. Когда новый бар обнаружен, советник оценивает только выбранный тестовый режим. Для каждого случая он вызывает соответствующую функцию обнаружения и выводит сообщение, когда паттерн найден.
На этом этапе сделки не открываются. Выводимые сообщения выступают подтверждающими сигналами. Они позволяют визуально проверить, что паттерны обнаруживаются в правильный момент и при правильных условиях.
Этот шаг критически важен. Перед добавлением реальной торговой логики мы должны убедиться, что обнаружение паттернов работает точно. Построение на ошибочной логике сделало бы все последующие результаты недействительными.
В следующем разделе мы заменим эти операторы печати фактической логикой выполнения сделок. В этот момент наш советник перейдет от наблюдения к эксперименту, позволяя проверить, могут ли эти неслучайные поведения превратиться в реальные рыночные результаты.
На этом этапе наш советник уже знает, как обнаруживать все паттерны, необходимые для тестов неслучайного поведения рынка. Следующий шаг — превратить эти сигналы в реальные рыночные действия. Именно здесь наблюдение становится экспериментом.
Для согласованности с исходной логикой Ларри Уильямса наш советник будет открывать только длинные позиции. Каждая сделка открывается в начале нового бара и закрывается в конце этого бара. Уровни стоп-лосс и тейк-профит не используются. Цель состоит не в управлении сделкой, а в измерении. Мы хотим наблюдать, как цена ведет себя сразу после появления определенного условия.
Из-за такой конструкции советник должен надежно уметь делать три вещи. Во-первых, он должен знать, есть ли у него уже открытая позиция. Во-вторых, он должен уметь аккуратно закрывать эту позицию. В-третьих, он должен открывать новую позицию только тогда, когда выполнено выбранное тестовое условие.
Проверка активной позиции
Перед открытием новой сделки советнику нужно подтвердить, есть ли у него уже активная позиция на покупку. Поскольку на одном счете могут существовать несколько советников или ручные сделки, мы используем магический номер для уникальной идентификации сделок, открытых именно этим советником.
Для этого мы определяем функцию, которая просматривает все открытые позиции и проверяет, есть ли среди них позиция с совпадающим магическим номером. Если такая позиция существует, функция возвращает true. В противном случае она возвращает false.
//+------------------------------------------------------------------+ //| Проверить, есть ли у этого советника активная позиция на покупку. | | //+------------------------------------------------------------------+ 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; }
Эта проверка позволяет советнику сохранять дисциплину. В любой момент времени может быть только одна активная позиция на тест. Это сохраняет результаты чистыми и предотвращает наложение сделок, которое исказило бы статистику.
Закрытие существующей позиции
Когда формируется новый бар, предыдущий бар официально закрылся. Этот момент представляет конец нашего тестового окна для предыдущей сделки. Если позиция все еще открыта, ее нужно немедленно закрыть.
Для этого мы определяем функцию, которая проходит по всем открытым позициям и закрывает те, которые принадлежат этому советнику на основе магического номера. Функция не пытается управлять прибылью или убытком. Она просто выходит из сделки по рыночной цене.
//+------------------------------------------------------------------+ //| Закрыть все позиции с указанным магическим номером | //+------------------------------------------------------------------+ 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); } } } } }
Такой подход гарантирует, что каждый тест следует одним и тем же правилам. Удержание ровно один бар. Ничего больше.
Открытие новой длинной позиции
Когда управление позициями готово, мы определяем простую функцию для открытия рыночного ордера на покупку. Функция принимает два входа. Первый — цена входа, то есть текущая цена Ask, уже отслеживаемая советником. Второй — размер позиции.
//+------------------------------------------------------------------+ //| Функция открытия рыночной позиции на покупку | //+------------------------------------------------------------------+ bool OpenBuy(double entryPrice, double lotSize){ if(!Trade.Buy(NormalizeDouble(lotSize, 2), _Symbol, entryPrice)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
Если сделка по какой-либо причине не выполняется, функция выводит диагностическую информацию. В противном случае она возвращает true, подтверждая, что ордер успешно размещен.
Чтобы сделать это гибким, мы позволяем пользователю задать предпочитаемый размер лота через входной параметр. Это сохраняет пригодность советника для разных размеров счетов, не нарушая логику эксперимента.
//+------------------------------------------------------------------+ //| Входные переменные пользователя | //+------------------------------------------------------------------+ ... input double positionSize = 0.01;
Выполнение логики внутри OnTick
Теперь все соединяется вместе.
Каждый раз, когда обнаруживается новый бар, советник выполняет следующие шаги по порядку. Сначала он закрывает любую существующую позицию, открытую этим советником. Это отмечает конец предыдущего тестового цикла. Добавляется короткая пауза, чтобы платформа корректно обработала закрытие.
//+------------------------------------------------------------------+ //| Функция обработки тиков советника | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Выполнять этот блок только при обнаружении нового бара на выбранном таймфрейме if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Закрыть существующие позиции на покупку этого советника перед открытием новой if(IsThereAnActiveBuyPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(100); } ... } }
Затем советник проверяет, какой тестовый режим выбран в данный момент. На основе этого выбора он оценивает соответствующее условие. Если условие выполнено, новая позиция на покупку открывается немедленно в начале нового бара.
//+------------------------------------------------------------------+ //| Функция обработки тиков советника | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Выполнять этот блок только при обнаружении нового бара на выбранном таймфрейме if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ ... //--- Открыть покупку, если выбран режим тестирования перекоса от открытия к закрытию if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ OpenBuy(askPrice, positionSize); } //--- Открыть покупку для проверки смещения вероятности роста после одного медвежьего закрытия if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку для проверки смещения вероятности роста после двух последовательных медвежьих закрытий if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку для проверки смещения вероятности роста после трех последовательных медвежьих закрытий if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку для проверки смещения вероятности роста после одного бычьего закрытия if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку для проверки смещения вероятности роста после двух последовательных бычьих закрытий if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку для проверки смещения вероятности роста после трех последовательных бычьих закрытий if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Открыть покупку при обнаружении краткосрочного минимума по определению Ларри Уильямса if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ OpenBuy(askPrice, positionSize); } } } }
Единственное изменение по сравнению с предыдущей фазой тестирования простое, но важное. Мы заменили все вызовы Print фактическими вызовами выполнения сделок. Логика обнаружения осталась неизменной. Это подтверждает, что наша предыдущая работа по проверке была правильной.
На этом этапе советник полностью работоспособен. Он обнаруживает паттерны, открывает сделки, закрывает их после одного бара и последовательно повторяет процесс во всех тестовых случаях.
Настройка графика для наглядного визуального тестирования
Поскольку этот советник предназначен для визуальных и статистических экспериментов, важно, чтобы мы ясно видели, что происходит на графике во время выполнения тестов. Мы хотим сразу распознавать бычьи и медвежьи бары, видеть входы и выходы из сделок и избегать лишнего визуального шума, который может отвлекать от наблюдения.
По этой причине мы вводим небольшую вспомогательную функцию, которая настраивает внешний вид графика сразу после прикрепления советника.
//+------------------------------------------------------------------+ //| Эта функция настраивает внешний вид графика. | //+------------------------------------------------------------------+ 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; }
Во время экспериментов визуальная обратная связь играет ключевую роль. Хотя наши выводы в конечном счете будут основаны на данных и статистике, наблюдение за поведением цены при каждом тестовом условии помогает проверить предположения и рано заметить аномалии.
Конфигурация, применяемая этой функцией, достигает трех основных целей:
- Ясность — белый фон с черными элементами переднего плана обеспечивает максимальный контраст.
- Визуальное выделение направления движения — бычьи бары четко отличаются от медвежьих с помощью современных интуитивных цветов.
- Фокус — линии сетки удаляются, чтобы внимание оставалось на движении цены и исполненных сделках.
Функция настройки графика вызывается внутри функции OnInit.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Настроить внешний вид графика if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
Это гарантирует, что график настраивается сразу после запуска советника. Если по какой-либо причине платформа не сможет применить настройку, советник сообщит о проблеме и остановит инициализацию.
С этим добавлением разработка нашего тестового советника завершена. В следующем разделе мы начнем запускать эти эксперименты на разных рынках. Мы будем наблюдать результаты, сравнивать исходы по классам активов и наконец ответим на вопрос, с которого началось это исследование: действительно ли рынки случайны или они оставляют измеримые следы?
Выполнение тестов и наблюдения за поведением рынка
До этого момента наше внимание было сосредоточено на идеях и подготовке. Мы рассмотрели аргумент Ларри Уильямса о том, что поведение цены не является полностью случайным, перевели эти идеи в четко определенные тестовые сценарии и построили советник, способный последовательно выполнять эти тесты.
Советник намеренно прост. Он не оптимизирован; у него нет ни стоп-лосса, ни тейк-профита, и он не управляет сделками динамически. Каждая позиция открывается в начале нового бара и закрывается в конце того же бара. В один момент времени может быть открыта только одна сделка, и каждый тест выполняется независимо от остальных.
Наша цель здесь — не построить прибыльную торговую систему и не оптимизировать входы, выходы или управление риском. Вместо этого мы хотим ответить на гораздо более простой и фундаментальный вопрос:
Возникают ли определенные модели поведения цены чаще, чем предполагала бы случайность, и можно ли наблюдать их последовательно на разных рынках?
Среда тестирования
Чтобы результаты наших экспериментов были согласованными, сопоставимыми и воспроизводимыми, все тесты проводятся при одинаковых фиксированных условиях. Параметры не настраиваются от одного рынка к другому, и оптимизация не выполняется ни на одном этапе.
Все эксперименты выполняются на дневном таймфрейме (D1). Использование более высокого таймфрейма помогает снизить шум микроструктуры рынка и гарантирует, что каждая сделка отражает полную торговую сессию, а не краткосрочные ценовые колебания.
Период тестирования для каждого финансового инструмента охватывает даты с 01.01.2025 по 30.11.2025. Этот одинаковый диапазон дат позволяет сравнивать поведение рынков без внесения временного смещения.
Во всех экспериментах используется фиксированный размер лота (0.1). Советник открывает только одну сделку за раз, и каждая сделка удерживается ровно один бар. Пирамидинг, масштабирование и наложение позиций отсутствуют. Это гарантирует, что каждый результат независим и напрямую связан с оцениваемым тестовым условием.
Все тесты начинаются с одинакового начального баланса $10,000 USD и используют кредитное плечо 1:500. Торговые правила, логика выполнения сделок и поведение выхода остаются неизменными для всех рынков и тестовых сценариев. Такая единая настройка критически важна для изоляции поведения рынка, а не подгонки стратегии.
Чтобы упростить воспроизведение, предоставляется файл конфигурации (configurations.ini). При запуске советника в тестере стратегий MetaTrader 5, этот файл можно загрузить из вкладки Настройки нажав “Загрузить” и выбрав предоставленный файл .ini. Это применит те же параметры тестирования, которые использовались в этих экспериментах, позволяя читателям повторить их в идентичных условиях.
Финансовые инструменты для тестирования
Чтобы оценить, как неслучайное поведение рынка проявляется в разных типах рынков, мы запускаем все тесты на следующих финансовых инструментах:
- XAUUSD — товарный актив (золото)
- BTCUSD — криптовалюта (Bitcoin)
- GBPUSD — валютная пара (британский фунт против доллара США)
- NAS100 (Nasdaq 100) — фондовый индекс
Эти инструменты были намеренно выбраны из разных, в значительной степени несвязанных классов активов. Каждый рынок работает в разных условиях с точки зрения ликвидности, поведения участников, торговых часов и чувствительности к новостям и настроениям.
Применяя одну и ту же тестовую логику к товарам, криптовалютам, валютам и фондовым индексам, мы можем определить, является ли обнаруженное неслучайное поведение специфичным для отдельных рынков или проявляется последовательно в разных классах активов.
Это сравнение необходимо для отделения эффектов рыночной структуры от характеристик конкретного инструмента. Оно закладывает основу для последующих результатов тестов, в которых каждый инструмент оценивается независимо по одинаковым правилам.
Запуск экспериментов и запись результатов
Чтобы сохранить целостность данных и предотвратить смешивание результатов, каждый тестовый случай проводится независимо. Результаты каждого эксперимента будут задокументированы в отдельной таблице для максимальной ясности. Для каждого эксперимента в каждой таблице мы будем записывать следующие поля:- Инструмент — тестируемый финансовый инструмент (XAUUSD, BTCUSD, GBPUSD, NAS100).
- Количество сделок — общее число сделок, вызванных этим сценарием в течение периода тестирования.
- Процент прибыльных сделок (%) — доля сделок, закрытых с прибылью.
- Итоговый результат — показатель того, был ли суммарный результат сделок положительным (прибыльным) или отрицательным (убыточным).
Этот структурированный подход позволяет сравнивать результаты по рынкам и сценариям, сохраняя данные чистыми и удобными для анализа.
Эксперимент 1: направленный перекос от открытия к закрытию
Тест направленного перекоса от открытия к закрытию проверяет, склонны ли свечи закрываться выше или ниже цены открытия. Это самый фундаментальный сценарий, который дает представление о базовых направленных тенденциях каждого рынка на выбранном таймфрейме.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 236 | 56.36 | Прибыльно |
| BTCUSD | 331 | 51.06 | Убыточно |
| GBPUSD | 238 | 45.38 | Убыточно |
| NAS100 | 236 | 55.93 | Прибыльно |
Эксперимент 2: бычий перекос после одного медвежьего закрытия
Этот тест проверяет, становится ли бычья свеча более вероятной после одной медвежьей свечи. Он помогает понять, склонен ли рынок разворачиваться после одного нисходящего бара. Советник проверит, следует ли бычья свеча за медвежьей свечой.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 101 | 62.38 | Прибыльно |
| BTCUSD | 161 | 53.42 | Прибыльно |
| GBPUSD | 106 | 46.23 | Убыточно |
| NAS100 | 106 | 62.26 | Прибыльно |
Эксперимент 3: бычий перекос после двух последовательных медвежьих закрытий
Этот тест исследует, повышают ли две последовательные медвежьи свечи вероятность бычьего разворота на следующей свече.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 35 | 57.14 | Прибыльно |
| BTCUSD | 73 | 50.68 | Прибыльно |
| GBPUSD | 47 | 44.68 | Убыточно |
| NAS100 | 40 | 57.50 | Убыточно |
Эксперимент 4: бычий перекос после трех последовательных медвежьих закрытий
Этот тест расширяет предыдущий до трех последовательных медвежьих свечей, проверяя, повышает ли более сильное нисходящее движение вероятность разворота вверх.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 14 | 71.43 | Прибыльно |
| BTCUSD | 35 | 60.00 | Прибыльно |
| GBPUSD | 20 | 35.00 | Убыточно |
| NAS100 | 19 | 52.63 | Прибыльно |
Эксперимент 5: бычий перекос после одного бычьего закрытия
Этот тест оценивает, склонна ли бычья свеча сопровождаться еще одной бычьей свечой, выявляя возможные краткосрочные импульсные тенденции.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 135 | 51.85 | Прибыльно |
| BTCUSD | 170 | 48.82 | Убыточно |
| GBPUSD | 131 | 45.04 | Убыточно |
| NAS100 | 130 | 50.77 | Убыточно |
Эксперимент 6: бычий перекос после двух последовательных бычьих закрытий
Этот тест проверяет, усиливается ли импульс после двух последовательных бычьих свечей и вероятно ли появление третьей.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 69 | 53.62 | Прибыльно |
| BTCUSD | 82 | 43.90 | Убыточно |
| GBPUSD | 72 | 41.67 | Убыточно |
| NAS100 | 64 | 54.69 | Убыточно |
Эксперимент 7: бычий перекос после трех последовательных бычьих закрытий
Этот тест проверяет, повышают ли три последовательные бычьи свечи вероятность четвертой, выявляя более сильный импульс.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 37 | 37.84 | Убыточно |
| BTCUSD | 34 | 52.94 | Прибыльно |
| GBPUSD | 37 | 40.54 | Убыточно |
| NAS100 | 34 | 50.00 | Убыточно |
Эксперимент 8: бычий перекос после краткосрочного минимума Ларри Уильямса
Этот тест проверяет, вероятно ли появление бычьей свечи после краткосрочного свинг-минимума, определенного Ларри Уильямсом. Он сочетает анализ price action и свинг-анализ для выявления потенциального предсказательного поведения.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| XAUUSD | 21 | 61.90 | Прибыльно |
| BTCUSD | 26 | 53.85 | Прибыльно |
| GBPUSD | 23 | 65.22 | Убыточно |
| NAS100 | 29 | 62.07 | Прибыльно |
Доказательства неслучайного поведения рынка
В этом разделе объединены все восемь экспериментов, и внимание сосредоточено на том, что действительно важно, — наблюдаемых процентах прибыльных сделок. Если бы рынки были полностью случайными, бычьи и медвежьи закрытия распределялись бы равномерно около 50% независимо от предшествующих условий. Здесь мы наблюдаем другое.
На нескольких инструментах и в нескольких тестовых случаях поведение цены меняется в зависимости от того, что произошло ранее. Уже одно это ставит под сомнение предположение, что каждая свеча является независимым событием, а прошлое не влияет на будущую динамику.
1. Направленный перекос от открытия к закрытию неоднороден
Тест от открытия к закрытию задает базовую линию. Если бы рынки были случайными, результаты по инструментам должны были бы плотно группироваться около пятидесяти процентов. Вместо этого мы видим расхождение. Золото и Nasdaq показывают процент прибыльных сделок выше пятидесяти пяти процентов и оба завершают тест прибыльно. Bitcoin остается близким к случайному уровню и становится убыточным. GBPUSD падает значительно ниже пятидесяти процентов и показывает слабый результат. Это говорит нам о важном: даже самое базовое поведение цены не идентично на разных рынках; структура и поведение участников имеют значение.
2. Медвежьи закрытия выявляют более сильное прогностическое поведение
Самые существенные доказательства против случайности появляются после медвежьих свечей. После одного нисходящего закрытия золото и Nasdaq показывают процент прибыльных сделок выше шестидесяти. Bitcoin поднимается выше пятидесяти процентов и становится прибыльным. GBPUSD остается слабым.
После двух последовательных медвежьих закрытий золото и Bitcoin по-прежнему удерживают процент прибыльных сделок выше пятидесяти. Даже несмотря на то что Nasdaq теряет прибыльность, процент прибыльных сделок остается повышенным.
После трех последовательных медвежьих закрытий эффект становится яснее. Золото достигает более семидесяти процентов. Bitcoin остается сильным. Nasdaq остается выше пятидесяти процентов. Только GBPUSD продолжает проваливаться.
Эта динамика важна. В случайной системе увеличение числа предшествующих медвежьих свечей не должно повышать вероятность бычьего исхода. Здесь же оно повышает, особенно у золота и Bitcoin.
Это не шум. Это условное поведение.
3. Бычьи закрытия ведут себя совсем иначе
Та же логика, примененная к бычьим последовательностям, дает более слабые и менее стабильные результаты.
После одного восходящего закрытия большинство инструментов опускаются ниже пятидесяти процентов. После двух восходящих закрытий только золото сохраняет небольшой устойчивый перевес (edge). После трех восходящих закрытий результаты продолжают ухудшаться.
Эта асимметрия критически важна. Рынки реагируют на слабость иначе, чем на силу. Возврат к среднему после снижений выглядит более устойчивым, чем продолжение после ростов.
Случайные системы не демонстрируют асимметрию.
4. Краткосрочные минимумы подтверждают структурное поведение
Тест краткосрочного минимума дает одни из самых стабильных результатов.
Золото, Bitcoin и Nasdaq все показывают процент прибыльных сделок выше 60% и завершают тест прибыльно. Даже GBPUSD имеет высокий процент прибыльных сделок, но, несмотря на высокий процент прибыльных сделок, итог остается убыточным.
Это подтверждает, что простые структурные паттерны имеют значение. Четко определенный краткосрочный минимум меняет вероятность следующей свечи.
Это напрямую поддерживает аргумент Ларри Уильямса о том, что цена не движется слепо от бара к бару.
5. Различия между классами активов усиливают аргумент
Результаты различаются по инструментам, но сама эта вариативность значима.
Золото последовательно показывает наиболее заметный бычий перекос в нескольких тестах. Bitcoin демонстрирует условное поведение, которое усиливается после снижений. NAS100 хорошо реагирует на краткосрочную слабость и структуру. GBPUSD остается в основном неэффективным в рамках этих правил.
Если бы рынки были случайными, классы активов не имели бы значения. Здесь они явно имеют значение.
Итоговый вывод
Эти эксперименты не претендуют на абсолютную определенность. Они не утверждают, что позволяют предсказывать рынок наверняка. Они показывают наличие смещения вероятности.
Определенные условия сдвигают шансы от пятидесяти процентов. Уже одно это опровергает чистую случайность.
Рынки не являются полностью предсказуемыми. Однако прошлое поведение цены не исчезает бесследно. Цена реагирует на предшествующее поведение измеримыми способами. Это и есть основа торгового преимущества (edge).
Именно это и предлагал Ларри Уильямс. Более того, благодаря автоматизации, данным и контролируемому тестированию теперь мы можем увидеть это сами.
Заключение
Эта статья ставила перед собой цель ответить на простой, но важный вопрос. Движутся ли рынки полностью случайным образом или прошлая ценовая динамика влияет на то, что происходит дальше?
С помощью контролируемого тестирования, автоматизации и реальных рыночных данных мы показали, что поведение цены распределено неравномерно. Определенные условия последовательно сдвигают вероятности от 50%. Уже этого достаточно, чтобы отвергнуть идею полностью случайного рынка.
Результаты не указывают на достоверность или идеальное прогнозирование. Вместо этого они выявили нечто более практичное. Рынки по-разному реагируют после слабости и после силы. Некоторые структуры повторяются чаще, чем допускает случайность. Некоторые инструменты проявляют эти тенденции яснее, чем другие. Это не совпадения. Это измеримые поведения.
Не менее важен и процесс получения результатов. Вы пошагово прошли через проектирование и разработку тестового советника. Советник был намеренно простым, прозрачным и модульным. Он был создан для проверки идей, а не для оптимизации прибыли. Такой подход необходим при поиске настоящих преимуществ.
Имея эту основу, теперь вы располагаете рабочим каркасом. Вы можете изменять логику, добавлять новые условия, тестировать разные таймфреймы или исследовать другие ценовые паттерны. Те же методы можно повторно использовать для создания исследовательских инструментов, торговых систем или компонентов стратегий, основанных на ясных доказательствах, а не на предположениях.
Этот вывод поддерживает ключевую идею Ларри Уильямса. Рынки не являются полностью предсказуемыми, но прошлое поведение цены не исчезает бесследно. Когда это понятно, разработка систем становится осмысленной.
В следующих частях этой серии мы будем опираться на эти выводы и начнем строить торговые системы, нацеленные на захват этих неслучайных поведений структурированным и повторяемым способом.
Для вашего удобства ниже приложены файлы, чтобы вы могли следовать материалу или точно воспроизвести эксперименты.
| Имя файла | Описание | |
|---|---|---|
| 1. | larryWilliamsNonRandomMarketBehaviorTester.mq5 | Полный исходный код советника, разработанного в этой статье. |
| 2. | configurations.ini | Файл конфигурации, использованный для всех запусков тестера стратегий. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20510
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Рыночные секреты Ларри Уильямса (Часть 4): Автоматизация краткосрочных свинговых максимумов и минимумов в MQL5
Моделирование рынка: Position View (III)
Архитектура прибыльной торговли с усиленной многоуровневой защитой счёта
От начального до среднего уровня: Песочница и MetaTrader
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования