Рыночные секреты Ларри Уильямса (Часть 7): Эмпирическое исследование концепции "торгового дня недели"
Введение
Большинство торговых систем строится на ценовых моделях, индикаторах или моделях волатильности. Однако время часто воспринимают как нейтральный фон: оно будто просто проходит, пока ключевую роль играет цена. Сделки открываются в понедельник так же, как и в четверг, словно рынки ведут себя одинаково на протяжении всей недели.
Ларри Уильямс поставил это предположение под сомнение еще десятилетия назад. Благодаря обширным наблюдениям и тестированию он обнаружил, что рынки склонны вести себя по-разному в разные дни. В одни дни попытки пробоя чаще получают развитие, а в другие дни чаще наблюдаются неудачные движения или нерешительность. Идея проста, но ее последствия важны для краткосрочных трейдеров.
Вместо оптимизации входов, выходов или управления риском эта статья делает шаг назад и задает более базовый вопрос. Если одна и та же стратегия применяется каждый день, все ли дни одинаково влияют на ее результаты? Или не оказываются ли некоторые дни фактически более значимыми, чем другие? Чтобы проверить это, мы построим контролируемый эксперимент без лишней сложности. Используется один дневной вход на основе волатильности, позиции удерживаются только одну сессию, а результативность оценивается исключительно по проценту прибыльных сделок. Упрощая систему до сути, мы даем данным возможность говорить ясно.
Цель — не построить прибыльную торговую систему, а рассмотреть поведение рынка через призму времени. Тем самым мы закладываем основу структурированной методики, которую трейдеры смогут использовать для тестирования, проверки и уточнения концепции торгового дня недели на любом рынке.
Концепция торгового дня недели
Ларри Уильямс заметил, что рыночные возможности распределяются по времени неравномерно. В его исследованиях ценовое движение, сгруппированное по календарным дням, не выглядело случайным. В одни дни чаще наблюдалось более дальнейшее развитие движения, тогда как в другие движения чаще останавливались или завершались неудачей. Такое поведение было связано не с конкретным индикатором или паттерном, а с самим временем.
Ключевая идея состоит в том, что время может работать как условный фильтр. Если в определенные дни рынок с большей вероятностью расширяет диапазон или формирует трендовое движение, сделки, открытые в эти дни, имеют иной профиль вероятностей, чем сделки в дни с менее выраженной реакцией рынка. Уильямс не утверждал, что конкретный день гарантирует успех; скорее, некоторые дни статистически предлагают более благоприятные условия для краткосрочной торговли, чем другие.
Эта идея особенно актуальна для краткосрочных торговых систем. Такие системы работают с коротким временем удержания позиции и почти не прощают ошибок. Даже незначительное улучшение отбора сделок может заметно повлиять на результат. Если стратегия торгует одинаково каждый день, она может непреднамеренно усреднять хорошие условия вместе с плохими. Фильтрация сделок по дню недели может снижать рыночную экспозицию в периоды, которые исторически показывали менее благоприятное поведение, тем самым повышая общую эффективность без изменения базовой логики входа.
Концепция Trade Day of the Week хорошо подходит для эмпирического тестирования, поскольку она четко определена, измерима и воспроизводима. Дни недели — это объективные временные категории, которые не меняются от платформы к платформе или от источника данных к источнику. Каждый торговый день можно однозначно классифицировать, а такие показатели, как процент прибыльных сделок, можно напрямую рассчитывать и сравнивать. Это делает концепцию удобной для статистического анализа, walk-forward-тестирования и воспроизведения на разных рынках и периодах.
Изолируя время как переменную и сохраняя торговую логику простой, можно оценить, существует ли закономерность по дням недели и сохраняется ли оно во времени. Такой подход соответствует исходному акценту Уильямса на наблюдении и проверке, а не на предположениях, позволяя данным подтвердить или опровергнуть наличие рыночного поведения, зависящего от времени.
Упрощение эксперимента
При тестировании рыночной гипотезы, связанной со временем, сложность быстро превращается в проблему. Чем больше условий, фильтров и правил управления добавляется в систему, тем труднее понять, что именно определяет результат. Поскольку цель этого исследования — проверить, предлагают ли определенные дни стабильно лучшие торговые условия, все несущественные компоненты нужно убрать.
По этой причине эксперимент намеренно не использует стандартные приемы управления сделкой, такие как стоп-лоссы, цели тейк-профита, трейлинг-стопы и частичные выходы. В реальной торговле эти инструменты важны, но они добавляют дополнительные уровни принятия решений, которые могут скрыть влияние времени. Например, срабатывание стоп-лосса может больше говорить о внутридневном шуме, чем о том, был ли конкретный день благоприятен для направленного движения.
Вместо этого каждая сделка следует одному единообразному правилу. Позиция открывается с использованием входа на основе волатильности по Ларри Уильямсу и удерживается до конца торгового дня, после чего закрывается независимо от прибыли или убытка. Это гарантирует, что каждая сделка отражает полный дневной результат, а итоги напрямую сопоставимы по всем дням недели.
Сама логика входа намеренно минимальна. Вместо опоры на подтверждения свингов или распознавание паттернов система использует диапазон предыдущего дня, чтобы спроецировать уровень пробоя от цены открытия текущего дня. Такой подход остается близким к исходной работе Уильямса, сохраняя сигнал объективным и легко воспроизводимым. Система либо входит в рынок, либо нет, что устраняет неоднозначность в интерпретации сигнала.
Упрощая структуру таким образом, эксперимент изолирует одну переменную — время. Поэтому любое наблюдаемое различие в доле прибыльных сделок между днями можно отнести к дню недели, а не к управлению сделкой, настройке индикаторов или дискреционным фильтрам. Это создает чистую базу для последующего расширения, которую позднее можно использовать после того, как наличие или отсутствие закономерности по дням будет установлено.
Создание советника для тестирования торгового дня недели
Перед переходом к реализации важно уточнить задачу этого раздела. Наша цель — не представить готовую систему типа «черный ящик», а построить исследовательский инструмент для контролируемого тестирования, который позволит проверить в чистой и воспроизводимой постановке идею Ларри Уильямса Trade Day of the Week . Каждый добавляемый компонент выполняет конкретную задачу и поддерживает логический шаг в схеме тестирования.
Предварительные требования
Чтобы эффективно следовать этому разделу, предполагается несколько предварительных условий.
Во-первых, предполагается практическое знакомство с языком программирования MQL5. Базовые понятия, такие как переменные, функции, условная логика, циклы, перечисления, структуры и использование стандартных библиотек, уже должны быть понятны. Если эти основы пока неясны, официальная справка MQL5 станет отличной отправной точкой.
Во-вторых, требуется предварительный опыт работы с MetaTrader 5 как торговой платформой. Мы опираемся на базовые операции платформы: открытие графиков, прикрепление советников, навигацию в Тестере стратегий и работу с разными таймфреймами.
В-третьих, предполагается уверенная работа с MetaEditor. Умение создавать новые исходные файлы, писать код, компилировать программы и проверять ошибки компиляции необходимо для продуктивного освоения этого раздела.
Для практического обучения полный исходный код советника приложен под именем lwTDWStudy.mq5. По мере пошаговой сборки советника настоятельно рекомендуется держать этот файл открытым в отдельной вкладке для сверки. Освоение программирования требует активного сравнения, а не пассивного чтения материала.
Создание основы советника
Начнем с создания нового пустого файла советника в MetaEditor и вставки начального каркаса кода.
//+------------------------------------------------------------------+ //| lwTDWStudy.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Пользовательские перечисления | //+------------------------------------------------------------------+ enum ENUM_TDW_MODE { TDW_ALL_DAYS, TDW_SELECTED_DAYS }; //+------------------------------------------------------------------+ //| Стандартные библиотеки | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Входные параметры пользователя | //+------------------------------------------------------------------+ input group "Информация" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "Фильтры TDW" input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS; input bool tradeSunday = false; input bool tradeMonday = true; input bool tradeTuesday = false; input bool tradeWednesday = false; input bool tradeThursday = false; input bool tradeFriday = false; input bool tradeSaturday = false; input group "Параметры пробоя волатильности" input double inpBuyRangeMultiplier = 0.50; input group "Торговля и управление риском" input double positionSize = 0.1; //+------------------------------------------------------------------+ //| Глобальные переменные | //+------------------------------------------------------------------+ //--- Создаем объект CTrade для обработки торговых операций CTrade Trade; //--- Для отслеживания текущих рыночных цен для покупки (Ask) и продажи (Bid) double askPrice; double bidPrice; //--- Для хранения текущего времени datetime currentTime; //+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ //--- Назначаем уникальный magic number для идентификации сделок этого советника Trade.SetExpertMagicNumber(magicNumber); return(INIT_SUCCEEDED); }
Эта основа задает метаданные, подключает необходимые библиотеки, определяет входные параметры пользователя и подготавливает глобальные переменные, которые будут поддерживать всю дальнейшую логику.
Перечисление, задающее режим торгового дня недели, вводится уже в начале. Оно позволяет советнику работать в двух разных конфигурациях. Один режим торгует каждый день без фильтрации и служит базой для сравнения. Второй режим ограничивает торговлю выбранными пользователем днями, позволяя эмпирически проверять закономерность по дням недели.
Подключение стандартной библиотеки Trade позволяет передать исполнение заявок и управление позициями надежному, хорошо протестированному интерфейсу. Благодаря этому советник сосредоточен на исследовательской логике, а не на низкоуровневой обработке торговых операций.
Входные параметры пользователя сгруппированы логически. Информационные параметры задают magic number и используемый таймфрейм. Фильтры торгового дня задают параметры выбора дней недели. Параметры пробоя волатильности позволяют настроить расчет уровня входа. Параметры торговли и риска задают размер позиции. Каждая группа отражает отдельный концептуальный слой системы.
Глобальные переменные объявляются для отслеживания цен, времени и состояния. Эти переменные служат общим хранилищем состояния, через которое взаимодействуют все компоненты советника.
Определение начала нового торгового дня
Поскольку эксперимент работает в дневном цикле принятия решений, критически важно определить открытие нового бара. Для этого мы вводим служебную функцию, которая сравнивает временную метку самого последнего бара с сохраненным значением.
//+------------------------------------------------------------------+ //| Функция проверки нового бара на заданном таймфрейме графика| //+------------------------------------------------------------------+ 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, которая инициализируется при запуске советника.
//--- Для отслеживания открытия нового бара datetime lastBarOpenTime;
Инициализация нулем гарантирует корректное обнаружение первого бара.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Инициализируем глобальные переменные lastBarOpenTime = 0; return(INIT_SUCCEEDED); }
Представление уровней пробоя волатильности
Логика пробоя волатильности по Ларри Уильямсу требует двух производных значений: диапазона предыдущего дня и расчетного уровня входа на текущий день. Вместо управления ими как отдельными переменными мы определяем специальную структуру для хранения обоих значений.
//--- Хранит все ценовые уровни, полученные из расчетов пробоя волатильности Ларри Уильямса struct MqlLwVolatilityLevels { double yesterdayRange; double buyEntryPrice; }; MqlLwVolatilityLevels lwVolatilityLevels;
Эта структура объединяет связанные данные в одну логическую единицу, повышая читаемость и снижая риск несогласованного состояния. Экземпляр структуры объявляется глобально и сбрасывается в ноль при инициализации, чтобы обеспечить чистое начальное состояние.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Сбрасываем уровни волатильности ZeroMemory(lwVolatilityLevels); return(INIT_SUCCEEDED); }
Измерение диапазона предыдущего дня
Для расчета дневной волатильности мы вводим функцию, которая возвращает диапазон бара с заданным индексом. Функция получает максимум и минимум напрямую из исторических данных и возвращает их разницу.
//+------------------------------------------------------------------+ //| Возвращает ценовой диапазон бара (high - low) по заданному индексу | //+------------------------------------------------------------------+ double GetBarRange(const string symbol, ENUM_TIMEFRAMES tf, int index){ double high = iHigh(symbol, tf, index); double low = iLow (symbol, tf, index); if(high == 0.0 || low == 0.0){ return 0.0; } return NormalizeDouble(high - low, Digits()); }
Защитная проверка не позволяет некорректным ценовым значениям распространяться по системе. Нормализация согласует результат с точностью символа. Эта функция пригодна для повторного использования и изолирует расчет диапазона от логики более высокого уровня.
Определение уровня входа в покупку
Цена входа на пробой рассчитывается путем прибавления доли диапазона предыдущего дня к цене открытия текущего дня. Этот расчет вынесен в небольшую специализированную функцию, которая получает необходимые входные данные и возвращает расчетную цену.
//+------------------------------------------------------------------+ //| Рассчитывает цену входа для бычьего пробоя по открытию дня и диапазону вчерашнего дня | //+------------------------------------------------------------------+ double CalculateBuyEntryPrice(double todayOpen, double yesterdayRange, double buyMultiplier){ return todayOpen + (yesterdayRange * buyMultiplier); }
Выделение этой логики делает намерение явным и освобождает функцию OnTick от лишней арифметики.
Обновление дневных уровней при открытии бара
Когда все вспомогательные компоненты готовы, мы интегрируем их внутри функции OnTick .
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Выполняем этот блок только при обнаружении нового бара на выбранном таймфрейме if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ lwVolatilityLevels.yesterdayRange = GetBarRange(_Symbol, timeframe, 1); lwVolatilityLevels.buyEntryPrice = CalculateBuyEntryPrice (askPrice, lwVolatilityLevels.yesterdayRange, inpBuyRangeMultiplier ); } }
Когда обнаруживается новый дневной бар, советник пересчитывает диапазон вчерашнего дня и вычисляет цену входа в покупку для нового дня. В этот же момент закрывается любая активная позиция. В рамках этой экспериментальной схемы открытие нового дня означает завершение предыдущей сделки. Такой подход гарантирует, что каждая сделка фиксирует полный дневной результат и соответствует фокусу исследования на поведении по дням, а не на внутридневном управлении.
Отслеживание внутридневного движения цены
Чтобы определить момент пересечения ценой расчетного уровня входа, мы отслеживаем минутные цены закрытия. Для хранения последних значений закрытия на M1 определяется глобальный массив.
//--- Для хранения минутных данных double closePriceMinutesData [];
Во время инициализации советника массив настраивается как временной ряд, чтобы самые свежие данные всегда были доступны по индексу ноль.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Обрабатываем следующие массивы как таймсерии (индекс 0 — самый последний бар) ArraySetAsSeries(closePriceMinutesData, true); return(INIT_SUCCEEDED); }
На каждом тике массив обновляется с помощью CopyClose.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Получаем минутные данные if(CopyClose(_Symbol, PERIOD_M1, 0, 5, closePriceMinutesData) == -1){ Print("Ошибка при копировании минутных данных ", GetLastError()); return; } }
Это обеспечивает скользящее окно недавнего ценового поведения без избыточной обработки данных.
Определение пересечений уровня пробоя
Логика определения пересечения проверяет, перешла ли цена от уровня ниже расчетного к уровню выше него между двумя последовательными минутными закрытиями. Такое определение гарантирует, что входы возникают только при настоящем пересечении снизу вверх, а не при повторных касаниях.
//+------------------------------------------------------------------+ //| Для определения пересечения заданного ценового уровня снизу вверх | //+------------------------------------------------------------------+ bool IsCrossOver(const double price, const double &closePriceMinsData[]){ if(closePriceMinsData[1] <= price && closePriceMinsData[0] > price){ return true; } return false; }
Функция намеренно проста. Она отвечает на один вопрос и возвращает понятный логический результат. Такая простота облегчает проверку и анализ торгового условия.
Реализация фильтра торгового дня недели
Для поддержки концепции Trade Day of the Week вводятся две вспомогательные функции. Первая преобразует значение datetime в числовой день недели.
//+------------------------------------------------------------------------------------+ //| Возвращает день недели (0 = воскресенье, 6 = суббота) для заданного значения datetime| //+------------------------------------------------------------------------------------+ int TimeDayOfWeek(datetime time){ MqlDateTime timeStruct = {}; if(!TimeToStruct(time, timeStruct)){ Print("TimeDayOfWeek: ошибка TimeToStruct"); return -1; } return timeStruct.day_of_week; }
Вторая определяет, разрешена ли торговля на основе выбранного режима и заданных пользователем флагов дней недели.
//+-----------------------------------------------------------------------------------------------------+ //| Определяет, разрешена ли торговля для заданного datetime на основе выбранного режима торговых дней | //+-----------------------------------------------------------------------------------------------------+ bool IsTradingDayAllowed(datetime time) { // Базовый режим: без фильтрации if(tradeDayMode == TDW_ALL_DAYS){ return true; } int day = TimeDayOfWeek(time); switch(day) { case 0: return tradeSunday; case 1: return tradeMonday; case 2: return tradeTuesday; case 3: return tradeWednesday; case 4: return tradeThursday; case 5: return tradeFriday; case 6: return tradeSaturday; } return false; }
Когда советник работает в базовом режиме, торговля разрешена всегда. Когда включена фильтрация по дням, решение полностью передается конфигурации дней недели. Такое разделение гарантирует, что базовый и фильтрованный эксперименты используют одинаковую логику исполнения и отличаются только моментами разрешения торговли.
Безопасное управление позициями
Поскольку исследование допускает только одну активную позицию одновременно, используется служебная функция, которая просматривает все открытые позиции и проверяет, существует ли уже позиция на покупку, принадлежащая этому советнику.
//+------------------------------------------------------------------+ //| Проверяет, есть ли у советника активная позиция на покупку. | | //+------------------------------------------------------------------+ bool IsThereAnActiveBuyPosition(ulong magic){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Ошибка при получении тикета позиции ", GetLastError()); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ return true; } } } return false; }
Magic number гарантирует, что учитываются только позиции, созданные данным экземпляром советника.
Вторая функция отвечает за закрытие позиций по magic number.
//+------------------------------------------------------------------+ //| Закрывает все позиции с указанным 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); } } } } }
Она используется в начале каждого нового торгового дня, чтобы аккуратно и последовательно закрыть предыдущую сделку.
Исполнение сделок
Исполнение сделок выполняется через отдельную функцию, которая открывает позицию по рынку на покупку с заданным размером лота.
//+------------------------------------------------------------------+ //| Функция открытия позиции по рынку на покупку | //+------------------------------------------------------------------+ bool OpenBuy(double entryPrice, double lotSize){ if(!Trade.Buy(NormalizeDouble(lotSize, 2), _Symbol, entryPrice)){ Print("Ошибка при исполнении рыночной заявки на покупку: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
Ошибки выводятся явно, что упрощает диагностику проблем исполнения во время тестирования. Эта функция представляет финальное действие в цепочке принятия решений и вызывается только после выполнения всех предыдущих условий.
Завершение торговой логики
Последний шаг — интегрировать все условия в функцию OnTick . Этот советник предназначен для удержания сделок ровно один торговый день: без стоп-лосса, без тейк-профита и без трейлинг-логики. Сделка должна закрываться в конце торгового дня — не раньше и не позже.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Выполняем этот блок только при обнаружении нового бара на выбранном таймфрейме if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ ... //--- Закрываем любую активную длинную позицию if(IsThereAnActiveBuyPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); } } }
Советник проверяет пересечение ценой расчетного уровня входа снизу вверх. Если активен базовый режим, сделка исполняется сразу. Если включена фильтрация по дням, пересечение должно также произойти в разрешенный торговый день.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... if(tradeDayMode == TDW_ALL_DAYS){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData )){ OpenBuy(askPrice, positionSize); } } if(tradeDayMode == TDW_SELECTED_DAYS){ if(IsTradingDayAllowed(currentTime)){ if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){ OpenBuy(askPrice, positionSize); } } } }
Редакторское примечание: следующий листинг, вероятно, вставлен в исходный материал по ошибке: он содержит логику Supertrend и не соответствует описываемому советнику для проверки концепции торгового дня недели. Кодовый блок сохранен без подмены, чтобы не изменять исходный фрагмент.
Ниже приведен окончательный вид функции OnTick после реализации всех компонентов:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); //--- Run this block only when a new bar is detected on the selected timeframe if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ UpdateSupertrendBandValues(); if(IsSupertrendBullishSignal()){ if(IsThereAnActiveSellPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(50); } if(direction == TRADE_BOTH || direction == ONLY_LONG){ OpenBuy(askPrice, CalculateAdaptiveStopLossPrice(POSITION_TYPE_BUY), positionSize); } } if(IsSupertrendBearishSignal()){ if(IsThereAnActiveBuyPosition(magicNumber)){ ClosePositionsByMagic(magicNumber); Sleep(50); } if(direction == TRADE_BOTH || direction == ONLY_SHORT){ OpenSel(bidPrice, CalculateAdaptiveStopLossPrice(POSITION_TYPE_SELL), positionSize); } } } }
Перед переходом к тестированию нужно убедиться, что среда графика наглядно отображает ценовое движение и торговую активность. Чистое и единообразное оформление графика значительно облегчает визуальную проверку входов, выходов и общего поведения во время тестирования стратегии. Поскольку этот советник предназначен для исследования и анализа, улучшение читаемости графика — небольшой, но важный шаг.
Для этого мы определяем пользовательскую служебную функцию, которая настраивает внешний вид графика при запуске советника. Функция применяет набор визуальных параметров, чтобы свечи, фон и движения цены было легко различать во время тестирования и воспроизведения. Ниже приведено определение функции, которое следует поместить в раздел для пользовательских функций MQL5.
//+------------------------------------------------------------------+ //| Эта функция настраивает внешний вид графика. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Ошибка при настройке фона графика, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Ошибка при настройке сетки графика, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Ошибка при настройке режима графика, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Ошибка при настройке переднего плана графика, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){ Print("Ошибка при настройке цвета бычьих свечей, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){ Print("Ошибка при настройке цвета медвежьих свечей, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){ Print("Ошибка при настройке цвета медвежьих свечей, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){ Print("Ошибка при настройке цвета медвежьих свечей, ", GetLastError()); return false; } return true; }
Функция работает через последовательность вызовов ChartSetInteger. Каждый вызов изменяет определенное визуальное свойство текущего активного графика.
Сначала цвет фона графика устанавливается белым. Светлый фон улучшает контраст и делает цвета свечей более заметными. Затем отключается сетка графика. Удаление сетки снижает визуальный шум и помогает сосредоточиться на ценовом движении, а не на вспомогательных линиях. После этого режим графика переключается на свечное отображение. Свечи дают больше информации, чем линейный график, и лучше подходят для анализа волатильности, структуры и внутридневного поведения.
Затем цвет переднего плана устанавливается черным. Это гарантирует, что текст графика и ценовые шкалы хорошо видны на белом фоне. Далее функция задает разные цвета для бычьих и медвежьих свечей. Бычьи свечи окрашиваются в цвет sea green, чтобы выделить восходящее движение цены, а медвежьи — в черный для нейтрального и читаемого контраста. Та же цветовая логика применяется к контурам баров графика для восходящего и нисходящего движения. Благодаря этому тела свечей и их контуры остаются визуально согласованными и легко интерпретируемыми.
Каждый вызов ChartSetInteger проверяется на успешность. Если какой-либо шаг настройки завершается неудачей, в журнал записывается сообщение об ошибке, и функция сразу возвращает false. Это позволяет советнику заранее обнаружить проблемы конфигурации и не работать при непредусмотренных условиях графика. Если все настройки графика успешно применены, функция возвращает true, показывая, что график готов к тестированию и анализу.
После определения функции она вызывается из функции инициализации советника. Это гарантирует, что внешний вид графика настраивается один раз — сразу при запуске советника.
//+------------------------------------------------------------------+ //| Функция инициализации советника | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Для настройки внешнего вида графика if(!ConfigureChartAppearance()){ Print("Ошибка при настройке внешнего вида графика", GetLastError()); return INIT_FAILED; } return(INIT_SUCCEEDED); }
Внутри функции OnInit мы вызываем процедуру настройки графика и проверяем ее результат. Если настройка не удалась, советник прекращает инициализацию и сообщает о проблеме. Если настройка успешна, инициализация продолжается в обычном режиме.
Такой подход гарантирует, что каждый тест начинается с чистого и читаемого оформления графика. Это упрощает оценку поведения сделок, просмотр исторических результатов и визуальное подтверждение того, что логика стратегии во время тестирования работает так, как задумано.
На этом этапе логика советника завершена. Каждый компонент, введенный ранее, теперь встроен в согласованный поток исполнения, который точно отражает исследовательскую цель.
В этом разделе поэтапно показано создание специализированного советника, предназначенного для проверки гипотезы Trade Day of the Week . В архитектуре сделан акцент на ясности, изоляции переменных и воспроизводимости. Каждая функция выполняет определенную роль и поддерживает четкое разделение между дневным принятием решений, внутридневным исполнением и фильтрацией по времени.
Готовый исходный код представлен как lwTDWStudy.mq5 и может использоваться непосредственно для тестирования или расширяться для дополнительных экспериментов. Когда советник готов, можно перейти от реализации к эмпирической оценке и начать анализ того, действительно ли определенные дни дают измеримое торговое преимущество.
Эмпирическое тестирование фильтра торгового дня недели
Для проверки гипотезы торгового дня недели была проведена серия контролируемых экспериментов на золоте (XAUUSD) с использованием дневного таймфрейма (D1). Цель заключалась в том, чтобы определить, приводит ли фильтрация сделок по дню недели к измеримым различиям в результативности.
Использовались два типа экспериментов:
1. Базовые тесты
В этих тестах стратегия может торговать в любой день недели при выполнении условия входа. Фильтрация по времени не применяется. Это создает нейтральную точку отсчета.
2. Тесты торгового дня недели
В этих тестах торговля ограничивается одним конкретным днем недели за раз, что позволяет отдельно оценивать результативность каждого дня. Все тесты выполнялись с поквартальной walk-forward-сегментацией, чтобы один благоприятный рыночный этап не определял результат.
Базовый тест без фильтрации по дням
Базовый эксперимент был проведен за весь календарный год.
Конфигурация теста:
- Период: с 1 января 2025 по 30 декабря 2025
- tradeDayMode: TDW_ALL_DAYS
- Сделки разрешены во все торговые дни
Результаты:
| Показатель | Значение |
|---|---|
| Процент прибыльных сделок | 54.25% |
| Чистая прибыль | $26102.15 |
Поквартальные walk-forward-результаты по торговым дням
Каждый квартал тестировался отдельно с использованием TDW_SELECTED_DAYS. За один раз был включен только один день недели, после чего результаты фиксировались.
Результаты 1-го квартала:
1 января 2025 — 31 марта 2025
| День | Процент прибыльных сделок (%) | Чистая прибыль ($) |
|---|---|---|
| Понедельник | 77.00 | 4444.24 |
| Вторник | 59.35 | 93344.24 |
| Среда | 64.22 | -14112.00 |
| Четверг | 57.43 | 9883.21 |
| Пятница | 72.45 | -1280.82 |
| Базовый тест | 59.64 | 26102.15 |
Наблюдение:
Высокий процент прибыльных сделок не всегда означает прибыльность. Среда и пятница показывают достойную точность, но отрицательную доходность, подчеркивая важность совместной оценки процента прибыльных сделок и профиля прибыли и убытков.
Результаты 2-го квартала:
1 апреля 2025 — 30 июня 2025
| День | Процент прибыльных сделок (%) | Чистая прибыль ($) |
|---|---|---|
| Понедельник | 100.00 | 33137.25 |
| Вторник | 0.00 | -21054.39 |
| Среда | 92.31 | 44388.16 |
| Четверг | 23.53 | -13790.47 |
| Пятница | 61.40 | 17674.23 |
| Базовый тест | 61.16 | 48514.81 |
Наблюдение:
Этот квартал показывает резкое расхождение между днями. Среда доминирует и по точности, и по прибыльности, тогда как вторник полностью проваливается. Такая дисперсия плохо согласуется с предположением о случайном рынке.
Результаты 3-го квартала:
1 июля 2025 — 30 сентября 2025
| День | Процент прибыльных сделок (%) | Чистая прибыль ($) |
|---|---|---|
| Понедельник | 92.787 | 32767.01 |
| Вторник | 13.95 | -13797.71 |
| Среда | 5.91 | -9466.48 |
| Четверг | 54.10 | -4330.74 |
| Пятница | 58.18 | 16728.25 |
| Базовый тест | 52.41 | 28616.31 |
Наблюдение:
Понедельник и пятница выделяются, тогда как торговля в середине недели показывает слабые результаты. И снова поведение, зависящее от конкретного дня, определяет итоговую результативность.
Результаты 4-го квартала:
1 октября 2025 — 30 декабря 2025
| День | Процент прибыльных сделок (%) | Чистая прибыль ($) |
|---|---|---|
| Понедельник | 61.38 | 20288.15 |
| Вторник | 0.00 | -4677.96 |
| Среда | 60.32 | -31755.28 |
| Четверг | 42.59 | 3169.84 |
| Пятница | 22.88 | -2059.73 |
| Базовый тест | 64.27 | -1101.06 |
Наблюдение:
В этом квартале сам базовый режим становится убыточным, тогда как выборочная торговля по дням остается жизнеспособной. Понедельник продолжает демонстрировать устойчивость в разных рыночных условиях.
Что говорят нам цифры
Из данных ясно следуют несколько выводов:
Результативность распределена по неделе неравномерно. Одни дни стабильно превосходят другие, тогда как другие регулярно не оправдывают ожиданий. Одного процента прибыльных сделок недостаточно. Некоторые дни дают высокую точность, но генерируют убытки, что подтверждает необходимость оценивать прибыльность вместе с долей прибыльных сделок. Фильтрация по дням может снижать подверженность в структурно слабые периоды.
Исключение слабых дней повышает стабильность без изменения базовой логики стратегии. Рынок демонстрирует временную поведенческую закономерность. Повторяющиеся поквартальные закономерности убедительно поддерживают утверждение Ларри Уильямса о том, что равнозначная торговля каждый день приводит к потере измеримого преимущества.
Фильтр торгового дня недели не предсказывает направление, а работает как вероятностная линза: он направляет трейдера к периодам, где вероятность возможности выше, и отводит от дней, когда риск перевешивает потенциальную прибыль.
Заключение
Это исследование было посвящено простой, но часто игнорируемой идее: само время может давать преимущество в краткосрочной торговле. Преобразовав концепцию торгового дня недели по Ларри Уильямсу в измеримую и тестируемую методику, мы перевели обсуждение из области мнений в область доказательств.
С помощью контролируемого базового тестирования и поквартальных walk-forward-экспериментов на золоте мы показали, что торговая результативность распределяется по неделе неравномерно. Одни дни стабильно давали более высокий процент прибыльных сделок и более сильную прибыльность, тогда как другие регулярно ухудшали математическое ожидание. Эти различия сохранялись в нескольких рыночных фазах, ставя под сомнение предположение о том, что все торговые дни ведут себя одинаково.
Что важнее, статья не остановилась на результатах. Она показала, как такие временные поведенческие закономерности рынка можно объективно исследовать с помощью MQL5. Конструкция советника, структура входных параметров и методика тестирования образуют основу, пригодную для повторного использования, которую читатели могут адаптировать к другим рынкам, таймфреймам или критериям фильтрации. Хотя здесь акцент был сделан на прибыльности и результативности, ту же исследовательскую схему можно расширить для изучения волатильности, расширения диапазона, поведения просадки и внутридневной торговли.
К концу статьи читатель получает не только подтверждение того, что одни дни лучше других. Он получает практическую исследовательскую схему, которая поощряет выборочное участие, дисциплинированную фильтрацию и более глубокое изучение поведения рынка вместо слепого исполнения.
Для трейдеров и разработчиков эта работа подтверждает простую идею с важными последствиями. Если рынки демонстрируют временные склонности, то игнорировать их — это выбор. Измерять их — преимущество.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20941
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Моделирование рынка: Position View (IV)
Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть II): Новый уровень онлайн-адаптации
Самообучающийся SuperTrend: адаптивный индикатор тренда на машинном обучении
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования