Создание объектно-ориентированного расчетного модуля сессионного VWAP на MQL5
Введение и практические проблемы
Разработку внутридневных торговых систем на MQL5 я обычно начинаю с добавления на график стандартной скользящей средней, которая помогает отфильтровать краткосрочный тренд. Такой подход кажется приемлемым в периоды затишья на рынке или при узких ценовых диапазонах. Однако тестирование в реальных условиях на валютных парах с высокой ликвидностью выявляет серьезный структурный недостаток. Крупная свеча, сформировавшаяся на фоне важных макроэкономических новостей, может мгновенно сместить базовую линию рынка за счет объема торгов институциональных участников. Простая скользящая средняя учитывает эту свечу с большим объемом так же, как свечу с низким объемом из спокойной ночной сессии, поскольку принимает во внимание только цены закрытия. Это создает опасную ловушку для торгового робота. Советник открывает сделку у линии скользящей средней, считая это откатом к справедливой стоимости, но реальный ценовой ориентир, заданный объемом торгов институциональных участников, остается далеко позади.
Причина ошибки заключается в том, что стандартные индикаторы при расчетах полностью игнорируют массивы тиковых объемов. Я хотел заменить этот традиционный подход гораздо более адаптивным решением, которое отслеживает, на каких ценовых уровнях фактически проходят сделки. Средневзвешенная по объему цена (VWAP) выполняет эту задачу, учитывая каждое колебание цены по абсолютному объему сделок на соответствующем ценовом уровне. В MQL5 нет встроенного индикатора VWAP с ежедневным сбросом и динамически пересчитываемыми полосами отклонения. Это затрудняет быструю интеграцию в автоматизированные стратегии. Мы перенесем накопительные расчеты в подключаемый файл. Затем мы нанесём базовую линию и полосы волатильности на график с помощью пользовательского индикатора для визуальной проверки. В заключение мы интегрируем этот модуль в советник для торговли на откатах.
Основные расчеты и ежедневные временные привязки
Математический расчет, лежащий в основе VWAP, ориентирован на практическое применение и позволяет избежать запаздывания из-за сглаживания, характерного для традиционных технических индикаторов. Вместо того чтобы просто суммировать цены закрытия и делить полученную сумму на фиксированное число баров в расчетном окне, мы вычисляем типичную цену каждой свечи, усредняя ее максимум, минимум и цену закрытия. Затем мы умножаем эту типичную цену на тиковый объем, зафиксированный для соответствующего бара. Полученные произведения последовательно суммируются начиная с фиксированной начальной точки. Наконец, мы делим накопленную сумму произведений цены на объем на суммарный тиковый объем за тот же исторический период.
Базовое уравнение можно записать следующим образом:

Если текущая рыночная цена находится вблизи полученной линии VWAP, считается, что цена актива колеблется около его внутридневной справедливой стоимости, признаваемой большинством участников рынка. Когда цена значительно отклоняется от этого ориентира, она попадает в зону перекупленности или перепроданности по отношению к дневному распределению ликвидности. Чтобы динамически оценивать величину этого отклонения, наш расчетный модуль также будет рассчитывать взвешенное по объему стандартное отклонение. Эта дисперсия позволяет нам построить верхнюю и нижнюю полосы отклонения, которые естественным образом расширяются и сужаются в зависимости от фактических изменений внутридневной волатильности. Я не рассматриваю полученную базовую линию VWAP как некий "волшебный" уровень поддержки или сопротивления. В ходе резкого развития тренда финансовые рынки стремительно преодолевают эти границы. По моему опыту, эта линия служит институциональным фильтром: она позволяет подтвердить, что при откате цена возвращается в реальную зону высокой исторической ликвидности, прежде чем разработчик разрешит вход в рынок.
Контракт сигналов VWAP
Прежде чем преобразовать эту математическую архитектуру в структуры MQL5, необходимо установить строгие правила работы для наших слоев исполнения. Этот контракт сигналов точно определяет, как и когда наш торговый робот будет считывать показатели VWAP и разрешать отправку рыночного ордера. В нашей схеме стратегии порог входа задается границами на расстоянии одного стандартного отклонения от VWAP. Контракт предписывает алгоритму искать возможность входа на покупку, если цена снижается и касается нижней полосы отклонения, при условии что общая рыночная ситуация благоприятствует покупке. И наоборот, мы ищем возможность открытия короткой позиции, если цена резким скачком достигает верхней полосы отклонения.
Чтобы наш торговый робот не совершал сделок на основе неподтвержденного внутридневного шума, цикл расчета будет работать исключительно с полностью закрытыми барами. Мы обеспечиваем соблюдение этого ограничения как в индикаторе, так и в советнике, передавая в наши методы расчета фиксированное значение параметра сдвига бара, равное 1. Это гарантирует, что алгоритм полностью игнорирует текущие, еще не подтвержденные колебания свечи с индексом 0. Кроме того, контракт сигналов строго требует, чтобы накопленные суммы обнулялись каждый день ровно в 00:00 по времени сервера брокера. В полночь алгоритм должен полностью очищать память, игнорируя все вчерашние данные об объеме и учитывая только формирующийся внутридневной профиль ликвидности.
Проектирование подключаемого файла для повторного использования
Размещение ресурсоемких статистических циклов непосредственно в основном программном файле советника приводит к созданию жесткой, немасштабируемой архитектуры. Если впоследствии вы решите отслеживать несколько символов или применять этот расчет к разным таймфреймам, код быстро станет неуправляемым. Для обеспечения стабильной работы мы выделяем расчетный модуль VWAP в отдельный пользовательский подключаемый файл с именем VWAP_Engine.mqh. Этот объектно-ориентированный подход обеспечивает четкое разделение математической обработки данных и механизмов маршрутизации ордеров. Индикатор на графике и торговый робот будут обращаться к одному и тому же классу из подключаемого файла, что обеспечит полное совпадение результатов расчетов в обоих модулях.
Начнем с определения интерфейса и закрытых членов нашего класса.
//+------------------------------------------------------------------+ //| VWAP_Engine.mqh | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #property copyright "Open Source" #property version "3.08" class CVWAP { private: string m_symbol; ENUM_TIMEFRAMES m_timeframe; datetime GetStartOfDay(datetime current_time); public: CVWAP(string symbol, ENUM_TIMEFRAMES tf); ~CVWAP(void); bool GetVWAPData(int shift, double &vwap, double &upper_band, double &lower_band, double deviation_multiplier = 1.0); }; CVWAP::CVWAP(string symbol, ENUM_TIMEFRAMES tf) { //--- Initialize internal environment variables m_symbol = (symbol == "") ? _Symbol : symbol; m_timeframe = tf; } CVWAP::~CVWAP(void) { } datetime CVWAP::GetStartOfDay(datetime current_time) { MqlDateTime dt; //--- Deconstruct raw Unix time into calendar structure variables TimeToStruct(current_time, dt); dt.hour = 0; dt.min = 0; dt.sec = 0; return StructToTime(dt); } bool CVWAP::GetVWAPData(int shift, double &vwap, double &upper_band, double &lower_band, double deviation_multiplier) { //--- Read time signature from the target historical index datetime current_time = iTime(m_symbol, m_timeframe, shift); if(current_time == 0) return false; //--- Grab the session midnight anchor node coordinates datetime start_time = GetStartOfDay(current_time); MqlRates rates[]; int copied = CopyRates(m_symbol, m_timeframe, start_time, current_time, rates); if(copied <= 0) return false; double cumulative_pv = 0.0; long cumulative_vol = 0; //--- Pass 1: Sum historical typical prices weighted by liquidity volumes for(int i = 0; i < copied; i++) { double typical_price = (rates[i].high + rates[i].low + rates[i].close) / 3.0; cumulative_pv += typical_price * rates[i].tick_volume; cumulative_vol += rates[i].tick_volume; } //--- Defensive engineering: Intercept fatal zero volume division crashes if(cumulative_vol == 0) return false; vwap = cumulative_pv / (double)cumulative_vol; double variance_sum = 0.0; //--- Pass 2: Compute statistical variance distribution weighted by volume for(int i = 0; i < copied; i++) { double typical_price = (rates[i].high + rates[i].low + rates[i].close) / 3.0; double diff = typical_price - vwap; variance_sum += (diff * diff) * rates[i].tick_volume; } double std_dev = MathSqrt(variance_sum / (double)cumulative_vol); upper_band = vwap + (std_dev * deviation_multiplier); lower_band = vwap - (std_dev * deviation_multiplier); return true; }
Закрытая секция класса CVWAP строго контролирует переменные среды, фиксируя целевой финансовый инструмент и таймфрейм при инициализации. Предоставление открытого доступа только к необходимому методу получения данных защищает внутренние параметры расчета от несанкционированных внешних изменений. Обратите внимание, что основная открытая функция GetVWAPData возвращает значение примитивного типа bool, а не массив значений типа double. Она передает сами результаты вычислений по ссылке с помощью оператора "&". Этот шаблон проектирования является важнейшим средством оптимизации производительности в MQL5. Это позволяет за один вызов функции одновременно обновлять базовую линию, верхнюю и нижнюю полосы без выполнения трех отдельных ресурсоемких циклов по массивам исторических данных.
Отслеживание полуночи с помощью MqlDateTime
Самая сложная техническая задача при разработке сессионного VWAP — определить начало текущего торгового дня. При разработке на MQL5 нельзя просто отсчитать назад фиксированное число баров, например 20 или 200. На стандартном 5-минутном графике один полный день теоретически должен состоять из 288 свечей. Однако если рынок закрывается раньше из-за праздника или если сервер брокера теряет пакеты данных в часы низкой ликвидности, фактическое количество баров будет колебаться непредсказуемым образом. Жестко заданное окно ретроспективного анализа заставит циклы захватывать данные за предыдущий день, полностью искажая внутридневные расчеты со взвешиванием по объему.
Мы передаем время запрашиваемого бара в закрытый вспомогательный метод. Он преобразует временную метку Unix в удобную для восприятия календарную структуру.
//+------------------------------------------------------------------+ //| Extracts the exact 00:00 midnight timestamp of the current bar | //+------------------------------------------------------------------+ datetime CVWAP::GetStartOfDay(datetime current_time) { MqlDateTime dt; //--- Deconstruct raw Unix time into calendar structure variables TimeToStruct(current_time, dt); //--- Force time parameters to absolute midnight baseline dt.hour = 0; dt.min = 0; dt.sec = 0; //--- Reconstruct properties back into a clean timestamp index return StructToTime(dt); }
Это гарантирует, что циклы всегда смогут определить точное начало текущего дня, даже при отсутствии свечей или нерегулярном поступлении данных. Обратное преобразование этой структуры с помощью StructToTime дает абсолютную временную привязку для наших запросов исторических данных. Такая схема гарантирует, что даже при отсутствии свечей или нерегулярном поступлении данных наши вычислительные циклы всегда будут знать точный индекс, с которого начался текущий день.
Реализация двухпроходного цикла расчета
После установки временной привязки к полуночи метод GetVWAPData должен извлечь исходные данные баров и выполнить циклы взвешивания по объему. Алгоритм использует встроенную функцию CopyRates для получения массива структур MqlRates за интервал от полуночи до бара с запрошенным сдвигом. На раннем этапе тестирования я обнаружил ошибку в организации индексации. Если извлекать котировки напрямую, без сортировки, данные считываются в хронологическом порядке (от самых старых к самым новым), что нарушает схемы расчета на основе сдвигов баров, используемые в стандартных торговых системах. Чтобы исправить это, расчетный модуль явно применяет ArraySetAsSeries к внутреннему массиву котировок, гарантируя, что индекс 0 всегда указывает на самый новый бар в полученной выборке.
Реализация этого основного метода обработки данных обеспечивает загрузку данных и компактно объединяет математические операции внутри подключаемого файла.
//+------------------------------------------------------------------+ //| Main accumulation loop: Computes baseline price and deviations | //+------------------------------------------------------------------+ bool CVWAP::GetVWAPData(int shift, double &vwap, double &upper_band, double &lower_band, double deviation_multiplier) { //--- Read time signature from the target historical index datetime current_time = iTime(m_symbol, m_timeframe, shift); if(current_time == 0) return false; //--- Grab the session midnight anchor node coordinates datetime start_time = GetStartOfDay(current_time); //--- Fetch historical chart rates data into series array MqlRates rates[]; int copied = CopyRates(m_symbol, m_timeframe, start_time, current_time, rates); //--- Gracefully abort execution if history synchronization is incomplete if(copied <= 0) return false; double cumulative_pv = 0.0; long cumulative_vol = 0; //--- Pass 1: Sum historical typical prices weighted by liquidity volumes for(int i = 0; i < copied; i++) { double typical_price = (rates[i].high + rates[i].low + rates[i].close) / 3.0; cumulative_pv += typical_price * rates[i].tick_volume; cumulative_vol += rates[i].tick_volume; } //--- Defensive engineering: Intercept fatal zero volume division crashes if(cumulative_vol == 0) return false; //--- Complete tracking pass by extracting daily vwap benchmark price vwap = cumulative_pv / (double)cumulative_vol; double variance_sum = 0.0; //--- Pass 2: Compute statistical variance distribution weighted by volume for(int i = 0; i < copied; i++) { double typical_price = (rates[i].high + rates[i].low + rates[i].close) / 3.0; double diff = typical_price - vwap; variance_sum += (diff * diff) * rates[i].tick_volume; } //--- Calculate dynamic standard deviation from historical sample variance double std_dev = MathSqrt(variance_sum / (double)cumulative_vol); //--- Project structural outer boundary bands using the multiplier upper_band = vwap + (std_dev * deviation_multiplier); lower_band = vwap - (std_dev * deviation_multiplier); return true; }
Обработка данных выполняется строго за два последовательных прохода с линейной сложностью. В ходе первого прохода выполняется перебор синхронизированных данных котировок в памяти для расчета базовой линии VWAP. На этом проходе суммируются произведения цены на объем и учитывается общая ликвидность. Одна из серьезных ловушек здесь связана с условиями нулевого объема. Во время еженедельного рыночного ролловера или внезапных обрывов связи с брокером платформа может сформировать "фантомную" свечу с абсолютно нулевым тиковым объемом. Попытка разделить накопленную сумму цен на нулевой объём приведёт к критической ошибке деления на ноль, из-за которой советник немедленно аварийно завершит работу. Расчетный модуль предотвращает эту ошибку с помощью защитной проверки: если значение накопителя объема равно нулю, текущий шаг расчета пропускается.
Во втором проходе рассчитывается взвешенная по объему дисперсия. В ходе этого прохода алгоритм повторно перебирает бары в том же окне, вычитая из типичной цены каждого бара только что рассчитанное значение базовой линии VWAP. Эта разность возводится в квадрат и умножается на тиковый объем соответствующей свечи, чтобы всплески при высокой ликвидности оказывали наибольшее влияние на оценку волатильности. Наконец, квадратный корень из этого среднего значения дает стандартное отклонение, позволяя расчетному модулю динамически рассчитывать верхнюю и нижнюю полосы отклонения.
Создание визуального диагностического индикатора
Прежде чем использовать этот подключаемый файл для автоматической отправки реальных рыночных ордеров, необходимо визуально проверить, как сбрасывается базовая линия в разных торговых сессиях. Поскольку основная архитектура полностью инкапсулирована в классе из подключаемого файла, мы можем создать экземпляр того же компонента CVWAP в пользовательском индикаторе Ind_Session_VWAP.mq5. Этот индикатор настраивает три динамически обновляемые линии, используя индикаторные буферы для их отрисовки непосредственно на основном ценовом графике.

Блоки кода заголовка и инициализации задают свойства отрисовки и обеспечивают корректную привязку внутренних массивов к слою отрисовки терминала.
//+------------------------------------------------------------------+ //| Ind_Session_VWAP.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #include "VWAP_Engine.mqh" input double InpDeviation = 1.5; double BufferVWAP[]; double BufferUpper[]; double BufferLower[]; CVWAP *g_vwap_engine; int OnInit() { //--- Bind dynamic double arrays to indicator data channels SetIndexBuffer(0, BufferVWAP, INDICATOR_DATA); SetIndexBuffer(1, BufferUpper, INDICATOR_DATA); SetIndexBuffer(2, BufferLower, INDICATOR_DATA); g_vwap_engine = new CVWAP(_Symbol, PERIOD_CURRENT); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { //--- Purge dynamic memory pointer allocation to prevent leaks if(CheckPointer(g_vwap_engine) == POINTER_DYNAMIC) { delete g_vwap_engine; } } int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Enforce historical bars availability check if(rates_total < 1) return 0; //--- Identify optimized calculation start point to save CPU power int start = (prev_calculated > 0) ? prev_calculated - 1 : 0; //--- Traverse the visible timeline dataset sequentially for(int i = start; i < rates_total && !IsStopped(); i++) { int shift = rates_total - 1 - i; double vwap, upper, lower; //--- Query the engine and populate indicator buffers via dot operator if(g_vwap_engine.GetVWAPData(shift, vwap, upper, lower, InpDeviation)) { BufferVWAP[i] = vwap; BufferUpper[i] = upper; BufferLower[i] = lower; } } return(rates_total); }Для экономии процессорных ресурсов индикатор использует параметр prev_calculated. При первой загрузке индикатор выполняет двухпроходный цикл расчета по всей видимой истории графика. При поступлении каждого последующего рыночного тика алгоритм задает начальный индекс так, чтобы обновлять только текущий формирующийся бар. Эта оптимизационная проверка позволяет избежать пересчета терминалом всей истории за тысячи дней на каждом ценовом тике, экономя вычислительные ресурсы в часы высокой торговой активности.
Интеграция советника и уровень исполнения
После визуального подтверждения того, что расчетный модуль корректно вычисляет значения на всей временной шкале графика, следующим логическим этапом становится создание уровня исполнения. Я решил реализовать торговую логику в оболочке советника с именем EA_VWAP_Pullback.mq5. При маршрутизации ордеров разработчик может создавать собственные структуры вручную или использовать готовые модули. Я предпочел подключить штатный класс CTrade из стандартной библиотеки, поскольку он позволяет обойтись без сотен строк низкоуровневого кода для отслеживания торговых операций. Глобальный указатель инициализирует наш класс, а логика остается строго сосредоточенной на конкретных параметрах контракта сигналов.
Код инициализации задает время жизни объекта.
//+------------------------------------------------------------------+ //| EA_VWAP_Pullback.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include "VWAP_Engine.mqh" input double InpDeviation = 1.5; input double InpLotSize = 0.10; CVWAP *g_vwap_engine; CTrade g_trade; int OnInit() { //--- Secure memory space and instantiate the include engine class g_vwap_engine = new CVWAP(_Symbol, PERIOD_CURRENT); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { //--- Release heap allocation memory to clean the system footprint if(CheckPointer(g_vwap_engine) == POINTER_DYNAMIC) { delete g_vwap_engine; } } void OnTick() { static datetime last_bar_time = 0; //--- Check opening timestamp coordinates for the current active candle datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0); //--- Open execution gate exclusively when a new bar forms on chart if(current_bar_time != last_bar_time) { last_bar_time = current_bar_time; //--- Validate active state allocation of dynamic class pointer if(CheckPointer(g_vwap_engine) != POINTER_INVALID) { double vwap, upper, lower; //--- Query calculation engine using the last confirmed closed bar (shift 1) if(g_vwap_engine.GetVWAPData(1, vwap, upper, lower, InpDeviation)) { double close_price = iClose(_Symbol, PERIOD_CURRENT, 1); //--- Manage trade routing conditions based on active netting exposure if(!PositionSelect(_Symbol)) { //--- Open entries when price breaks structure deviations boundaries if(close_price <= lower) { g_trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "VWAP Lower Band Touch"); } else if(close_price >= upper) { g_trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "VWAP Upper Band Touch"); } } else { long pos_type = PositionGetInteger(POSITION_TYPE); if(pos_type == POSITION_TYPE_BUY && close_price >= vwap) { g_trade.PositionClose(_Symbol); } else if(pos_type == POSITION_TYPE_SELL && close_price <= vwap) { g_trade.PositionClose(_Symbol); } } } } } }
Запрос исторических данных и пересчет взвешенных по объему стандартных отклонений на каждом тике могут перегрузить процессор во время сессий с высокой волатильностью. Мы избегаем этой проблемы, вводя строгий фильтр нового бара в обработчике события OnTick. Проверяя время открытия текущей свечи, алгоритм разрешает выполнение основного цикла расчета только один раз за период выбранного таймфрейма.
Логика исполнения работает в рамках этой оптимизированной схемы обработки тиков и управляет открытыми позициями.
//+------------------------------------------------------------------+ //| Expert tick function with strict CPU optimization | //+------------------------------------------------------------------+ void OnTick() { static datetime last_bar_time = 0; //--- Check opening timestamp coordinates for the current active candle datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0); //--- Open execution gate exclusively when a new bar forms on chart if(current_bar_time != last_bar_time) { last_bar_time = current_bar_time; //--- Validate active state allocation of dynamic class pointer if(CheckPointer(g_vwap_engine) != POINTER_INVALID) { double vwap, upper, lower; //--- Query calculation engine using the last confirmed closed bar (shift 1) if(g_vwap_engine.GetVWAPData(1, vwap, upper, lower, InpDeviation)) { double close_price = iClose(_Symbol, PERIOD_CURRENT, 1); //--- Manage trade routing conditions based on active netting exposure if(!PositionSelect(_Symbol)) { //--- Open entries when price breaks structure deviations boundaries if(close_price <= lower) { g_trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "VWAP Lower Band Touch"); } else if(close_price >= upper) { g_trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "VWAP Upper Band Touch"); } } else { //--- Read order type properties from the active deal ticket long pos_type = PositionGetInteger(POSITION_TYPE); //--- Enforce immediate structural exit when price returns to fair value core if(pos_type == POSITION_TYPE_BUY && close_price >= vwap) { g_trade.PositionClose(_Symbol); } else if(pos_type == POSITION_TYPE_SELL && close_price <= vwap) { g_trade.PositionClose(_Symbol); } } } } } }
Протокол выхода реализует строгое правило возврата к среднему. Эта логика отлично работает на неттинговых счетах, где PositionSelect напрямую выбирает единственную открытую позицию. Для режима хеджирования разработчику потребуется доработать этот раздел, добавив цикл выбора позиций по тикетам, чтобы безопасно управлять несколькими одновременно открытыми позициями.
Ограничения при бэктестинге и динамика рынка
Ни один математический расчетный модуль не работает в вакууме. В ходе проверочных прогонов в тестере стратегий на пятиминутных графиках в режиме моделирования "Только цены открытия" я обнаружил критическое ограничение, заложенное в модели базовой линии дневной сессии. Поскольку накопленные суммы полностью обнуляются в полночь, первые несколько свечей нового дня по времени сервера брокера еще не имеют значительного веса по объему. В первые часы азиатской сессии полосы отклонения становятся исключительно узкими и крайне чувствительными к рыночному шуму в условиях низкой ликвидности. Входы без дополнительной фильтрации в этот период приводят к нестабильному исполнению ордеров и чрезмерно частым срабатываниям стоп-лосса.

Расчетный модуль достигает наибольшей статистической надежности в периоды перекрытия лондонской и нью-йоркской сессий, когда объемы торгов высоки. В эти периоды мощный поток ордеров институциональных участников формирует устойчивую границу справедливой цены, благодаря чему взаимодействие цены с этой границей при откатах становится весьма надежным. На дневном таймфрейме время открытия бара совпадает с точкой привязки к полуночи. В результате в цикле нет внутридневных исторических данных для накопления, и он вернет false.
Заключение и материалы для повторного использования
Разработка этого сессионного расчетного модуля позволяет создать надежную рабочую среду, в которой базовые расчеты ликвидности отделены от управления сделками. Базовая архитектура включает полный набор из трех унифицированных файлов, которые совместно обеспечивают отображение внутридневной стоимости. Подключаемый файл VWAP_Engine.mqh служит автономным ядром и содержит проверки временной привязки и двухпроходные циклы расчета с учетом объема. Индикатор Ind_Session_VWAP.mq5 создает экземпляр этого класса для плавной отрисовки базовой линии и маркеров волатильности на графике. Наконец, советник EA_VWAP_Pullback.mq5 напрямую использует методы подключаемого файла и строгий временной фильтр для безопасного запроса данных без ущерба для производительности терминала и без засорения памяти. Разработчики могут расширять эту программную основу, добавляя фильтры по времени суток и трейлинг-стопы. Тот же класс из подключаемого файла можно повторно использовать для нескольких символов, формируя внутридневные портфели из разных активов без изменения математических расчетов.
Структура файлов организована четко. Файл VWAP_Engine.mqh содержит исходный код объектно-ориентированного класса с ежедневным сбросом. Файл Ind_Session_VWAP.mq5 содержит исходный код индикатора для отрисовки на графике. Файл EA_VWAP_Pullback.mq5 реализует торговую логику советника для автоматической торговли на откатах.
Таблица структуры файлов
| Имя файла | Описание |
|---|---|
| VWAP_Engine.mqh | Исходный код объектно-ориентированного класса с ежедневным сбросом и расчетного модуля накопления сумм. |
| Ind_Session_VWAP.mq5 | Исходный код пользовательского индикатора визуализации, отображающего базовую линию и полосы отклонения. |
| EA_VWAP_Pullback.mq5 | Исходный код советника для автоматической торговли на откатах с фильтрами по закрытым барам. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22990
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Я согласен на 100%. Мой индикатор NRP ZigZag рисует стрелку на текущем баре при формировании отрезка, а также квадрат в истории на фактическом экстремуме. AVWAP будет перезакрепляться только на исторических квадратах NRP ZigZag. Таким образом , период ожидания, возможно, уже заложен в этом плане. Именно это я имел в виду, написав «Задержка индикатора будет допустимой...» в сообщении № 3. Мой индикатор AVWAP имеет лишь один входной параметр для ручной настройки времени закрепления — благодаря чему оба индикатора программно совместимы по принципу «подключи и работай». Конечно, подтвердить это может только тестирование.
Единственный элемент головоломки, над которым я все еще размышляю, — это анализ «квадратных баров», например, сырой объем, положительное отклонение от среднего объема, VSA, пиковые бары и/или оценка пин-баров.
Вот так...
Я согласен на 100%. Мой индикатор NRP ZigZag рисует стрелку на текущем баре при формировании отрезка, а также квадрат в истории на фактическом экстремуме. AVWAP будет переориентироваться только на исторические квадраты NRP ZigZag. Поэтому в этом плане, возможно, уже предусмотрен период охлаждения. Именно это я имел в виду, написав «Задержка индикатора будет приемлемой...» в сообщении № 3. Мой индикатор AVWAP имеет лишь один временной параметр для ручной настройки времени привязки, что делает эти два индикатора программно совместимыми по принципу «plug-and-play». Конечно, подтвердить это можно только тестированием.
Единственный элемент головоломки, над которым я все еще размышляю, — это анализ «квадратных баров», например, сырой объем, положительное отклонение от среднего объема, VSA, пиковые бары и/или оценка пин-баров.
Этот график выглядит очень наглядно, Райан. Фильтрация этих структурных квадратов с помощью индикатора VSA Climax Churn — это, безусловно, лучший подход в данном случае, само по себе сырое значение объёма даст ложные сигналы во время новостных всплесков на сессиях с низким объёмом торгов, но поиск массивного поглощения со стороны институциональных инвесторов на экстремумах позволяет сохранить надёжную базовую линию; можно легко добавить вторичный фильтр волатильности для перепроверки отклонения объёма — это гарантирует, что вы уловите только реальные институциональные блоки и пробьёте свой AVWAP прямо в самом центре настоящего разворота
На скриншоте это видно очень слабо, но к индикатору объема в дополнительном окне применена скользящая средняя с периодом 100.