Рыночные секреты Ларри Уильямса (Часть 3): Проверка неслучайного поведения рынка с помощью MQL5
Введение
Действительно ли финансовые рынки случайны или в них существуют закономерности, которые можно измерить и проверить? Этот вопрос лежит в основе каждой когда-либо созданной торговой системы. Если движение цены полностью случайно, то любая попытка построить стратегию, индикатор или автоматическую торговую систему в конечном счете бессмысленна. Не было бы никакого статистического преимущества, которое можно найти, — только случайность.
В этой статье мы возвращаемся к этим идеям с современной практической точки зрения (более подробное обсуждение базовых статистических понятий см. в предыдущей статье). Вместо того чтобы принимать выводы на веру, мы используем MQL5 для воспроизведения и расширения экспериментов Уильямса с помощью кода. Написав советник, мы проверим, возникают ли определенные модели поведения цены чаще, чем можно было бы ожидать случайно. Цель состоит не в том, чтобы предсказывать рынки с уверенностью, а в том, чтобы определить, существуют ли небольшие измеримые статистические перекосы.
Это важно, потому что даже небольшой статистический перекос может стать основой торгового преимущества. Если прошлое поведение цены никак не связано с будущими результатами, системная торговля не может работать. Однако если рынки хотя бы частично неслучайны, то структура, логика и вероятность становятся значимыми инструментами для трейдеров.
Как мы тестируем неслучайное поведение рынка
Во введении мы сформулировали простую, но важную идею. Если бы рынки были действительно случайными, поведение цены напоминало бы подбрасывание монеты. Каждая свеча была бы независима от предыдущей, а вероятность закрытия вверх или вниз оставалась бы фиксированной на уровне пятидесяти процентов независимо от того, что произошло ранее.
Ларри Уильямс поставил это предположение под сомнение, задав практический вопрос. Что происходит после того, как цена закрывается вверх или вниз? Если прошлое не влияет на будущее поведение цены, результат следующей свечи не должен был бы меняться. Его исследования показали, что на практике происходит иначе.
Чтобы проверить эту концепцию практически и измеримо, мы используем автоматизированный подход. Вместо того чтобы опираться на предположения или выборочные примеры, мы позволим коду проанализировать исторические ценовые данные и посчитать, как часто возникают определенные условия. Каждый тест предназначен для ответа на конкретный вероятностный вопрос о поведении цены.
Эксперименты, которые мы проведем, делятся на три широкие категории. Сначала мы проверяем, существует ли общий направленный перекос внутри одной свечи. Это отвечает на фундаментальный вопрос: закрывается ли цена выше открытия чаще, чем можно было бы ожидать случайно.
Во-вторых, мы тестируем условную вероятность. Здесь мы спрашиваем, ведет ли себя рынок иначе после одной или нескольких последовательных бычьих и медвежьих свечей. Если бы рынки были случайными, вероятность того, что следующая свеча будет бычьей или медвежьей, оставалась бы неизменной. Если вероятности сдвигаются, значит цена явно реагирует на свое недавнее прошлое.
В-третьих, мы проверяем простую форму рыночной структуры. Ларри Уильямс описывал краткосрочный минимум как трехбарный паттерн, при котором цена формирует минимум, а затем не продолжает снижение. Если у этого паттерна есть предсказательная ценность, следующая свеча должна закрываться выше чаще, чем предполагала бы одна лишь случайность.
Чтобы охватить эти идеи, мы автоматизируем следующие восемь тестовых сценариев:
Направленный перекос от открытия к закрытию
Мы измеряем, как часто свеча закрывается бычьей, то есть когда цена закрытия выше цены открытия. На действительно случайном рынке бычьи и медвежьи закрытия должны происходить примерно по пятьдесят процентов времени каждое.
Бычья реакция после одного медвежьего закрытия
Мы проверяем, как часто следующая свеча закрывается бычьей после одной медвежьей свечи. Это помогает понять, привлекает ли краткосрочный откат покупательское давление.
Бычья реакция после двух последовательных медвежьих закрытий
Здесь мы проверяем, повышается ли вероятность бычьего закрытия после двух медвежьих свечей подряд. Это исследует, склонны ли краткосрочные снижения создавать условия для отскока.
Бычья реакция после трех последовательных медвежьих закрытий
Этот тест развивает ту же идею дальше, изучая поведение цены после более глубокого краткосрочного снижения.
Бычья реакция после одного бычьего закрытия
Мы проверяем, стремится ли бычий импульс продолжаться или угасать после одной бычьей свечи.
Бычья реакция после двух последовательных бычьих закрытий
Этот эксперимент проверяет, следует ли сила за силой или цена останавливается после краткого ралли.
Бычья реакция после трех последовательных бычьих закрытий
Этот тест проверяет, приводит ли продолжительное краткосрочное давление покупателей к продолжению движения или к истощению.
Бычья реакция после краткосрочного рыночного минимума
На основе определения Ларри Уильямса мы выявляем краткосрочный минимум с помощью трехбарного ценового паттерна и измеряем, как часто следующая свеча закрывается бычьей.
Каждый из этих тестов отвечает на простой вопрос. Влияет ли поведение предыдущих свечей на вероятность следующей? Если ответ всегда был бы равен пятидесяти процентам, рынки вели бы себя как подбрасывание монеты. Любое устойчивое отклонение от этого уровня указывает на неслучайное поведение.
Когда эти тестовые сценарии четко определены, мы можем перейти к технической части. В следующем разделе мы реализуем советник, который позволит запускать каждый эксперимент отдельно на разных рынках и наблюдать результаты непосредственно по историческим данным.
Реализация экспериментов в MQL5
Чтобы проверить, действительно ли такие модели рыночного поведения носят неслучайный характер, нам нужна не только статистика. Мы хотим смоделировать реальные торговые условия. Это означает открытие позиции на открытии свечи, закрытие ее на закрытии свечи и запись того, что фактически происходит. Такой подход позволяет оценивать вероятности, а также отвечать на более практический вопрос: можно ли позже превратить эти поведения в торговые идеи?
Для начала откройте MetaEditor 5, создайте новый советник, выберите пустой шаблон и назовите файл larryWilliamsNonRandomMarketBehaviorTester.mq5. После создания вставьте предоставленный исходный код в файл и сохраните его.
//+------------------------------------------------------------------+ //| larryWilliamsNonRandomMarketBehaviorTester.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/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." //+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- Bid and Ask double askPrice; //+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); 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); } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ 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/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/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."
Первый раздел определяет имя файла, сведения об авторе, версию и описание. Эти свойства важны, потому что они описывают назначение советника при добавлении на график. В данном случае описание ясно указывает, что советник запускает один статистический эксперимент за раз, а эксперимент выбирается с помощью выбора из выпадающего списка входных параметров. Это напрямую соответствует целям статьи.
Подключение стандартной библиотеки
//+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
Далее мы подключаем стандартную торговую библиотеку. Эта библиотека предоставляет доступ к классу CTrade, который обеспечивает безопасное и структурированное выполнение ордеров. Использование этого класса рекомендуется, поскольку он упрощает управление сделками и снижает риск ошибок выполнения.
Пользовательские входные настройки//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
Раздел входных параметров определяет параметры, которыми пользователь может управлять при добавлении советника на график. Мы задаем магический номер для уникальной идентификации сделок, открытых этим советником, чтобы они не мешали другим стратегиям. Мы также позволяем пользователю выбрать таймфрейм эксперимента. Это делает советник гибким и пригодным для повторного использования на разных настройках графика.
Глобальные торговые объекты и переменные
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- Bid and Ask double askPrice;
Затем мы создаем глобальный объект CTrade. Этот объект будет использоваться во всем советнике для открытия и закрытия позиций. Мы также объявляем переменные для рыночных цен. На этом этапе мы получаем только цену Ask, чего достаточно для открытия сделок на покупку во время наших экспериментов.
Логика инициализации
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); }
Функция инициализации выполняется один раз, когда советник устанавливается на график. Здесь мы назначаем магический номер объекту CTrade. Этот шаг важен, потому что он гарантирует, что каждую сделку, открытую советником, позже можно будет корректно отслеживать и управлять ею.
Логика деинициализации
//+------------------------------------------------------------------+ //| 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); }
Функция обработки тиков выполняется непрерывно по мере поступления новых рыночных данных. Пока она получает только текущую цену Ask. Позже эта функция станет центром управления нашими экспериментами. Она будет обнаруживать новые свечи, оценивать тестовые условия, открывать сделки и закрывать их в нужный момент.
Обработчик торговых транзакций
//+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { }
Наконец, мы определяем функцию торговой транзакции. Эта функция срабатывает всякий раз, когда происходит торговое действие, например открытие или закрытие позиции. Хотя сейчас она пустая, позже она поможет отслеживать результаты сделок и собирать результаты эксперимента.
На этом этапе советник не показывает видимой активности на графике. Это сделано намеренно. Мы построили чистый и надежный каркас, соответствующий лучшим практикам. В следующем разделе мы начнем добавлять логику для обнаружения новых свечей, применения наших неслучайных тестовых сценариев и выполнения сделок, чтобы наблюдать реальное поведение рынка в действии.
Теперь, когда каркас советника готов, следующий этап — научить нашу программу распознавать поведение рынка. На этом этапе мы еще не торгуем. Наша цель проста и важна: мы хотим, чтобы советник корректно обнаруживал каждый паттерн, представляющий один из наших неслучайных тестовых сценариев.
Прежде чем советник сможет что-либо тестировать, он должен четко понимать, какой эксперимент выполняется. Поэтому первый шаг — определить механизм переключения между тестовыми сценариями чистым и контролируемым способом.
Определение режимов тестирования
Сразу под подключением стандартной библиотеки мы вводим пользовательское перечисление, в котором перечислены все неслучайные эксперименты, которые мы хотим запустить. Каждое значение представляет отдельный тип рыночного поведения, который мы хотим изучить.
//--- CUSTOM ENUMERATIONS //+------------------------------------------------------------------+ //| Non-random market behavior test modes | //+------------------------------------------------------------------+ 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 };
Это перечисление позволяет советнику работать в одном тестовом режиме за раз. Когда советник устанавливается на график, пользователь выбирает эксперимент из выпадающего списка, заданного входным параметром. Такой дизайн сохраняет логику простой и не смешивает результаты разных тестов.
На этом этапе мы еще не тестируем вероятности. Мы только определяем структуру. Эта структура будет направлять все последующее обнаружение паттернов и принятие решений.
Обнаружение момента открытия бара
Все наши эксперименты оцениваются один раз — когда открывается новая свеча. Это критически важно. Если реагировать на каждый тик, мы внесем шум и дублирующие сигналы. Чтобы избежать этого, мы обнаруживаем паттерны только при формировании нового бара.
Для этого мы определяем небольшую, но мощную служебную функцию, которая проверяет, открылся ли новый бар на выбранном таймфрейме.
//--- UTILITY FUNCTIONS //+------------------------------------------------------------------+ //| 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; }
Функция сравнивает время открытия текущего бара с последним записанным временем бара. Если время изменилось, значит сформировался новый бар.
Чтобы поддержать эту логику, мы также объявляем глобальную переменную, которая хранит время открытия самого последнего бара.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ... //--- 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; }
Чтобы функция была понятной и пригодной для повторного использования, мы передаем четыре входных параметра: тестируемый символ, таймфрейм, количество последовательных баров для оценки и направление этих баров. Чтобы четко описать направление бара, мы вводим небольшое перечисление для бычьих и медвежьих закрытий.
//--- CUSTOM ENUMERATIONS //+------------------------------------------------------------------+ //| Non-random market behavior test modes | //+------------------------------------------------------------------+ ... //+------------------------------------------------------------------+ //| Direction of candle close relative to open | //+------------------------------------------------------------------+ enum ENUM_BAR_CLOSE_STATE { BAR_CLOSE_UP, BAR_CLOSE_DOWN };
Функция проходит только по завершенным свечам. Она начинает с последнего закрытого бара и движется назад. Для каждого бара она сравнивает цены открытия и закрытия и проверяет, соответствуют ли они ожидаемому направлению. Если какой-либо бар не проходит условие, функция возвращает false немедленно. Если все бары совпадают, функция возвращает true.
Эта единственная функция поддерживает шесть из восьми наших экспериментов. Это основной строительный блок наших неслучайных тестов, и позже он будет управлять реальными сделками.
Обнаружение краткосрочного минимума Ларри Уильямса
Последний паттерн, который нам нужно обнаруживать, более структурный. Ларри Уильямс определяет краткосрочный минимум как трехбарную формацию, где средний бар формирует свинг-минимум, а по обе стороны от него находятся более высокие минимумы.
Однако он также добавляет два важных фильтра. Свинг-бар не должен быть внешним баром (outside bar), а самый последний бар не должен быть внутренним баром (inside bar). Эти условия помогают исключить слабые или вводящие в заблуждение сигналы.
Чтобы обнаружить этот паттерн, мы создаем специальную функцию, которая анализирует последние три завершенных бара.
//+------------------------------------------------------------------+ //| Detects a Larry Williams short-term low on the last three bars | //| Bar index 2 must be a swing low with higher lows on both sides | //| Bar 2 must NOT be an outside bar | //| Bar 1 must NOT be an inside bar | //+------------------------------------------------------------------+ bool IsLarryWilliamsShortTermLow(string symbol, ENUM_TIMEFRAMES tf){ //--- Price data for the three bars 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); //--- Condition 1: Bar 2 must be a swing low bool isSwingLow = (low2 < low1) && (low2 < low3); if(!isSwingLow){ return false; } //--- Condition 2: Bar 2 must NOT be an outside bar relative to bar 3 bool isOutsideBar = (high2 > high3) && (low2 < low3); if(isOutsideBar){ return false; } //--- Condition 3: Bar 1 must NOT be an inside bar relative to bar 2 bool isInsideBar = (high1 < high2) && (low1 > low2); if(isInsideBar){ return false; } //--- All conditions satisfied return true; }
Сначала она проверяет, имеет ли средний бар самый низкий минимум. Затем проверяет, что средний бар не является внешним баром относительно предыдущего. Наконец, она подтверждает, что самый последний бар не является внутренним баром относительно свинг-бара.
Только если все условия выполнены, функция возвращает true.
Выбор активного эксперимента
Чтобы упростить тестирование, мы вводим входной параметр пользователя, позволяющий трейдеру выбрать, какой неслучайный тестовый режим запускать. Этот параметр использует перечисление, определенное ранее, и отображается в виде выпадающего списка при добавлении советника на график.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ ... input group "Trade And Risk Management" input ENUM_NON_RANDOM_TEST_MODE nonRandomTestMode = TEST_OPEN_TO_CLOSE_BIAS;
Такой подход позволяет использовать один и тот же советник для всех экспериментов без изменения кода каждый раз.
Проверка обнаружения паттернов внутри OnTick
Когда вся логика обнаружения готова, мы соединяем все внутри функции обработки тиков.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Execute a buy trade when the Open-to-Close bias test mode is selected if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ Print("EA should open a long position"); } //--- Enter a buy position to test bullish bias following a single down close if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ Print("Previous bearish bar"); } } //--- Enter a buy position to test bullish bias following two consecutive down closes if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ Print("Two consecutive bearish bars"); } } //--- Enter a buy position to test bullish bias following three consecutive down closes if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ Print("Three consecutive bearish bars"); } } //--- Enter a buy position to test bullish bias following a single up close if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ Print("Previous bullish bar"); } } //--- Enter a buy position to test bullish bias following two consecutive up closes if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ Print("Two consecutive bullish bars"); } } //--- Enter a buy position to test bullish bias following three consecutive up closes if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ Print("Three consecutive bullish bars"); } } //--- Enter a buy position when a Larry Williams-defined short-term low pattern is detected if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ Print("Short-term low"); } } } }
Сначала советник проверяет, сформировался ли новый бар. Если нет, ничего не происходит. Когда новый бар обнаружен, советник оценивает только выбранный тестовый режим. Для каждого случая он вызывает соответствующую функцию обнаружения и выводит сообщение, когда паттерн найден.
На этом этапе сделки не открываются. Выводимые сообщения выступают подтверждающими сигналами. Они позволяют визуально проверить, что паттерны обнаруживаются в правильный момент и при правильных условиях.
Этот шаг критически важен. Перед добавлением реальной торговой логики мы должны убедиться, что обнаружение паттернов работает точно. Построение на ошибочной логике сделало бы все последующие результаты недействительными.
В следующем разделе мы заменим эти операторы печати фактической логикой выполнения сделок. В этот момент наш советник перейдет от наблюдения к эксперименту, позволяя проверить, могут ли эти неслучайные поведения превратиться в реальные рыночные результаты.
На этом этапе наш советник уже знает, как обнаруживать все паттерны, необходимые для тестов неслучайного поведения рынка. Следующий шаг — превратить эти сигналы в реальные рыночные действия. Именно здесь наблюдение становится экспериментом.
Для согласованности с исходной логикой Ларри Уильямса наш советник будет открывать только длинные позиции. Каждая сделка открывается в начале нового бара и закрывается в конце этого бара. Уровни стоп-лосс и тейк-профит не используются. Цель состоит не в управлении сделкой, а в измерении. Мы хотим наблюдать, как цена ведет себя сразу после появления определенного условия.
Из-за такой конструкции советник должен надежно уметь делать три вещи. Во-первых, он должен знать, есть ли у него уже открытая позиция. Во-вторых, он должен уметь аккуратно закрывать эту позицию. В-третьих, он должен открывать новую позицию только тогда, когда выполнено выбранное тестовое условие.
Проверка активной позиции
Перед открытием новой сделки советнику нужно подтвердить, есть ли у него уже активная позиция на покупку. Поскольку на одном счете могут существовать несколько советников или ручные сделки, мы используем магический номер для уникальной идентификации сделок, открытых именно этим советником.
Для этого мы определяем функцию, которая просматривает все открытые позиции и проверяет, есть ли среди них позиция с совпадающим магическим номером. Если такая позиция существует, функция возвращает true. В противном случае она возвращает false.
//+------------------------------------------------------------------+ //| 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 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); } } } } }
Такой подход гарантирует, что каждый тест следует одним и тем же правилам. Удержание ровно один бар. Ничего больше.
Открытие новой длинной позиции
Когда управление позициями готово, мы определяем простую функцию для открытия рыночного ордера на покупку. Функция принимает два входа. Первый — цена входа, то есть текущая цена Ask, уже отслеживаемая советником. Второй — размер позиции.
//+------------------------------------------------------------------+ //| Function to open a market buy position | //+------------------------------------------------------------------+ 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, подтверждая, что ордер успешно размещен.
Чтобы сделать это гибким, мы позволяем пользователю задать предпочитаемый размер лота через входной параметр. Это сохраняет пригодность советника для разных размеров счетов, не нарушая логику эксперимента.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ ... input double positionSize = 0.01;
Выполнение логики внутри OnTick
Теперь все соединяется вместе.
Каждый раз, когда обнаруживается новый бар, советник выполняет следующие шаги по порядку. Сначала он закрывает любую существующую позицию, открытую этим советником. Это отмечает конец предыдущего тестового цикла. Добавляется короткая пауза, чтобы платформа корректно обработала закрытие.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Close any existing buy positions for this EA before opening a new one if(IsThereAnActiveBuyPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(100); } ... } }
Затем советник проверяет, какой тестовый режим выбран в данный момент. На основе этого выбора он оценивает соответствующее условие. Если условие выполнено, новая позиция на покупку открывается немедленно в начале нового бара.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ ... //--- Execute a buy trade when the Open-to-Close bias test mode is selected if(nonRandomTestMode == TEST_OPEN_TO_CLOSE_BIAS ){ OpenBuy(askPrice, positionSize); } //--- Enter a buy position to test bullish bias following a single down close if(nonRandomTestMode == TEST_AFTER_ONE_DOWN_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following two consecutive down closes if(nonRandomTestMode == TEST_AFTER_TWO_DOWN_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following three consecutive down closes if(nonRandomTestMode == TEST_AFTER_THREE_DOWN_CLOSES){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_DOWN)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following a single up close if(nonRandomTestMode == TEST_AFTER_ONE_UP_CLOSE ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 1, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following two consecutive up closes if(nonRandomTestMode == TEST_AFTER_TWO_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 2, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position to test bullish bias following three consecutive up closes if(nonRandomTestMode == TEST_AFTER_THREE_UP_CLOSES ){ if(IsConsecutiveBarCloseState(_Symbol, timeframe, 3, BAR_CLOSE_UP)){ OpenBuy(askPrice, positionSize); } } //--- Enter a buy position when a Larry Williams-defined short-term low pattern is detected if(nonRandomTestMode == TEST_AFTER_SHORT_TERM_LOW ){ if(IsLarryWilliamsShortTermLow(_Symbol, timeframe)){ OpenBuy(askPrice, positionSize); } } } }
Единственное изменение по сравнению с предыдущей фазой тестирования простое, но важное. Мы заменили все вызовы Print фактическими вызовами выполнения сделок. Логика обнаружения осталась неизменной. Это подтверждает, что наша предыдущая работа по проверке была правильной.
На этом этапе советник полностью работоспособен. Он обнаруживает паттерны, открывает сделки, закрывает их после одного бара и последовательно повторяет процесс во всех тестовых случаях.
Настройка графика для наглядного визуального тестирования
Поскольку этот советник предназначен для визуальных и статистических экспериментов, важно, чтобы мы ясно видели, что происходит на графике во время выполнения тестов. Мы хотим сразу распознавать бычьи и медвежьи бары, видеть входы и выходы из сделок и избегать лишнего визуального шума, который может отвлекать от наблюдения.
По этой причине мы вводим небольшую вспомогательную функцию, которая настраивает внешний вид графика сразу после установки советника.
//+------------------------------------------------------------------+ //| 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; }
Во время экспериментов визуальная обратная связь играет ключевую роль. Хотя наши выводы в конечном счете будут основаны на данных и статистике, наблюдение за поведением цены при каждом тестовом условии помогает проверить предположения и рано заметить аномалии.
Конфигурация, применяемая этой функцией, достигает трех основных целей:
- Ясность — белый фон с черными элементами переднего плана обеспечивает максимальный контраст.
- Визуальное выделение направления движения — бычьи бары четко отличаются от медвежьих с помощью современных интуитивных цветов.
- Фокус — линии сетки удаляются, чтобы внимание оставалось на движении цены и исполненных сделках.
Функция настройки графика вызывается внутри функции OnInit.
//+------------------------------------------------------------------+ //| 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); }
Это гарантирует, что график настраивается сразу после запуска советника. Если по какой-либо причине платформа не сможет применить настройку, советник сообщит о проблеме и остановит инициализацию.
С этим добавлением разработка нашего тестового советника завершена. В следующем разделе мы начнем запускать эти эксперименты на разных рынках. Мы будем наблюдать результаты, сравнивать исходы по классам активов и наконец ответим на вопрос, с которого началось это исследование: действительно ли рынки случайны или они оставляют измеримые следы?
Выполнение тестов и наблюдения за поведением рынка
До этого момента наше внимание было сосредоточено на идеях и подготовке. Мы рассмотрели аргумент Ларри Уильямса о том, что поведение цены не является полностью случайным, перевели эти идеи в четко определенные тестовые сценарии и построили советник, способный последовательно выполнять эти тесты.
Советник намеренно прост. Он не оптимизирован; у него нет ни стоп-лосса, ни тейк-профита, и он не управляет сделками динамически. Каждая позиция открывается в начале нового бара и закрывается в конце того же бара. В один момент времени может быть открыта только одна сделка, и каждый тест выполняется независимо от остальных.
Наша цель здесь — не построить прибыльную торговую систему и не оптимизировать входы, выходы или управление риском. Вместо этого мы хотим ответить на гораздо более простой и фундаментальный вопрос:
Возникают ли определенные модели поведения цены чаще, чем предполагала бы случайность, и можно ли наблюдать их последовательно на разных рынках?
Среда тестирования
Чтобы результаты наших экспериментов были согласованными, сопоставимыми и воспроизводимыми, все тесты проводятся при одинаковых фиксированных условиях. Параметры не настраиваются от одного рынка к другому, и оптимизация не выполняется ни на одном этапе.
Все эксперименты выполняются на дневном таймфрейме (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: бычий перекос после краткосрочного минимума Ларри Уильямса
Этот тест проверяет, вероятно ли появление бычьей свечи после краткосрочного свинг-минимума, определенного Ларри Уильямсом. Он сочетает анализ поведения цены и свинг-анализ для выявления потенциального предсказательного поведения.
| Количество сделок | Процент прибыльных сделок (%) | Итоговые результаты | |
|---|---|---|---|
| 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 не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Моделирование рынка: Position View (III)
От начального до среднего уровня: Песочница и MetaTrader
Рыночные секреты Ларри Уильямса (Часть 4): Автоматизация краткосрочных свинговых максимумов и минимумов в MQL5
Нейросети в трейдинге: От рыночного шума к устойчивому торговому плану (Окончание)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования