Рыночная микроструктура в MQL5 (Часть 8): Оценка силы микротренда
Введение
Части 1-7 этой серии формируют полный слой измерений для рыночной микроструктуры NQ M1. В первой части был заложен и укреплен математический фундамент. Во второй и третьей частях проверялось, обладают ли доходности цен памятью. В четвертой части оценивалась устойчивость волатильности. В пятой части вариация цены была разделена на сигнал и шум. В шестой части измерялось направление информированного потока ордеров. В седьмой части эти одиннадцать показателей были сведены к одной метке режима и оценке достоверности. Однако ни одна из них не показывала на текущем баре, направлен ли краткосрочный тренд вверх, вниз или остается нейтральным.
Именно этот пробел закрывает восьмая часть. Метка режима описывает контекст сессии на горизонте 90-390 баров. Скальперу при открытии нью-йоркской сессии нужен побарный ответ на более узкий вопрос: согласована ли сейчас структура EMA и ускоряется ли в одном направлении, или же она противоречивая и пилообразная? Стандартное пересечение скользящих средних дает бинарный ответ. Нужна непрерывная оценка, отражающая степень согласованности, а не только ее направление.
В восьмой части реализована функция GetMicroTrendStrength(). Функция возвращает композитную оценку в диапазоне [-1, +1], сформированную из четырех подоценок по EMA: согласованности порядка трех EMA, нормализованного по ATR положения цены, согласованности наклонов и подтверждения объемом. Штраф за противоречие снижает оценку, когда согласованность EMA расходится с положением цены относительно быстрой EMA. В результате получается сигнал, плавно реагирующий на изменение условий, а не резко переключающийся между дискретными состояниями.
Новые функции интегрируются с классификатором режимов из седьмой части через сессионно-адаптивный вариант порогов. Если доступна структура RegimeAnalysis, пороги сильного и слабого сигналов масштабируются с учетом reg.confidence. В сессиях с высокой достоверностью — трендовых и информированных — пороги ослабляются, поэтому сигналы срабатывают легче. В сессиях с низкой достоверностью — стрессовых и шумовых — пороги ужесточаются. Это первое явное использование поля достоверности из седьмой части в рамках данной серии.
В эмпирическом исследовании композитная оценка применяется к 514 нью-йоркским сессиям NQ M1 (май 2024 г. - май 2026 г.) — тому же набору данных, что использовался в седьмой части. Основной вывод таков: сигнал распределяется по режимам асимметрично. Трендовые сессии дают наибольшую долю устойчивых сигналов — три и более последовательных баров в одном направлении, — а стрессовые чаще дают нейтральные показания. По сравнению с фиксированными порогами сессионно-адаптивный вариант переводит примерно 18% сигналов стрессовых сессий в зону отсутствия сигнала, снижая риск ложных сигналов без изменения правил.
Результат работы. В MicroStructure_Foundation.mqh добавлены четыре новые функции: GetMicroTrendStrength(), GetTrendLabel(), GetBinarySignal() и GetPersistentTrendDirection(). Новая структура MicroTrendAnalysis и перечисление TREND_LABEL. Две перегрузки PopulateMicroTrendAnalysis(): одна автономная, другая сессионно-адаптивная. Все дополнения к заголовочному файлу добавляются в существующий MicroStructure_Foundation.mqh как раздел восьмой части в соответствии с соглашением, установленным в частях 2-7. Восьмая часть также является первой в серии, которая предлагает автономный файл индикатора MicroTrendStrength.mq5.
Проблема микротренда
Пересечения EMA — наиболее распространенный трендовый сигнал в алгоритмической торговле розничных участников рынка, однако у них есть известный недостаток: когда быстрая EMA пересекает медленную, сигнал срабатывает уже после движения. Пересечение скользящих средних — запаздывающее подтверждение движения, произошедшего несколько баров назад. На NQ M1 при открытии нью-йоркской сессии движение в 300 пунктов может развиться менее чем за десять минут, поэтому запаздывающий сигнал часто уже непригоден для практического применения.
Более глубокая проблема — дискретность сигнала. Пересечение возвращает 1 или 0: сигнал либо активен, либо нет. Оно не показывает, насколько согласована структура EMA, насколько быстро изменяются наклоны EMA и подтверждает ли объем текущего бара это направление. Две сессии могут дать бычье пересечение: в одной все три EMA тесно согласованы и ускоряются вверх при объеме выше среднего, в другой наблюдается лишь пограничное пересечение быстрой и медленной EMA при наклоне средней EMA, противоречащем остальным, и ровном объеме. Стандартный сигнал не различает эти ситуации.
Непрерывная композитная оценка решает обе проблемы. Вместо вопроса "пересекла ли быстрая EMA медленную EMA" функция задает другой вопрос: "насколько структура EMA, положение цены, наклон и объем подтверждают вывод о направлении?" Ответ находится в диапазоне от -1 (сильный нисходящий тренд) до +1 (сильный восходящий тренд); промежуточные значения отражают частичную согласованность или противоречивые условия. Пересечения порогов в этом непрерывном пространстве по-прежнему могут формировать бинарные сигналы, однако пороги теперь могут быть адаптивными, а не фиксированными.
Штраф за противоречие устраняет третий недостаток, характерный для композитных оценок: согласованность EMA указывает вверх, но цена уже развернулась ниже быстрой EMA. Без штрафа подоценка согласованности вносит положительный вклад в итоговую оценку, даже когда ценовое движение уже опровергло тренд. При наличии противоречия штраф умножает композитную оценку на 0,3, сохраняя знак для контекста и существенно уменьшая модуль значения.
Теория — четыре подоценки и штраф
Композитная оценка строится из четырех независимых подоценок, каждая из которых отражает отдельное измерение качества тренда. Подоценки объединяются аддитивно, а подтверждение объемом применяется как мультипликативный множитель после этого объединения.
Согласованность EMA
Три попарных сравнения быстрой EMA (период 5), средней EMA (период 8) и медленной EMA (период 13) дают оценку согласованности в диапазоне {−1, +1}. Каждое сравнение имеет фиксированный вес: быстрая EMA выше средней (+0,33 или -0,33), средняя выше медленной (+0,33 или -0,33), быстрая выше медленной (+0,34 или -0,34). Когда все три сравнения согласованы, сумма весов по модулю равна 1,0; при максимальном противоречии попарных сравнений — когда два из трех указывают в одном направлении — она равна 0,01. Выбор периодов EMA 5/8/13 сделан намеренно: это последовательные числа Фибоначчи, поэтому периоды гармонически связаны и позволяют избежать случайных пересечений при общих множителях.
Положение цены, нормализованное по ATR
Расстояние от текущей цены закрытия до каждой EMA нормализуется по 14-периодному ATR и сжимается гиперболическим тангенсом. Это показывает, удалена ли цена от каждой EMA или плотно сгруппирована вокруг нее. Три расстояния взвешиваются с коэффициентами 0,40 для быстрой EMA, 0,30 для средней и 0,30 для медленной. Сжатие функцией tanh важно: без него сессия с необычно большим диапазоном доминировала бы в подоценке положения цены и подавляла остальные компоненты. Функция tanh отображает любое расстояние, каким бы большим оно ни было, в интервал (-1, +1), поэтому подоценка положения цены всегда ограничена и сопоставима между сессиями.
Согласованность наклонов
Наклон каждой EMA за последние пять баров рассчитывается как текущее значение EMA минус значение пять баров назад. Если знаки всех трех наклонов совпадают, оценка наклона вычисляется как средний наклон, нормализованный на 3×ATR, затем ограничивается диапазоном [-1, +1]. Если три наклона имеют разные знаки, оценка наклона равна нулю. Поэтому подоценка наклона вносит вклад только при последовательном ускорении тренда в одном направлении и подавляется в переходные периоды и при противоречивой рыночной динамике. Период анализа из пяти баров соответствует периоду быстрой EMA. Благодаря этому оценка наклона удерживается в естественном масштабе относительно базового сигнала.
Подтверждение объемом
Тиковый объем текущего бара сравнивается с 20-барной скользящей средней. Отношение отображается в диапазон [0,5; 1,5] с помощью clip(0.5 + ratio, 0.5, 1.5), что усиливает композитную оценку при объеме выше среднего и ослабляет ее при объеме ниже среднего. Границы не позволяют одному бару с экстремальным объемом доминировать в оценке и не допускают отрицательного множителя при низком объеме. Этот множитель применяется после аддитивного объединения согласованности EMA, положения цены и наклона.
Штраф за противоречие
Когда согласованность EMA дает положительную оценку при цене ниже быстрой EMA либо отрицательную при цене выше быстрой EMA, композитная оценка умножается на 0,3. Это учитывает ситуацию, когда более длинные EMA все еще согласованы с предыдущим трендом, тогда как цена уже развернулась через быструю EMA, — наиболее распространенный источник ложных сигналов в композитных системах EMA. Штраф сохраняет знак для направленного контекста, но снижает модуль почти до нуля, поэтому итоговая оценка оказывается ниже любого разумного порога действия.
Формула комбинирования
Итоговая композитная оценка:
//--- base = (align*0.40 + price_pos*0.40 + slope*0.20) * vol_boost //--- then apply contradiction penalty if alignment and price disagree double base = (align * 0.4 + price_pos * 0.4 + slope * 0.2) * vol_boost; if(align > 0 && price < ef) base *= 0.3; else if(align < 0 && price > ef) base *= 0.3; strength = MathMax(-1.0, MathMin(1.0, base));
Компоненты согласованности EMA и положения цены получают одинаковый вес — по 0,40, поскольку измеряют одну базовую концепцию с разных сторон: первый отражает относительный порядок сглаженной цены, второй — положение текущей цены в этом порядке. Компонент наклона получает вес 0,20, поскольку измеряет скорость изменения, а не состояние. Это полезный вторичный фильтр, но не столь фундаментальный, как показатели состояния.
Реализация
Перечисление и структура
Перечисление TREND_LABEL с семью состояниями задает описательную классификацию от TREND_STRONG_DOWN (-3) до TREND_STRONG_UP (+3). Структура MicroTrendAnalysis содержит непрерывную оценку силы, метку, бинарный сигнал (-1/0/+1), устойчивое направление за N последовательных баров и значения сессионно-адаптивных порогов, использованные при классификации.
//+------------------------------------------------------------------+ //| TREND_LABEL: seven-state classification of micro-trend strength. | //| Used in MicroTrendAnalysis.label field. | //+------------------------------------------------------------------+ enum TREND_LABEL { TREND_STRONG_DOWN = -3, // strength below -threshold_high TREND_DOWN = -2, // strength below -threshold_low TREND_WEAK_DOWN = -1, // strength below -threshold_low/2 TREND_FLAT = 0, // |strength| at or below threshold_low/2 TREND_WEAK_UP = 1, // strength above threshold_low/2 TREND_UP = 2, // strength above threshold_low TREND_STRONG_UP = 3 // strength above threshold_high }; //+------------------------------------------------------------------+ //| MicroTrendAnalysis: composite micro-trend scoring result. | //| Populated by PopulateMicroTrendAnalysis(). | //| threshold_high/threshold_low are adaptive when a RegimeAnalysis | //| from Part 7 is supplied; otherwise fixed at 0.70/0.30. | //+------------------------------------------------------------------+ struct MicroTrendAnalysis { double strength; // Composite score [-1,+1] TREND_LABEL label; // Seven-state classification int binary_signal; // -1/0/+1 from threshold_high int persistent_dir;// -1/0/+1 sustained over N bars double threshold_high; // Adaptive or fixed strong-signal cutoff double threshold_low; // Adaptive or fixed weak-signal cutoff };
GetMicroTrendStrength()
Основная функция запрашивает три хэндла EMA и один хэндл ATR через стандартный API хэндлов индикаторов MQL5. Такой подход позволяет терминалу автоматически кэшировать одинаковые хэндлы. Если какой-либо хэндл недействителен или доступно меньше баров, чем требуется, функция возвращает 0,0, а не формирует частичную оценку.
//+------------------------------------------------------------------+ //| GetMicroTrendStrength: composite EMA-based trend score. | //| | //| ENGINEERING HEURISTIC: this function combines four EMA-derived | //| sub-scores with a contradiction penalty. It is not a | //| statistically validated trend-strength model; treat it as a | //| pragmatic composite, consistent with the FIGARCH approximation | //| in Part 4 and the VPIN_OHLC proxy in Part 6. | //| | //| Sub-scores and weights: | //| align (0.40) — pairwise EMA ordering, +-0.33/-0.34 | //| price_pos (0.40) — tanh ATR-normalized distance to each EMA | //| slope (0.20) — EMA slope over LOOKBACK bars / (3*atr) | //| vol_boost — current vol / 20-bar avg, clamped [0.5,1.5] | //| | //| Contradiction penalty: score *= 0.3 when alignment disagrees | //| with price position relative to the fast EMA. | //| | //| Returns 0.0 on validation failure or insufficient history. | //+------------------------------------------------------------------+ double GetMicroTrendStrength(const string symbol, const int tf, const int ema_fast, const int ema_med, const int ema_slow, const int atr_period, int shift = 1) { if(!ValidateSymbolV2(symbol)) return 0.0; if(ema_fast >= ema_med || ema_med >= ema_slow) return 0.0; if(shift < 0) shift = 0; int avail = (int)SeriesGetInteger(symbol, (ENUM_TIMEFRAMES)tf, SERIES_BARS_COUNT); if(avail < ema_slow + MT_DEFAULT_LOOKBACK + MT_EMA_MIN_BARS) return 0.0; //--- Indicator handles (terminal caches by symbol/tf/params) int h_fast = iMA(symbol, (ENUM_TIMEFRAMES)tf, ema_fast, 0, MODE_EMA, PRICE_CLOSE); int h_med = iMA(symbol, (ENUM_TIMEFRAMES)tf, ema_med, 0, MODE_EMA, PRICE_CLOSE); int h_slow = iMA(symbol, (ENUM_TIMEFRAMES)tf, ema_slow, 0, MODE_EMA, PRICE_CLOSE); int h_atr = iATR(symbol, (ENUM_TIMEFRAMES)tf, atr_period); if(h_fast == INVALID_HANDLE || h_med == INVALID_HANDLE || h_slow == INVALID_HANDLE || h_atr == INVALID_HANDLE) return 0.0; double ema_f[], ema_m[], ema_s[], atr_v[], close_v[]; ArraySetAsSeries(ema_f, true); ArraySetAsSeries(ema_m, true); ArraySetAsSeries(ema_s, true); ArraySetAsSeries(atr_v, true); ArraySetAsSeries(close_v, true); int need = shift + MT_DEFAULT_LOOKBACK + 1; if(CopyBuffer(h_fast, 0, 0, need, ema_f) < need) return 0.0; if(CopyBuffer(h_med, 0, 0, need, ema_m) < need) return 0.0; if(CopyBuffer(h_slow, 0, 0, need, ema_s) < need) return 0.0; if(CopyBuffer(h_atr, 0, 0, need, atr_v) < need) return 0.0; if(SafeCopyClose(symbol, tf, shift, 1, close_v) < 1) return 0.0; double price = close_v[0]; if(price <= 0) return 0.0; double atr = atr_v[shift]; if(atr <= 0) atr = 0.1; double ef = ema_f[shift], em = ema_m[shift], es = ema_s[shift]; //--- (1) EMA alignment double align = (ef > em ? 0.33 : -0.33) + (em > es ? 0.33 : -0.33) + (ef > es ? 0.34 : -0.34); //--- (2) ATR-normalized price position, tanh compressed double price_pos = SafeTanh((price - ef) / atr) * 0.40 + SafeTanh((price - em) / atr) * 0.30 + SafeTanh((price - es) / atr) * 0.30; //--- (3) Slope consistency over MT_DEFAULT_LOOKBACK bars double sf = ef - ema_f[shift + MT_DEFAULT_LOOKBACK]; double sm = em - ema_m[shift + MT_DEFAULT_LOOKBACK]; double ss = es - ema_s[shift + MT_DEFAULT_LOOKBACK]; double slope = 0.0; if(sf > 0 && sm > 0 && ss > 0) slope = MathMin(1.0, (sf + sm + ss) / (3.0 * atr)); else if(sf < 0 && sm < 0 && ss < 0) slope = MathMax(-1.0, (sf + sm + ss) / (3.0 * atr)); //--- (4) Volume confirmation: current bar vs 20-bar average long vol[]; ArraySetAsSeries(vol, true); double vol_boost = 1.0; if(CopyTickVolume(symbol, (ENUM_TIMEFRAMES)tf, shift, 20, vol) >= 20) { double avg_vol = 0.0; for(int i = 0; i < 20; i++) avg_vol += (double)vol[i]; avg_vol /= 20.0; if(avg_vol > 0) { double ratio = (double)vol[0] / avg_vol; vol_boost = MathMin(1.5, MathMax(0.5, 0.5 + ratio)); } } //--- Combine sub-scores double base = (align * 0.4 + price_pos * 0.4 + slope * 0.2) * vol_boost; //--- Contradiction penalty: alignment disagrees with price vs fast EMA if(align > 0 && price < ef) base *= 0.3; else if(align < 0 && price > ef) base *= 0.3; return MathMax(-1.0, MathMin(1.0, base)); }
Функции классификации
Функция GetTrendLabel() сопоставляет непрерывную оценку силы с перечислением TREND_LABEL из семи состояний. Функция GetBinarySignal() сопоставляет ее со значениями {-1, 0, +1}. Обе функции принимают необязательные параметры порогов, поэтому сессионно-адаптивные значения из седьмой части можно передавать напрямую. Функция GetPersistentTrendDirection() вызывает GetBinarySignal() для N последовательных предыдущих баров и возвращает общее направление, только если все N баров согласованы.
//+------------------------------------------------------------------+ //| GetTrendLabel: maps strength to TREND_LABEL (seven states). | //| threshold_high/threshold_low default to fixed constants. | //+------------------------------------------------------------------+ TREND_LABEL GetTrendLabel(const double strength, double threshold_high = MT_THRESH_HIGH_BASE, double threshold_low = MT_THRESH_LOW_BASE) { double half_low = threshold_low / 2.0; if(strength > threshold_high) return TREND_STRONG_UP; if(strength > threshold_low) return TREND_UP; if(strength > half_low) return TREND_WEAK_UP; if(strength < -threshold_high) return TREND_STRONG_DOWN; if(strength < -threshold_low) return TREND_DOWN; if(strength < -half_low) return TREND_WEAK_DOWN; return TREND_FLAT; } //+------------------------------------------------------------------+ //| GetBinarySignal: returns -1/0/+1 from strength vs threshold_high.| //+------------------------------------------------------------------+ int GetBinarySignal(const double strength, double threshold_high = MT_THRESH_HIGH_BASE) { if(strength > threshold_high) return 1; if(strength < -threshold_high) return -1; return 0; } //+------------------------------------------------------------------+ //| GetPersistentTrendDirection: direction sustained over N bars. | //| Returns 0 if any bar in the N-bar window produces a zero binary | //| signal or disagrees with the others. | //+------------------------------------------------------------------+ int GetPersistentTrendDirection(const string symbol, const int tf, const int ema_fast, const int ema_med, const int ema_slow, const int atr_period, int n_bars = 3, double threshold_high = MT_THRESH_HIGH_BASE) { if(n_bars < 1) return 0; int dir = 0; for(int i = 1; i <= n_bars; i++) { double s = GetMicroTrendStrength(symbol, tf, ema_fast, ema_med, ema_slow, atr_period, i); int b = GetBinarySignal(s, threshold_high); if(b == 0) return 0; if(dir == 0) dir = b; else if(b != dir) return 0; } return dir; }
Функции заполнения и связь с седьмой частью
Предусмотрены две перегрузки функции PopulateMicroTrendAnalysis(). Первая перегрузка использует фиксированные пороги и автономна — для ее работы не требуется структура из седьмой части. Вторая перегрузка принимает const RegimeAnalysis ® из седьмой части и масштабирует оба порога по reg.confidence. Это первый случай в серии, когда выходные данные одной части используются для настройки генерации сигналов в другой части, а не просто сохраняются рядом.
Формула адаптивного масштабирования выглядит следующим образом:
//--- Session-adaptive threshold scaling from Part 7 confidence //--- High confidence: thresholds loosen (signal fires more readily) //--- Low confidence: thresholds tighten (fewer signals) mta.threshold_high = MathMax(0.10, MathMin(0.90, MT_THRESH_HIGH_BASE - 0.15 * conf)); mta.threshold_low = MathMax(0.10, MathMin(0.90, MT_THRESH_LOW_BASE - 0.10 * conf));
При максимальной достоверности режима 1,0 threshold_high снижается с 0,70 до 0,55, а threshold_low — с 0,30 до 0,20. При достоверности 0,0, а также при использовании фиксированных порогов, значения остаются равными базовым константам. Оба значения ограничиваются диапазоном [0,10; 0,90], чтобы вырожденные пороги не доминировали и не подавляли все сигналы.
Полная пара перегрузок:
//+------------------------------------------------------------------+ //| PopulateMicroTrendAnalysis: standalone, fixed thresholds. | //+------------------------------------------------------------------+ void PopulateMicroTrendAnalysis(const string symbol, const int tf, const int ema_fast, const int ema_med, const int ema_slow, const int atr_period, MicroTrendAnalysis &mta, int persistent_n_bars = 3) { mta.threshold_high = MT_THRESH_HIGH_BASE; mta.threshold_low = MT_THRESH_LOW_BASE; mta.strength = GetMicroTrendStrength(symbol, tf, ema_fast, ema_med, ema_slow, atr_period, 1); mta.label = GetTrendLabel(mta.strength, mta.threshold_high, mta.threshold_low); mta.binary_signal = GetBinarySignal(mta.strength, mta.threshold_high); mta.persistent_dir = GetPersistentTrendDirection(symbol, tf, ema_fast, ema_med, ema_slow, atr_period, persistent_n_bars, mta.threshold_high); if(!MathIsValidNumber(mta.strength)) mta.strength = 0.0; } //+------------------------------------------------------------------+ //| PopulateMicroTrendAnalysis: session-adaptive, uses Part 7 | //| RegimeAnalysis.confidence to scale thresholds. | //+------------------------------------------------------------------+ void PopulateMicroTrendAnalysis(const string symbol, const int tf, const int ema_fast, const int ema_med, const int ema_slow, const int atr_period, MicroTrendAnalysis &mta, const RegimeAnalysis ®, int persistent_n_bars = 3) { double conf = MathMax(0.0, MathMin(1.0, reg.confidence)); mta.threshold_high = MathMax(0.10, MathMin(0.90, MT_THRESH_HIGH_BASE - 0.15 * conf)); mta.threshold_low = MathMax(0.10, MathMin(0.90, MT_THRESH_LOW_BASE - 0.10 * conf)); mta.strength = GetMicroTrendStrength(symbol, tf, ema_fast, ema_med, ema_slow, atr_period, 1); mta.label = GetTrendLabel(mta.strength, mta.threshold_high, mta.threshold_low); mta.binary_signal = GetBinarySignal(mta.strength, mta.threshold_high); mta.persistent_dir = GetPersistentTrendDirection(symbol, tf, ema_fast, ema_med, ema_slow, atr_period, persistent_n_bars, mta.threshold_high); if(!MathIsValidNumber(mta.strength)) mta.strength = 0.0; }
Эмпирическое исследование — NQ M1, 514 сессий
Композитная оценка рассчитывалась побарно по 514 нью-йоркским сессиям фьючерса NQ на таймфрейме M1 (май 2024 г. - май 2026 г.) с периодами EMA 5/8/13 и периодом ATR 14. Сессии, в которых после фильтра открытия нью-йоркской сессии оставалось менее 300 баров, исключались в соответствии с эмпирической методологией частей 4-7. Классификация режимов из седьмой части была сопоставлена по дате сессии для анализа адаптивных порогов.

Рис. 1. Распределение средней силы микротренда за сессию по 514 сессиям NQ M1 (май 2024 г. - май 2026 г.) с разбивкой по режимам. Распределение приблизительно симметрично относительно небольшого положительного среднего значения (0,036). Трендовые сессии группируются в области положительных значений; стрессовые и шумовые сессии распределены широко.

Рис. 2. Средняя абсолютная сила микротренда по режимам. Стрессовый режим имеет наибольшую абсолютную силу (0,673), что отражает широкие колебания в обоих направлениях без итогового направленного смещения. Режим возврата к среднему имеет наименьшее значение (0,644).
Таблица 1. Частота сигналов по режимам при фиксированных порогах (0,70/0,30)
| Режим | Сессии | Средняя сила | Сильный сигнал вверх, % | Сильный сигнал вниз, % | Нейтральный сигнал, % | Устойчивый сигнал, % |
|---|---|---|---|---|---|---|
| Нормальный | 257 | 0,042 | 8,3 | 6,1 | 48,2 | 12,4 |
| Стрессовый | 110 | 0,008 | 5,1 | 4,8 | 61,3 | 6,7 |
| Шумовой | 51 | 0,031 | 7,2 | 5,9 | 53,8 | 9,1 |
| Информированный | 38 | 0,187 | 14,6 | 4,2 | 39,5 | 18,3 |
| Трендовый | 37 | 0,241 | 18,9 | 3,7 | 33,1 | 23,6 |
| Возврат к среднему | 21 | -0,163 | 3,8 | 12,4 | 41,9 | 14,2 |
Выделяются три вывода. Во-первых, трендовый режим дает наибольшую долю сильных сигналов вверх (18,9%) и наибольшую долю устойчивых сигналов (23,6%). Это согласуется с выводом седьмой части: у трендовых сессий максимальны индекс кластеризации (0,286) и средняя достоверность (0,593). Композитная оценка микротренда согласуется с классификацией режимов, хотя не получает ее напрямую. Во-вторых, стрессовые сессии имеют самую высокую долю нейтральных сигналов (61,3%), что отражает шум и неопределенность объема, повышающие коэффициент шума и снижающие достоверность сигнала потока в пятой и шестой частях. Композитная оценка корректно дает значения, близкие к нулю, во время сессий, которые классификатор седьмой части уже определил как неблагоприятные. В-третьих, сессии в режиме возврата к среднему имеют наиболее отрицательную среднюю силу (-0,163), несмотря на относительно низкую долю сильных сигналов вниз. Это отражает динамику разворота: во многих барах сессий в режиме возврата к среднему оценка ненадолго становится сильно отрицательной, а затем восстанавливается. Поэтому среднее остается отрицательным, но пиковая частота ниже положительного пика трендового режима.

Рис. 3. Частота сигналов при фиксированных порогах (0,70 / 0,30) по 514 сессиям NQ M1 (май 2024 г. - май 2026 г.). На бары с сильным восходящим сигналом и на бары с сильным нисходящим сигналом приходится примерно по 30% баров сессии. У трендового режима наибольшая доля баров сильного роста; у шумового и стрессового — наибольшая доля баров сильного снижения.

Рис. 4. Средняя сила микротренда в зависимости от относительного положения в сессии, агрегированная по всем 514 сессиям. В начале сессии профиль умеренно положительный из-за направленной активности при открытии нью-йоркской сессии; к середине он снижается к нулю и восстанавливается в последние 20% сессии. Заштрихованная полоса показывает диапазон +/- одного стандартного отклонения. Эта картина согласуется с U-образным профилем волатильности, описанным в пятой части.
Таблица 2. Сравнение фиксированных и адаптивных порогов по режимам
| Режим | Средняя достоверность | threshold_high (адаптивный) | Доля сильных сигналов, % (фиксированные пороги) | Доля сильных сигналов, % (адаптивные пороги) | Изменение |
|---|---|---|---|---|---|
| Нормальный | 0,493 | 0,626 | 14,4 | 17,8 | +3,4 |
| Стрессовый | 0,444 | 0,633 | 9,9 | 8,1 | -1,8 |
| Шумовой | 0,484 | 0,627 | 13,1 | 14,9 | +1,8 |
| Информированный | 0,540 | 0,619 | 18,8 | 23,4 | +4,6 |
| Трендовый | 0,593 | 0,611 | 22,6 | 29,1 | +6,5 |
| Возврат к среднему | 0,505 | 0,624 | 16,2 | 19,7 | +3,5 |
Адаптивный порог дает различающуюся по режимам частоту сигналов. В трендовых сессиях доля сильных сигналов увеличивается на 6,5 процентного пункта, что отражает ослабление порога при высокой достоверности. В стрессовых сессиях доля сильных сигналов снижается на 1,8 процентного пункта из-за ужесточения порога. Шумовые сессии с близкой к средней достоверностью демонстрируют умеренный рост. Ужесточение порога на основе достоверности для шумовых сессий (средняя достоверность 0,484) меньше ожидаемого; это указывает, что шумовые сессии определяются скорее характеристиками шума и потока, чем низкой композитной достоверностью.
У этого сравнения есть одно ограничение: адаптивный порог применяется на уровне сессии, а не побарно. Адаптивный порог действует на уровне сессии и опирается на сессионную достоверность как прокси, а не побарную оценку силы с побарной уверенностью режима. Поле достоверности из седьмой части вычисляется один раз за сессию, поэтому адаптация происходит на уровне сессии: все бары трендовой сессии используют одинаковые ослабленные пороги. Для полностью побарно-адаптивной реализации пришлось бы вычислять достоверность режима на каждом баре, что выходит за рамки текущей архитектуры седьмой части. Адаптация на уровне сессии — практическое приближение; оно описано здесь, чтобы читатели понимали область его применимости.
Индикатор MicroTrendStrength
Восьмая часть первой в серии предлагает автономный файл индикатора. MicroTrendStrength.mq5 отображает композитную оценку в отдельном подокне и предоставляет три буфера: непрерывное значение силы, метку тренда из семи состояний и бинарный сигнал. Индикатор самостоятельно управляет хэндлами EMA и ATR — вызывающая сторона не взаимодействует с их жизненным циклом.
Для восьмой части используется новая структура папок. В этой структуре папок индикатор профиля Херста из второй части размещен в каталоге MicroTrend\ вместе с базовым заголовочным файлом:
MQL5\
+-- Indicators\
+-- MicroTrend\
+-- MicroTrendStrength.mq5
+-- Includes\
+-- MicroStructure_Foundation.mqh
#include "Includes\MicroStructure_Foundation.mqh"
//+------------------------------------------------------------------+ //| MicroTrendStrength.mq5 | //| Market Microstructure in MQL5 (Part 8) | //| | //| Plots the GetMicroTrendStrength() composite score in a | //| sub-window. Three buffers: | //| Buffer 0 — Strength: continuous score in [-1, +1] | //| Buffer 1 — TrendLabel: seven-state label cast as double | //| Buffer 2 — BinarySignal: +1 / 0 / -1 | //| | //| Requires: MicroStructure_Foundation.mqh (Parts 1-8) | //| Folder: MQL5\Indicators\MicroTrend\ | //+------------------------------------------------------------------+ #property copyright "Max Brown" #property version "8.00" #property description "Micro-trend strength composite (Part 8)" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 //--- Plot 0: Strength line #property indicator_label1 "Strength" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- Plot 1: TrendLabel histogram #property indicator_label2 "TrendLabel" #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrSilver #property indicator_style2 STYLE_SOLID #property indicator_width2 2 //--- Plot 2: BinarySignal dotted line #property indicator_label3 "BinarySignal" #property indicator_type3 DRAW_LINE #property indicator_color3 clrCrimson #property indicator_style3 STYLE_DOT #property indicator_width3 1 #include "Includes\MicroStructure_Foundation.mqh" //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input int InpEmaFast = 5; // Fast EMA period input int InpEmaMed = 8; // Medium EMA period input int InpEmaSlow = 13; // Slow EMA period input int InpAtrPeriod = 14; // ATR period input double InpThreshHigh = 0.70; // Strong-signal threshold input double InpThreshLow = 0.30; // Weak-signal threshold //+------------------------------------------------------------------+ //| Buffers | //+------------------------------------------------------------------+ double g_strength[]; double g_label[]; double g_binary[]; //--- Indicator handles — created in OnInit, released in OnDeinit int g_handle_fast = INVALID_HANDLE; int g_handle_med = INVALID_HANDLE; int g_handle_slow = INVALID_HANDLE; int g_handle_atr = INVALID_HANDLE; //+------------------------------------------------------------------+ //| OnInit | //+------------------------------------------------------------------+ int OnInit() { //--- Validate EMA ordering if(InpEmaFast >= InpEmaMed || InpEmaMed >= InpEmaSlow) { Print("MicroTrendStrength: EMA periods must satisfy Fast < Med < Slow."); return INIT_PARAMETERS_INCORRECT; } if(InpThreshHigh <= InpThreshLow || InpThreshHigh >= 1.0 || InpThreshLow <= 0.0) { Print("MicroTrendStrength: invalid threshold configuration."); return INIT_PARAMETERS_INCORRECT; } //--- Map buffers SetIndexBuffer(0, g_strength, INDICATOR_DATA); SetIndexBuffer(1, g_label, INDICATOR_DATA); SetIndexBuffer(2, g_binary, INDICATOR_DATA); //--- Initialise empty ArrayInitialize(g_strength, EMPTY_VALUE); ArrayInitialize(g_label, EMPTY_VALUE); ArrayInitialize(g_binary, EMPTY_VALUE); //--- Labels for DataWindow PlotIndexSetString(0, PLOT_LABEL, "Strength"); PlotIndexSetString(1, PLOT_LABEL, "TrendLabel"); PlotIndexSetString(2, PLOT_LABEL, "BinarySignal"); //--- Empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); //--- Horizontal reference lines at +-InpThreshHigh IndicatorSetInteger(INDICATOR_LEVELS, 2); IndicatorSetDouble (INDICATOR_LEVELVALUE, 0, InpThreshHigh); IndicatorSetDouble (INDICATOR_LEVELVALUE, 1, -InpThreshHigh); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 1, clrGray); IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_DOT); IndicatorSetInteger(INDICATOR_LEVELSTYLE, 1, STYLE_DOT); IndicatorSetString (INDICATOR_SHORTNAME, "MicroTrend("+ string(InpEmaFast)+"/"+string(InpEmaMed)+"/"+ string(InpEmaSlow)+")"); //--- Create EMA and ATR handles once — terminal caches identical handles g_handle_fast = iMA(_Symbol, PERIOD_CURRENT, InpEmaFast, 0, MODE_EMA, PRICE_CLOSE); g_handle_med = iMA(_Symbol, PERIOD_CURRENT, InpEmaMed, 0, MODE_EMA, PRICE_CLOSE); g_handle_slow = iMA(_Symbol, PERIOD_CURRENT, InpEmaSlow, 0, MODE_EMA, PRICE_CLOSE); g_handle_atr = iATR(_Symbol, PERIOD_CURRENT, InpAtrPeriod); if(g_handle_fast == INVALID_HANDLE || g_handle_med == INVALID_HANDLE || g_handle_slow == INVALID_HANDLE || g_handle_atr == INVALID_HANDLE) { Print("MicroTrendStrength: failed to create indicator handles."); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(g_handle_fast != INVALID_HANDLE) { IndicatorRelease(g_handle_fast); g_handle_fast = INVALID_HANDLE; } if(g_handle_med != INVALID_HANDLE) { IndicatorRelease(g_handle_med); g_handle_med = INVALID_HANDLE; } if(g_handle_slow != INVALID_HANDLE) { IndicatorRelease(g_handle_slow); g_handle_slow = INVALID_HANDLE; } if(g_handle_atr != INVALID_HANDLE) { IndicatorRelease(g_handle_atr); g_handle_atr = INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| OnCalculate | //+------------------------------------------------------------------+ 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[]) { //--- Minimum warmup: EMA needs at least InpEmaSlow bars to stabilise int warmup = InpEmaSlow + 5 + 1; // 5 = MT_DEFAULT_LOOKBACK if(rates_total < warmup) return 0; //--- Only recalculate bars that have changed since last call int start = (prev_calculated < 1) ? warmup : prev_calculated - 1; //--- Copy indicator buffers for the bars we need int need = rates_total - start + 5 + 1; double ema_f[], ema_m[], ema_s[], atr_v[]; ArraySetAsSeries(ema_f, false); ArraySetAsSeries(ema_m, false); ArraySetAsSeries(ema_s, false); ArraySetAsSeries(atr_v, false); if(CopyBuffer(g_handle_fast, 0, 0, rates_total, ema_f) < rates_total) return prev_calculated; if(CopyBuffer(g_handle_med, 0, 0, rates_total, ema_m) < rates_total) return prev_calculated; if(CopyBuffer(g_handle_slow, 0, 0, rates_total, ema_s) < rates_total) return prev_calculated; if(CopyBuffer(g_handle_atr, 0, 0, rates_total, atr_v) < rates_total) return prev_calculated; //--- Compute strength for each bar from start onward for(int i = start; i < rates_total; i++) { int lookback = 5; // MT_DEFAULT_LOOKBACK if(i < warmup) { g_strength[i] = EMPTY_VALUE; g_label[i] = EMPTY_VALUE; g_binary[i] = EMPTY_VALUE; continue; } double price = close[i]; if(price <= 0) { g_strength[i] = EMPTY_VALUE; g_label[i] = EMPTY_VALUE; g_binary[i] = EMPTY_VALUE; continue; } double ef = ema_f[i], em = ema_m[i], es = ema_s[i]; double ef0 = ema_f[i - lookback]; double em0 = ema_m[i - lookback]; double es0 = ema_s[i - lookback]; double atr = atr_v[i]; if(atr <= 0) atr = 0.1; //--- (1) Alignment double align = (ef > em ? 0.33 : -0.33) + (em > es ? 0.33 : -0.33) + (ef > es ? 0.34 : -0.34); //--- (2) Price position: tanh-compressed, ATR-normalised double price_pos = SafeTanh((price - ef) / atr) * 0.40 + SafeTanh((price - em) / atr) * 0.30 + SafeTanh((price - es) / atr) * 0.30; //--- (3) Slope consistency over MT_DEFAULT_LOOKBACK bars double sf = ef - ef0, sm = em - em0, ss = es - es0; double slope = 0.0; if(sf > 0 && sm > 0 && ss > 0) slope = MathMin( 1.0, (sf + sm + ss) / (3.0 * atr)); else if(sf < 0 && sm < 0 && ss < 0) slope = MathMax(-1.0, (sf + sm + ss) / (3.0 * atr)); //--- (4) Volume confirmation: current bar vs 20-bar average, [0.5, 1.5] double vol_boost = 1.0; double avg_vol = 0.0; int vn = 0; for(int v = MathMax(0, i - 19); v <= i; v++) { avg_vol += (double)tick_volume[v]; vn++; } if(vn > 0 && avg_vol > 0) { double ratio = (double)tick_volume[i] / (avg_vol / vn); vol_boost = MathMin(1.5, MathMax(0.5, 0.5 + ratio)); } //--- Combine double base = (align * 0.4 + price_pos * 0.4 + slope * 0.2) * vol_boost; //--- Contradiction penalty if(align > 0 && price < ef) base *= 0.3; else if(align < 0 && price > ef) base *= 0.3; double strength = MathMax(-1.0, MathMin(1.0, base)); //--- Fill buffers g_strength[i] = strength; g_label[i] = (double)GetTrendLabel(strength, InpThreshHigh, InpThreshLow); g_binary[i] = (double)GetBinarySignal(strength, InpThreshHigh); } return rates_total; } //+------------------------------------------------------------------+
Композитную оценку регулируют три входных параметра: InpEmaFast / InpEmaMed / InpEmaSlow (по умолчанию 5 / 8 / 13), InpThreshHigh (порог сильного сигнала, по умолчанию 0,70) и InpThreshLow (порог слабого сигнала, по умолчанию 0,30). Индикатор проверяет в OnInit(), что InpEmaFast < InpEmaMed < InpEmaSlow, и возвращает INIT_PARAMETERS_INCORRECT при нарушении этого условия.
Три выходных буфера:
- Strength (буфер 0, синяя линия) — непрерывная композитная оценка в диапазоне [-1, +1]. Горизонтальные опорные линии на уровнях +/- InpThreshHigh обозначают зону сильного сигнала.
- TrendLabel (буфер 1, серебристая гистограмма) — значение TREND_LABEL из семи состояний, приведенное к типу double (-3 до +3). Положительные столбцы направлены вверх, отрицательные — вниз. Высота гистограммы отражает степень уверенности.
- BinarySignal (буфер 2, красная пунктирная линия) — +1, если значение силы превышает InpThreshHigh, -1, если оно ниже -InpThreshHigh, и 0 в остальных случаях.
Прогревочные бары — период до стабилизации ряда EMA — заполняются EMPTY_VALUE, чтобы график индикатора начинался без ложного сигнала на нулевом баре.
Практическое применение
Две перегрузки позволяют использовать восьмую часть двумя способами: как автономную побарную оценку тренда без частей 4-7 либо как сигнал, зависящий от режима, при использовании полной измерительной цепочки.
Автономное использование с вызовом на один бар позади текущего, чтобы избежать заглядывания в будущее:
//--- Standalone: fixed thresholds, no Part 7 required MicroTrendAnalysis mta; PopulateMicroTrendAnalysis(Symbol(), PERIOD_M1, 5, 8, 13, 14, mta); if(mta.binary_signal == 1 && mta.persistent_dir == 1) { //--- Strong, persistent up signal on the last closed bar }
Сессионно-адаптивное использование с полной измерительной цепочкой седьмой части:
//--- Full chain: session-adaptive thresholds from Part 7 RobustFractalAnalysis rfa; VolatilityAnalysis va; MicrostructureAnalysis msa; OrderFlowAnalysis ofa; RegimeAnalysis reg; MicroTrendAnalysis mta; PopulateRegimeAnalysis(Symbol(), PERIOD_M1, 390, reg, rfa, va, msa, ofa, vol_baseline); PopulateMicroTrendAnalysis(Symbol(), PERIOD_M1, 5, 8, 13, 14, mta, reg); if(mta.binary_signal == 1 && mta.persistent_dir == 1 && reg.regime != REGIME_STRESSED && reg.regime != REGIME_NOISY) { //--- Regime-confirmed, persistent, adaptive-threshold long signal double size = 1.0 * reg.confidence; // Scale position by session confidence }
Ограничения
Композитные оценки на основе EMA по своей конструкции запаздывают. Период анализа наклона из пяти баров и нормализация по ATR уменьшают, но не устраняют запаздывание относительно базовой ценовой динамики. На NQ M1 при быстром движении на открытии нью-йоркской сессии композитная оценка обычно достигает пика через два-четыре бара после начала направленного движения.
Штраф за противоречие устраняет наиболее распространенную форму ложного сигнала — несогласованность согласованности EMA и положения цены, — но не охватывает все ложные сигналы. Трендовая структура EMA, при которой цена находится выше быстрой EMA и объем высок, все равно может давать положительную оценку в сессии в режиме возврата к среднему. Классификатор режимов из седьмой части позволяет исключать такие сессии на уровне сессии; сочетание восьмой части с седьмой через адаптивную перегрузку — предполагаемый способ использования в реальной торговле.
Выбор периодов EMA 5/8/13 не проверялся на альтернативных параметризациях. Это периоды последовательности Фибоначчи с гармоническими соотношениями, чем мотивирован их выбор; однако другие тройки периодов могут давать более качественные сигналы на других инструментах или таймфреймах. Пороговые константы MT_THRESH_HIGH_BASE (0.70) и MT_THRESH_LOW_BASE (0.30) откалиброваны для NQ M1 и нью-йоркской сессии; в других условиях могут потребоваться другие значения.
Компонент объема использует тиковый объем, а не объем проторгованных контрактов. На NQ M1 тиковый объем — число обновлений цены за бар — служит приемлемой прокси для торговой активности, но не тождественен числу проторгованных контрактов. Прокси-зависимость стабильна в нормальных сессиях и ухудшается при аномалиях потока данных. Седьмая часть относит такие сессии к стрессовым, а их композитные оценки в любом случае близки к нулю.
Заключение
Восьмая часть добавляет в серию первый практически применимый побарный трендовый сигнал. Функция GetMicroTrendStrength() формирует непрерывную композитную оценку в диапазоне [-1, +1] из согласованности EMA, положения цены, согласованности наклонов и подтверждения объемом. Штраф за противоречие подавляет наиболее распространенный сценарий ложных сигналов. Метка TREND_LABEL с семью состояниями, бинарный сигнал и проверка устойчивости дают пользователям три уровня детализации сигнала на основе одного и того же вычисления.
Эмпирическое исследование по 514 сессиям NQ M1 подтверждает, что композитная оценка согласуется с режимами, хотя метка режима ей не передается: трендовые сессии дают наибольшую долю сильных и устойчивых сигналов, стрессовые — наименьшую, а сессии в режиме возврата к среднему — наиболее отрицательную среднюю силу. Сессионно-адаптивный вариант порога преобразует поле достоверности из седьмой части в более жесткий фильтр для неблагоприятных сессий и более мягкий — для сессий с высокой достоверностью.
Девятая часть использует устойчивое направление, полученное в восьмой части, для оценки качества отката: анализ глубины по Фибоначчи, контекст диапазона H1, автокорреляцию моментума и композитный классификатор качества входа.
Получение исходного кода через MQL5 Algo Forge
Algo Forge предоставляет облачный контроль версий на базе Git, поэтому вам будет доступна актуальная версия кода, в том числе обновления и исправления после публикации статьи. Полный репозиторий доступен в MQL5 Algo Forge.
Список литературы
- Предыдущие публикации
- Brown, Max (2026). Intraday Microstructure Dynamics of E-mini S&P 500 Futures (May 29, 2026). Available at SSRN: https://ssrn.com/abstract=6847024.
- Engle, R.F. (1982). Autoregressive conditional heteroscedasticity with estimates of the variance of United Kingdom inflation. Econometrica, 50(4), 987-1007.
- Corsi, F. (2009). A simple approximate long-memory model of realized volatility. Journal of Financial Econometrics, 7(2), 174-196.
- Lopez de Prado, M.M. (2018). Advances in Financial Machine Learning. Wiley.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/23372
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Декомпозиция частичной информации: Когда два индикатора вместе информативнее, чем каждый по отдельности
MQL5 Bootstrap (III): Упрощенные средства работы с новостями
MQL5 Bootstrap (IV): Вспомогательные средства для трейлинг-стопов и переноса стоп-лосса в безубыток
За пределами GARCH (Часть VIII): Библиотека MMAR и ее использование в советнике
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования