Рыночная микроструктура в MQL5 (Часть 5): Микроструктурный шум
Введение
В первой части мы создали защитный базовый слой: математические функции с проверками, проверку ценовых данных и надежные статистические примитивы. Во второй части мы добавили взвешенную по достоверности оценку показателя Херста и установили, что данные NQ на таймфрейме M1 в Globex близки к границе случайного блуждания. В третьей части мы добавили функцию-оценщик GPH для параметра дробного дифференцирования d. В четвертой части мы добавили полный набор показателей волатильности — реализованную и фракционную волатильность, прокси по мотивам FIGARCH, эффект левериджа GJR-GARCH, выявление скачков на основе бипауэр-вариации и ширину мультифрактального спектра.
В частях 1-4 мы определили поведение волатильности и наличие у нее памяти. Но происхождение этой волатильности пока не объяснили. На минутном масштабе у изменений цены два разных источника. Первый — новая информация: информированные трейдеры используют сведения, еще не отраженные в цене, и приближают ее к справедливой стоимости. Второй — шум, возникающий из механики торговли: колебания цены между Bid и Ask, издержки немедленного исполнения и дисперсия, порождаемая механизмом обработки ордеров, а не формированием рыночной цены.
Если система не разделяет эти компоненты, ее калибровка систематически неверна. Высокий коэффициент шума означает, что большая часть изменений цены временна и затем обращается вспять. На таких данных индекс кластеризации из четвертой части завышает эффект ARCH. Высокое значение прокси для котируемого спреда указывает на повышенные издержки исполнения независимо от уровня волатильности. Если в сессии преобладает влияние спреда Bid–Ask, оценка фракционной волатильности отражает транзакционные издержки, а не поток информации.
В этой статье мы добавляем в MicroStructure_Foundation.mqh две группы измерений. Во-первых, добавляем структуру MicrostructureAnalysis и пять функций для расчета прокси для котируемого спреда, спреда по модели Ролла, коэффициента шума на основе OHLC, дисбаланса ордеров и неблагоприятного отбора. Во-вторых, добавляем функцию PopulateMicrostructureAnalysis(), связывающую результаты с показателями волатильности из четвертой части и заполняющую поле microstructure_noise, предусмотренное еще в первой части.
В исследовании используется тот же набор данных фьючерса NQ E-mini Nasdaq 100, что и в четвертой части, но расширенный до 602 нью-йоркских сессий с января 2024 года по июнь 2026 года. Расширенная выборка включает весь период тарифного шока апреля 2025 года и все пять стрессовых режимов, рассматриваемых в серии. Эмпирическая основа исследования описана в сопутствующей работе, доступной на SSRN под номером 6847024.
Прежде чем перейти к реализации, нужно явно оговорить один момент. На данных NQ на таймфрейме M1 оценка спреда по модели Ролла близка к нулю. Это эмпирически верный результат, а не ошибка программы. На минутном масштабе для фьючерса с сотнями сделок в баре и минимальным шагом цены 0,25 пункта ковариация последовательных доходностей от закрытия к закрытию определяется преимущественно формированием цены внутри бара, а не колебаниями между Bid и Ask. Функция остается в инструментарии для инструментов и таймфреймов, где колебания между Bid и Ask различимы, а также как теоретическая основа для анализа баров длительностью менее минуты. В эмпирическом разделе это прямо указано.
Результат работы — пять функций, добавленных в MicroStructure_Foundation.mqh, новая структура MicrostructureAnalysis и полностью заполненное поле microstructure_noise структуры RobustFractalAnalysis. Новый подключаемый файл не создается.
Теория микроструктурного шума
Два источника изменения ценыВ литературе по рыночной микроструктуре наблюдаемые изменения цены разделяют на два компонента: инновацию эффективной цены, связанную с новой информацией, и шумовой член, связанный с временными торговыми издержками. Ролл (1984) показал, что даже на эффективном рынке совершение сделок по ценам Bid и Ask порождает отрицательную ковариацию последовательных доходностей. Такая отрицательная ковариация — признак шума. При отрицательной автокорреляции доходностей на лаге, равном 1, ряд показывает возврат к среднему на уровне баров. Причина не в том, что информированные участники предвидят развороты, а в механическом чередовании цен Bid и Ask, которое сначала раздвигает соседние цены, затем вновь сближает их.
Модель РоллаВ модели Ролла (1984) подразумеваемый спред определяется по ковариации последовательных изменений цены. Если c — половина спреда, а сделки случайным образом чередуются между ценами Bid и Ask, подразумеваемый спред равен:
Спред Ролла = 2 × √(max(0, −Cov(Δp_t, Δp_{t−1})))
При отрицательной ковариации последовательных изменений цены спред Ролла положителен, а колебания между Bid и Ask различимы. При положительной ковариации, когда преобладает трендовое движение, оценщик возвращает ноль. В модели Ролла три допущения: эффективная цена следует случайному блужданию, вероятности покупки и продажи равны, спред постоянен. На NQ с таймфреймом M1 ни одно допущение не выполняется в точности, но оценщик остается стандартным приближением первого порядка для определения спреда только по ценовым данным. Описанная в эмпирическом разделе неспособность оценщика обнаружить спред на M1 сама по себе информативна: при минутной агрегации формирование цены внутри бара полностью поглощает сигнал колебаний между Bid и Ask.
Неблагоприятный отборКомпонент неблагоприятного отбора — разность котируемого спреда и спреда по модели Ролла. Если котируемый спред значительно превышает спред Ролла, маркет-мейкеры учитывают риск торговли с более информированными контрагентами — так в наблюдаемых данных проявляется механизм Кайла (1985). Большая доля неблагоприятного отбора в котируемом спреде — предупреждение: в потоке ордеров присутствуют информированные участники, и простые направленные стратегии могут систематически выбирать неверное направление. На NQ с таймфреймом M1 спред Ролла близок к нулю, поэтому компонент неблагоприятного отбора фактически сводится к прокси для котируемого спреда. В эмпирическом разделе это прямо отражено.
Разложение шума на основе OHLCДисперсия отклонения цены закрытия от середины диапазона бара (H+L)/2 характеризует величину этого отклонения. При чисто шумовом процессе цены закрытия равномерно распределены вокруг середины диапазона. При направленном движении цены закрытия устойчиво располагаются возле одной из границ диапазона. Базовый коэффициент шума — отношение дисперсии отклонения цены закрытия от середины диапазона к полной дисперсии бара.
В усовершенствованной версии объединяются три компонента. Первый показатель сопоставляет две дисперсии: для разности между ценой закрытия и серединой диапазона и для самого диапазона бара. Это основной показатель шума. Второй — инвертированное отношение тела бара к диапазону (1 - |C-O|/(H-L)): при движении цены от открытия к закрытию в одном направлении тело занимает почти весь диапазон бара и шум мал; при разнонаправленных колебаниях тело невелико относительно диапазона и шум высок. Третий — доля дисперсии доходности от открытия к закрытию в общей дисперсии диапазона. Компоненты объединяются с весами 40%, 40% и 20% соответственно в один коэффициент шума в диапазоне [0, 1]. Чем выше значение, тем больше шума и слабее направленный сигнал.
Прокси для котируемого спредаДля баров M1 без текущих данных Bid и Ask средний диапазон бара используется как прокси для спреда. Обоснование дано в работах Паркинсона (1980), а также Гармана и Класса (1980): если цена внутри бара следует случайному блужданию, диапазон между ценой максимума и ценой минимума пропорционален стандартному отклонению процесса. Когда при стрессе маркет-мейкеры расширяют спред котировок, последовательные сделки совершаются по ценам с большим разбросом, а диапазон бара пропорционально расширяется. Эта прокси не измеряет спред Bid-Ask напрямую: она смешивает расширение спреда с направленным движением внутри бара и представляет собой относительный показатель, последовательно отражающий издержки исполнения по сессиям. Это прямо указано в заголовке функции и в разделе "Ограничения".
Дисбаланс ордеровДисбаланс ордеров измеряет направленное давление внутри бара. На данных OHLCV без тиковой детализации взвешенная по объему прокси использует положение цены закрытия в диапазоне бара: (Close - Low)/(High - Low) дает значение от 0 до 1 и равно 0,5 при закрытии в середине диапазона. Значения выше 0,5 указывают на давление со стороны покупателей; значения ниже 0,5 — на давление со стороны продавцов. Взвешенное по объему среднее за окно после центрирования относительно нуля дает дисбаланс ордеров в диапазоне [-1, +1]. Бары с High = Low исключаются — их направленный сигнал не определен.
Связь с частями 2-4Есть три прямые связи с предыдущими частями. Во-первых, при высоком microstructure_noise оценка показателя Херста из второй части смещается к 0,5. Шум подавляет сигнал долгой памяти, поэтому H указывает на большую близость к случайному блужданию, чем у истинного процесса эффективной цены. Во-вторых, ковариация в модели Ролла рассчитывается с помощью функции двухпроходного вычисления дисперсии из первой части, обеспечивающей численную устойчивость при близких к нулю дисперсиях. В-третьих, шум и волатильность различаются: по данным 602 сессий корреляция усовершенствованного коэффициента шума с реализованной волатильностью из четвертой части составляет лишь 0,159. Показатели не дублируют друг друга, и это эмпирически обосновывает расчет обеих групп.
Реализация
Новая структура — MicrostructureAnalysis//+------------------------------------------------------------------+ //| MicrostructureAnalysis: noise and spread decomposition results. | //| Populated by PopulateMicrostructureAnalysis(). | //| adverse_selection = quoted_spread - roll_spread. | //| roll_confidence = |correlation| of adjacent returns [0,1]. | //| On NQ M1 data, roll_spread approaches zero (see empirical study) | //| because M1 aggregation absorbs the bid-ask bounce signal. | //+------------------------------------------------------------------+ struct MicrostructureAnalysis { double quoted_spread; // Mean bar range as spread proxy (points) double roll_spread; // Roll-implied spread (points) double noise_ratio; // Enhanced OHLC noise ratio [0,1] double order_imbalance; // Volume-weighted directional pressure [-1,+1] double adverse_selection; // quoted_spread minus roll_spread (points) double roll_confidence; // |correlation| of adjacent returns [0,1] };BidAskSpread()
При вызове на текущем графике получает котируемый спред через функцию SymbolInfoDouble, а для исторических данных использует средний диапазон бара. Возвращает спред в пунктах. Результат проходит проверку корректности с порогом 10 000 пунктов, унаследованную от функции ValidateSymbolV2(). На исторических барах M1 без текущих данных Bid-Ask диапазон бара служит прокси с той же оговоркой, что приведена в заголовке структуры.
//+------------------------------------------------------------------+ //| BidAskSpread: quoted spread in points. | //| Live: SymbolInfoDouble ASK - BID, divided by point size. | //| Historical proxy: mean bar range over the window (points). | //| The historical proxy conflates spread widening with directional | //| intrabar movement but is consistent as a relative indicator. | //+------------------------------------------------------------------+ double BidAskSpread(const string symbol, const int tf, const int window) { if(!ValidateSymbolV2(symbol) || window < 2) return 0.0; double point = SymbolInfoDouble(symbol, SYMBOL_POINT); if(point <= DBL_MIN_POSITIVE) return 0.0; //--- Attempt live bid-ask first (works on current bar) double ask = SymbolInfoDouble(symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(symbol, SYMBOL_BID); if(ask > bid && bid > 0) { double spread = (ask - bid) / point; if(spread < 10000.0) return spread; } //--- Fallback: mean bar range as historical proxy double high[], low[]; ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); if(CopyHigh(symbol, (ENUM_TIMEFRAMES)tf, 0, window, high) < window) return 0.0; if(CopyLow (symbol, (ENUM_TIMEFRAMES)tf, 0, window, low) < window) return 0.0; double sum_range = 0.0; int valid = 0; for(int i = 0; i < window; i++) { double r = (high[i] - low[i]) / point; if(r > 0 && r < 10000.0) { sum_range += r; valid++; } } if(valid == 0) return 0.0; return sum_range / valid; }MicrostructureNoise()
Вычисляет отношение дисперсии разности между ценой закрытия и серединой диапазона к общей дисперсии бара — базовый коэффициент шума. Коэффициент безразмерен и сопоставим между разными инструментами и уровнями цен. Защита знаменателя обеспечивается константой DBL_MIN_POSITIVE.
//+------------------------------------------------------------------+ //| MicrostructureNoise: close-to-midpoint variance fraction. | //| Returns noise_basic in [0,1]. Higher = more noise. | //| Midpoint = (High + Low) / 2. Measures how far Close deviates | //| from midpoint relative to total bar range variance. | //+------------------------------------------------------------------+ double MicrostructureNoise(const string symbol, const int tf, const int window) { if(!ValidateSymbolV2(symbol) || window < 3) return 0.0; double high[], low[], close[]; ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); if(CopyHigh (symbol,(ENUM_TIMEFRAMES)tf,0,window,high) < window) return 0.0; if(CopyLow (symbol,(ENUM_TIMEFRAMES)tf,0,window,low) < window) return 0.0; if(SafeCopyClose(symbol,tf,0,window,close) < window) return 0.0; double sum_diff = 0.0, sum_rng = 0.0; double diff_vals[], rng_vals[]; ArrayResize(diff_vals, window); ArrayResize(rng_vals, window); int valid = 0; for(int i = 0; i < window; i++) { double mid = (high[i] + low[i]) / 2.0; if(mid <= DBL_MIN_POSITIVE) continue; diff_vals[valid] = (close[i] - mid) / mid; rng_vals [valid] = (high[i] - low[i]) / mid; valid++; } if(valid < 3) return 0.0; //--- Two-pass variance for numerical stability double mean_d = 0.0, mean_r = 0.0; for(int i=0;i<valid;i++){mean_d+=diff_vals[i];mean_r+=rng_vals[i];} mean_d/=valid; mean_r/=valid; double var_d = 0.0, var_r = 0.0; for(int i=0;i<valid;i++) { var_d+=(diff_vals[i]-mean_d)*(diff_vals[i]-mean_d); var_r+=(rng_vals[i] -mean_r)*(rng_vals[i] -mean_r); } var_d/=valid; var_r/=valid; if(var_r <= DBL_MIN_POSITIVE) return 0.0; return MathMin(1.0, var_d / (var_r + DBL_MIN_POSITIVE)); }EnhancedMicrostructureNoise()
Трехкомпонентный коэффициент шума на основе OHLC: дисперсия разности цены закрытия и середины диапазона (40%), единица минус отношение тела бара к диапазону (40%) и доля дисперсии изменений от открытия до закрытия (20%). Перед объединением каждый из трех компонентов проходит отдельную защитную проверку. Записывает результат в RobustFractalAnalysis.microstructure_noise.
//+------------------------------------------------------------------+ //| EnhancedMicrostructureNoise: three-component OHLC noise ratio. | //| Component 1 (40%): close-to-midpoint variance fraction. | //| Component 2 (40%): 1 - body_to_range (Garman-Klass efficiency). | //| Component 3 (20%): OC variance as fraction of range variance. | //| Returns noise in [0,1]. Writes to rfa.microstructure_noise. | //+------------------------------------------------------------------+ double EnhancedMicrostructureNoise(const string symbol, const int tf, const int window, RobustFractalAnalysis &rfa) { if(!ValidateSymbolV2(symbol) || window < 3) return 0.0; double open[], high[], low[], close[]; ArraySetAsSeries(open,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); if(CopyOpen (symbol,(ENUM_TIMEFRAMES)tf,0,window,open) < window) return 0.0; if(CopyHigh (symbol,(ENUM_TIMEFRAMES)tf,0,window,high) < window) return 0.0; if(CopyLow (symbol,(ENUM_TIMEFRAMES)tf,0,window,low) < window) return 0.0; if(SafeCopyClose(symbol,tf,0,window,close) < window) return 0.0; double diff_v[],rng_v[],btr_v[],oc_v[]; ArrayResize(diff_v,window); ArrayResize(rng_v,window); ArrayResize(btr_v,window); ArrayResize(oc_v,window); int valid = 0; for(int i = 0; i < window; i++) { double br = high[i] - low[i]; double mid = (high[i] + low[i]) / 2.0; if(mid <= DBL_MIN_POSITIVE || br <= DBL_MIN_POSITIVE) continue; diff_v[valid] = (close[i] - mid) / mid; rng_v [valid] = br / mid; btr_v [valid] = MathAbs(close[i] - open[i]) / br; oc_v [valid] = (close[i] - open[i]) / mid; valid++; } if(valid < 3) return 0.0; //--- Two-pass variance for each component double mean_d=0,mean_r=0,mean_oc=0,mean_btr=0; for(int i=0;i<valid;i++){mean_d+=diff_v[i];mean_r+=rng_v[i]; mean_oc+=oc_v[i]; mean_btr+=btr_v[i];} mean_d/=valid; mean_r/=valid; mean_oc/=valid; mean_btr/=valid; double var_d=0,var_r=0,var_oc=0; for(int i=0;i<valid;i++) { var_d +=(diff_v[i]-mean_d) *(diff_v[i]-mean_d); var_r +=(rng_v[i] -mean_r) *(rng_v[i] -mean_r); var_oc+=(oc_v[i] -mean_oc)*(oc_v[i] -mean_oc); } var_d/=valid; var_r/=valid; var_oc/=valid; //--- Component 1: close-to-midpoint fraction double c1 = (var_r > DBL_MIN_POSITIVE) ? MathMin(1.0, var_d/(var_r+DBL_MIN_POSITIVE)) : 0.0; //--- Component 2: inverted body-to-range (Garman-Klass efficiency) double c2 = MathMax(0.0, 1.0 - mean_btr); //--- Component 3: OC variance as fraction of range variance double c3 = (var_r > DBL_MIN_POSITIVE) ? MathMin(1.0, var_oc/(var_r+DBL_MIN_POSITIVE)) : 0.0; double noise = MathMax(0.0, MathMin(1.0, 0.40*c1 + 0.40*c2 + 0.20*c3)); //--- Write to shared struct field reserved in Part 1 rfa.microstructure_noise = noise; if(!MathIsValidNumber(rfa.microstructure_noise)) rfa.microstructure_noise = 0.0; return noise; }BidAskBounceEffect() — спред Ролла
Вычисляет спред по модели Ролла на основе ковариации соседних логарифмических доходностей между ценами закрытия. Использует двухпроходный расчет ковариации, согласованный с реализацией первой части. Нижняя граница на уровне нуля предотвращает комплексные результаты при положительной ковариации. Возвращает расчетный спред в пунктах. Также возвращает показатель достоверности, равный модулю корреляции двух рядов соседних доходностей. Значения ниже 0,0083 — 10-го процентиля в исследовании NQ — указывают на ненадежность оценки.
//+------------------------------------------------------------------+ //| BidAskBounceEffect: Roll-implied spread. | //| spread = 2 * sqrt(max(0, -Cov(Δp_t, Δp_{t-1}))) | //| confidence = |correlation| of adjacent returns [0,1]. | //| | //| On NQ M1, this returns values near zero because intrabar price | //| discovery absorbs the bid-ask bounce signal at 1-min aggregation.| //| The function is retained for sub-minute timeframes and for | //| instruments where the bounce is detectable. | //+------------------------------------------------------------------+ double BidAskBounceEffect(const string symbol, const int tf, const int window, double &confidence) { confidence = 0.0; if(!ValidateSymbolV2(symbol) || window < 5) return 0.0; double close[]; if(SafeCopyClose(symbol, tf, 0, window + 1, close) < window + 1) return 0.0; double point = SymbolInfoDouble(symbol, SYMBOL_POINT); if(point <= DBL_MIN_POSITIVE) return 0.0; //--- Build log-return array; filter gross outliers int n = ArraySize(close) - 1; double ret[]; ArrayResize(ret, n); int valid = 0; for(int i = 0; i < n; i++) { if(close[i] <= 0 || close[i+1] <= 0) continue; double r = SafeLog(close[i+1]) - SafeLog(close[i]); if(MathAbs(r) < 0.1) ret[valid++] = r; } if(valid < 4) return 0.0; ArrayResize(ret, valid); //--- Two-pass covariance of adjacent returns double mean1 = 0.0, mean2 = 0.0; int pairs = valid - 1; for(int i = 0; i < pairs; i++) { mean1 += ret[i]; mean2 += ret[i+1]; } mean1 /= pairs; mean2 /= pairs; double cov = 0.0, var1 = 0.0, var2 = 0.0; for(int i = 0; i < pairs; i++) { cov += (ret[i]-mean1) * (ret[i+1]-mean2); var1 += (ret[i]-mean1) * (ret[i] -mean1); var2 += (ret[i+1]-mean2) * (ret[i+1]-mean2); } cov /= pairs; var1 /= pairs; var2 /= pairs; //--- Confidence: absolute correlation of the two series double denom_conf = MathSqrt(MathMax(0,var1) * MathMax(0,var2)); if(denom_conf > DBL_MIN_POSITIVE) confidence = MathMin(1.0, MathAbs(cov / denom_conf)); //--- Roll spread: floor at zero when covariance is positive if(cov >= 0) return 0.0; double spread_price = 2.0 * MathSqrt(-cov); return MathMax(0.0, spread_price / point); }OrderImbalance()
Взвешенная по объему прокси для направленного давления. Для каждого бара (Close - Low)/(High - Low) показывает относительное положение цены закрытия внутри диапазона. Взвешенное по объему среднее, центрированное относительно нуля, дает дисбаланс ордеров в диапазоне [-1, +1]. Бары с нулевым диапазоном исключаются.
//+------------------------------------------------------------------+ //| OrderImbalance: volume-weighted directional pressure. | //| Returns (Close-Low)/(High-Low) centered at zero, volume-weighted.| //| +1 = all closes at high (buyer dominated). | //| -1 = all closes at low (seller dominated). | //| Bars with High == Low excluded (zero range = undefined signal). | //+------------------------------------------------------------------+ double OrderImbalance(const string symbol, const int tf, const int window) { if(!ValidateSymbolV2(symbol) || window < 3) return 0.0; double high[], low[], close[]; long vol[]; ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(vol,true); if(CopyHigh (symbol,(ENUM_TIMEFRAMES)tf,0,window,high) < window) return 0.0; if(CopyLow (symbol,(ENUM_TIMEFRAMES)tf,0,window,low) < window) return 0.0; if(SafeCopyClose(symbol,tf,0,window,close) < window) return 0.0; if(CopyTickVolume(symbol,(ENUM_TIMEFRAMES)tf,0,window,vol) < window) return 0.0; double weighted_sum = 0.0; double total_vol = 0.0; for(int i = 0; i < window; i++) { double br = high[i] - low[i]; if(br <= DBL_MIN_POSITIVE) continue; // exclude zero-range bars double frac = (close[i] - low[i]) / br; // [0,1] double imb = 2.0 * frac - 1.0; // center at zero [-1,+1] double v = (vol[i] > 0) ? (double)vol[i] : 1.0; weighted_sum += imb * v; total_vol += v; } if(total_vol <= DBL_MIN_POSITIVE) return 0.0; return MathMax(-1.0, MathMin(1.0, weighted_sum / total_vol)); }PopulateMicrostructureAnalysis()
Функция-обертка вызывает все пять функций, заполняет структуру MicrostructureAnalysis, вычисляет adverse_selection = quoted_spread - roll_spread и записывает microstructure_noise в RobustFractalAnalysis. Применяет ту же схему защиты от NaN, что и функция PopulateVolatilityAnalysis() из четвертой части.
//+------------------------------------------------------------------+ //| PopulateMicrostructureAnalysis: fills MicrostructureAnalysis and | //| writes microstructure_noise into RobustFractalAnalysis. | //| adverse_selection = quoted_spread - roll_spread. | //| On NQ M1, roll_spread ≈ 0 so adverse_selection ≈ quoted_spread. | //+------------------------------------------------------------------+ void PopulateMicrostructureAnalysis(const string symbol, const int tf, const int window, MicrostructureAnalysis &msa, RobustFractalAnalysis &rfa) { msa.quoted_spread = BidAskSpread(symbol, tf, window); msa.noise_ratio = EnhancedMicrostructureNoise(symbol, tf, window, rfa); msa.roll_spread = BidAskBounceEffect(symbol, tf, window, msa.roll_confidence); msa.order_imbalance = OrderImbalance(symbol, tf, window); msa.adverse_selection = MathMax(0.0, msa.quoted_spread - msa.roll_spread); //--- NaN guards if(!MathIsValidNumber(msa.quoted_spread)) msa.quoted_spread = 0.0; if(!MathIsValidNumber(msa.roll_spread)) msa.roll_spread = 0.0; if(!MathIsValidNumber(msa.noise_ratio)) msa.noise_ratio = 0.0; if(!MathIsValidNumber(msa.order_imbalance)) msa.order_imbalance = 0.0; if(!MathIsValidNumber(msa.adverse_selection)) msa.adverse_selection = 0.0; if(!MathIsValidNumber(msa.roll_confidence)) msa.roll_confidence = 0.0; }
Эмпирическое исследование — микроструктурный шум NQ на таймфрейме M1
Все оценщики применены к данным 602 нью-йоркских сессий фьючерса NQ E-mini Nasdaq 100 на CME Globex с января 2024 года по июнь 2026 года. Фильтр нью-йоркской сессии сохраняет бары со временем открытия в интервале от 14:30 до 21:00 UTC. Сессии с менее чем 300 барами на таймфрейме M1 исключаются. По сравнению с исследованием четвертой части выборка расширена на 88 сессий. Она охватывает более длительный обычный период и весь период тарифного шока апреля 2025 года. Все показатели заново рассчитываются по исходным барам OHLCV с помощью Python-реализации приведенных выше функций.
Рис. 1. Улучшенный коэффициент шума (вверху) и прокси для котируемого спреда (внизу) по 602 сессиям NQ, январь 2024 г. - июнь 2026 г. Фоновая закраска показывает четыре выделенных стрессовых эпизода. Во время тарифного шока апреля 2025 года котируемый спред увеличивается сильнее всего — в 2,6 раза относительно обычного уровня. В момент шока по Банку Японии коэффициент шума достигает максимума.
Таблица 1. Сводная статистика (602 нью-йоркские сессии, фьючерсы NQ, таймфрейм M1)
| Показатель | Среднее значение | Стандартное отклонение | Мин. | 25% | Медиана | 75% | Макс. |
|---|---|---|---|---|---|---|---|
| Прокси для котируемого спреда (пункты) | 13,82 | 6,47 | 5,34 | 9,51 | 12,41 | 16,34 | 74,18 |
| Расчетный спред Ролла (пункты) | 0,0001 | 0,0001 | 0,0000 | 0,0000 | 0,0000 | 0,0002 | 0,0013 |
| Достоверность оценки Ролла [0,1] | 0,060 | 0,046 | 0,000 | 0,025 | 0,050 | 0,086 | 0,248 |
| Коэффициент шума (базовый) | 0,466 | 0,132 | 0,202 | 0,372 | 0,458 | 0,541 | 1,000 |
| Отношение тела бара к диапазону | 0,453 | 0,019 | 0,396 | 0,440 | 0,452 | 0,466 | 0,513 |
| Коэффициент шума (улучшенный) | 0,604 | 0,053 | 0,438 | 0,570 | 0,602 | 0,632 | 0,815 |
| Дисбаланс ордеров [-1, +1] | 0,021 | 0,056 | -0,185 | -0,016 | 0,024 | 0,061 | 0,158 |
| Неблагоприятный отбор (пункты) | 13,82 | 6,47 | 5,34 | 9,51 | 12,41 | 16,34 | 74,18 |
| Реализованная волатильность (на бар) | 0,00042 | 0,00018 | 0,00013 | 0,00029 | 0,00038 | 0,00050 | 0,00310 |
Строку расчетного спреда Ролла нужно сразу пояснить. Близкие к нулю значения — в среднем 0,0001 пункта при минимальном шаге цены 0,25 пункта — подтверждают, что колебания цены между Bid и Ask не выявляются при минутной агрегации данных фьючерсов NQ. Средний показатель достоверности оценки Ролла составляет 0,060 — значительно ниже порога 0,65, использованного для MFDFA в четвертой части. Это ожидаемый результат для рынка, где каждый бар объединяет сотни сделок: ковариация соседних доходностей между ценами закрытия определяется прежде всего формированием рыночной цены внутри бара, а не чередованием сделок по Bid и Ask. Функция сохранена для полноты инструментария и для работы с данными короче минуты либо инструментами с более широкими спредами. Значения неблагоприятного отбора численно совпадают с прокси для котируемого спреда, поскольку спред Ролла ≈ 0; для прозрачности показаны обе строки.
Среднее значение улучшенного коэффициента шума — 0,604, а стандартное отклонение заметно мало — 0,053. По разложению OHLC примерно 60% вариации цены в типичной сессии NQ на таймфрейме M1 приходится на шум. Малый разброс показывает, что шум — устойчивая структурная особенность инструмента на этом таймфрейме, а не отдельное эпизодическое явление.
Рис. 2. Слева — улучшенный коэффициент шума в сопоставлении с реализованной волатильностью на бар по 602 сессиям (r = 0,159, p < 0,001); режимы обозначены цветом. Шум и волатильность — разные показатели. Справа — внутридневной профиль шума (синий, левая ось) и диапазон бара (красный, правая ось) по минутам после открытия нью-йоркской сессии. В течение сессии диапазон бара резко сокращается, а шум немного увеличивается.
Перед таблицей режимов выделим два вывода из анализа корреляций показателей и сопоставления режимов.
Во-первых, шум и волатильность — разные величины. Корреляция улучшенного коэффициента шума с реализованной волатильностью по 602 сессиям составляет лишь 0,159 (p < 0,001). Эти два показателя дают разную информацию, поэтому их стоит рассчитывать отдельно. Корреляция прокси для котируемого спреда с реализованной волатильностью равна 0,959. Причина в том, что на данных M1 сама прокси фактически измеряет волатильность, а не в тождестве понятий спреда и волатильности. Как и ожидалось, истинный спред, то есть оценка по модели Ролла, почти не коррелирует с волатильностью.
Во-вторых, внутридневной шум немного растет от открытия к закрытию — с 0,532 до 0,551, — тогда как диапазон бара резко сокращается с 26,1 до 11,5 пункта. Это противоречит прогнозу простой гипотезы об U-образном профиле ликвидности. Это означает, что к концу сессии диапазон баров сужается, но направленность движения не усиливается пропорционально: в абсолютном выражении колебания меньше, однако сигнал не становится "чище". Это может согласовываться с закрытием позиций к концу сессии, вызывающим небольшие движения с возвратом к среднему, хотя объема выборки недостаточно для установления причинной связи.
Таблица 2. Сравнение режимов (602 сессии)
| Режим | Сессии | Коэффициент шума | Относительно нормального режима | Прокси для спреда (пункты) | Относительно нормального режима | Дисбаланс ордеров | Реализованная волатильность |
|---|---|---|---|---|---|---|---|
| Нормальный | 556 | 0,603 | — | 13,14 | — | 0,021 | 0,00040 |
| Шок Банка Японии (август 2024 г.) | 8 | 0,677 | +12% | 23,16 | 1,76× | 0,010 | 0,00082 |
| Решение ФРС (декабрь 2024 г.) | 6 | 0,593 | -2% | 15,55 | 1,18× | 0,028 | 0,00054 |
| Тарифный шок, апрель 2025 г. | 14 | 0,645 | +7% | 34,12 | 2,60× | 0,008 | 0,00119 |
| Тарифный шок, апрель 2026 г. | 18 | 0,564 | -6% | 14,23 | 1,08× | 0,031 | 0,00040 |
Сравнение режимов дает три вывода, согласующиеся с анализом волатильности в четвертой части. Сворачивание керри-трейда на фоне шока Банка Японии в августе 2024 года сопровождалось максимальным шумом — 0,677, на 12% выше обычного уровня. Это указывает на сильные кратковременные колебания цены, а не на чистое направленное движение при экстремальной волатильности. Во время тарифного шока апреля 2025 года прокси для котируемого спреда максимальна — 34,1 пункта, в 2,6 раза выше обычного уровня. Шум повышен, но не экстремален — 0,645. В обоих стрессовых эпизодах дисбаланс ордеров приближается к нулю — 0,010 и 0,008 соответственно. Это согласуется с выводом четвертой части: при остром стрессе поток двусторонний, без явного преобладания одного направления. Во время тарифного эпизода апреля 2026 года шум и спред ниже обычного уровня или близки к нему. Это вновь согласуется с выводом, полученным при сопоставлении работ: к 2026 году рынки, возможно, в значительной степени адаптировались к тарифному режиму.
Рис. 3. Слева — распределение среднего за сессию дисбаланса ордеров по 602 сессиям. Распределение центрировано чуть выше нуля — среднее 0,021. Это указывает на слабое систематическое преобладание покупателей по NQ за период выборки. Справа — улучшенный коэффициент шума по режимам. Шок Банка Японии — явный выброс; тарифный эпизод апреля 2026 года — самый спокойный.
Ограничения
Реализованные в этой статье функции работают с минутными барами OHLCV. Это существенное ограничение, которое нужно понимать до практического применения показателей.
Полноценный анализ рыночной микроструктуры проводится на уровне тиков. Стандартные показатели — эффективный спред, ценовое воздействие, издержки неблагоприятного отбора и VPIN — определяются по отдельным сделкам: объему каждой сделки с учетом направления, точным Bid и Ask в момент ее совершения и реакции цены в ближайших следующих барах. На тиковом уровне спред Ролла оценивается по фактическим ценам сделок, а не по ценам закрытия баров; дисбаланс ордеров отражает реальное направление агрессивной сделки, а не прокси на уровне бара.
Исследование данных NQ непосредственно это подтверждает. Расчетный спред Ролла близок к нулю во всех 602 сессиях: среднее 0,0001 пункта, максимум 0,0013 пункта при минимальном шаге цены 0,25 пункта. При минутной агрегации это корректный результат: формирование рыночной цены внутри бара, объединяющего сотни сделок, полностью поглощает ковариацию соседних доходностей, необходимую модели Ролла для выявления колебаний цены между Bid и Ask. Функция дает содержательный сигнал на таймфреймах короче минуты и инструментах с более широкими эффективными спредами, но не на NQ M1. При применении функции BidAskBounceEffect() к данным NQ M1 считайте результат приблизительно нулевым независимо от вычисленного значения. Функция включена для полноты инструментария и согласованности между таймфреймами. Она предназначена для баров короче минуты или инструментов, где можно обнаружить колебания цены между Bid и Ask. На NQ M1 не используйте ее для торговых решений, если roll_confidence не превышает содержательного порога для выбранного инструмента.
Приближения на основе OHLCV также вносят определенное смещение в оценку дисбаланса ордеров. Взвешенная по объему прокси на основе (Close - Low)/(High - Low) корректно выявляет бары с выраженным направлением при закрытии, но ошибается, если цена в течение минуты двигалась в одном направлении, затем развернулась и закрылась около середины диапазона, несмотря на преобладание покупателей или продавцов за сессию.
Теоретически корректный подход — до расчета показателей объединять тиковые данные в бакеты по объему или денежной стоимости сделок. Эта методология лежит в основе VPIN (Изли и соавт., 2012) и исследований Лопеса де Прадо (2018) по построению баров: каждый бакет содержит фиксированный объем рыночной активности, а не охватывает фиксированный промежуток времени. Ньороге (2026) описывает в статьях сообщества MQL5 практическую реализацию на языке MQL5 баров активности и дисбаланса — прямую альтернативу используемым здесь приближениям на временных барах. Агрегация по бакетам полностью устраняет смещение OHLCV: каждая сделка участвует в оценках спреда и дисбаланса, а полученные показатели стационарны по рыночной активности, а не по времени на часах.
Реализации на минутных OHLCV-барах подходят для индикаторов на языке MQL5 без доступа к истории тиков, работающих в реальном времени, и для инструментов, по которым тиковые данные недоступны или слишком объемны. Они дают корректные по направлению сигналы на уровне сессии даже тогда, когда разложение Ролла не срабатывает. Это согласуется с остальной серией, во всех частях которой используются данные M1. В шестой части появится функция VPIN_OHLC() — наиболее близкое из доступных приближений к тиковому VPIN без данных по отдельным сделкам. На нее распространяется то же ограничение; для ясности мы повторим его и там.
Практические пороги
Все приведенные ниже пороги получены из эмпирического распределения по 602 сессиям NQ на таймфрейме M1 с января 2024 года по июнь 2026 года. Это эвристические рабочие пороги по эмпирическим квантилям, а не формальные критические значения статистических тестов. Они относятся именно к NQ на таймфрейме M1 и этому периоду выборки. Рассматривайте их как отправную точку и заново определяйте для своего инструмента, таймфрейма и периода данных.
| Показатель | Пороговое значение | Основание | Интерпретация | Действие |
|---|---|---|---|---|
| Коэффициент шума (улучшенный) | > 0,63 | 75-й процентиль | Повышенный шум; оценки волатильности менее достоверны | Увеличьте расстояние до стоп-лосса; снизьте вес индекса кластеризации из четвертой части |
| Коэффициент шума (улучшенный) | < 0,57 | 25-й процентиль | Формирование рыночной цены с меньшим шумом, чем обычно | Оценки волатильности и показателя Херста более достоверны |
| Прокси для котируемого спреда | > 16,3 пункта | 75-й процентиль | Повышенные издержки исполнения | Увеличьте расстояние до стоп-лосса; уменьшите размер позиции |
| Прокси для котируемого спреда | > 20,8 пункта | 90-й процентиль | Расширение спреда до стрессового уровня | Существенно уменьшите размер позиции; проверьте корректность потока котировок |
| Достоверность оценки Ролла | < 0,008 | 10-й процентиль | Оценка Ролла недостоверна (типично для NQ на таймфрейме M1) | Считайте значение roll_spread недоступным |
| Дисбаланс ордеров | > 0,09 | 90-й процентиль | Сильное давление покупателей | Рассмотрите возможность торговли в направлении дисбаланса |
| Дисбаланс ордеров | < -0,06 | 10-й процентиль | Сильное давление продавцов | Рассмотрите возможность торговли в направлении дисбаланса |
//--- Example: Part 5 metrics gating the Part 4 volatility signal RobustFractalAnalysis rfa; MicrostructureAnalysis msa; VolatilityAnalysis va; PopulateHurstAnalysis(Symbol(), PERIOD_M1, 90, rfa); PopulateVolatilityAnalysis(Symbol(), PERIOD_M1, 90, va, rfa); PopulateMicrostructureAnalysis(Symbol(), PERIOD_M1, 90, msa, rfa); //--- Select volatility estimate; discount when noise is elevated double current_vol = va.realized_vol; if(rfa.multifractal_width > 0.99 && rfa.dimension_confidence > 0.65) current_vol = va.figarch_vol; else if(va.clustering_index > 0.20) current_vol = va.fractional_vol; //--- Gate clustering signal on noise quality bool noise_clean = (msa.noise_ratio < 0.63); double target_vol = 0.0004; // calibrated to NQ M1 mean realized vol double base_pos = (current_vol > DBL_MIN_POSITIVE) ? target_vol / current_vol : 1.0; //--- Scale down for spread elevation if(msa.quoted_spread > 20.8) base_pos *= 0.5; else if(msa.quoted_spread > 16.3) base_pos *= 0.75; //--- Scale down for jump activity (Part 4) if(va.jump_intensity > 0.021) base_pos *= MathMax(0.3, 1.0 - va.jump_intensity * 10.0); //--- Align with order imbalance if strong and noise is clean double oi_signal = 0.0; if(noise_clean && MathAbs(msa.order_imbalance) > 0.09) oi_signal = MathSign(msa.order_imbalance);
Структура папок и путь к подключаемому файлу
Все функции пятой части добавлены в существующий заголовочный файл базового слоя. Новые файлы не создаются. Структура папок не изменилась по сравнению с частями 1-4:
MQL5\
└── Indicators\
└── HurstProfile\
├── HurstProfile.mq5
└── Includes\
└── MicroStructure_Foundation.mqh #include "Includes\MicroStructure_Foundation.mqh"
Заключение
В этой статье мы добавили в файл MicroStructure_Foundation.mqh пять функций анализа микроструктурного шума и ввели структуру MicrostructureAnalysis. Функция BidAskSpread() возвращает прокси для котируемого спреда. Функция BidAskBounceEffect() вычисляет расчетный спред Ролла. Исследование показывает, что на NQ M1 она возвращает значения около нуля, подтверждая необходимость тиковых данных для выявления колебаний цены между Bid и Ask на этом инструменте и таймфрейме. Функция EnhancedMicrostructureNoise() возвращает трехкомпонентный коэффициент шума на основе OHLC, информативный и устойчивый между сессиями. Функция OrderImbalance() измеряет направленное давление на уровне бара. Функция PopulateMicrostructureAnalysis() связывает все пять показателей с оценками волатильности из четвертой части и заполняет поле microstructure_noise, предусмотренное еще в первой части.
Исследование 602 сессий NQ на таймфрейме M1 показывает: по разложению OHLC примерно 60% вариации цены приходится на шум, причем разброс между сессиями мал (SD = 0,053). При стрессовых эпизодах шум повышается: на фоне шока Банка Японии его коэффициент достигает 0,677 — на 12% выше обычного уровня. Однако шум остается отдельной от волатильности характеристикой (r = 0,159). В среднем дисбаланс ордеров близок к нулю и еще больше снижается при остром стрессе, что согласуется с выводом четвертой части о двустороннем потоке. Во время тарифного эпизода апреля 2026 года шум и спред находятся на обычном уровне или ниже него. Это вновь согласуется с полученными в разных работах свидетельствами адаптации рынка.
Как отмечено в разделе "Ограничения", разложение Ролла частично не работает на данных фьючерсов NQ с таймфреймом M1. Это корректный эмпирический результат, а не недостаток реализации. Коэффициент шума, прокси для котируемого спреда и дисбаланс ордеров остаются информативными и пригодными для использования. В шестой части все пять показателей станут входными данными для сигнала потока ордеров вместе с оценками волатильности из четвертой части.
Как получить исходный код через MQL5 Algo Forge
Algo Forge предоставляет облачный контроль версий на базе Git, поэтому вам будет доступна актуальная версия кода, в том числе обновления и исправления после публикации статьи. Полный репозиторий доступен в MQL5 Algo Forge.
- Браун, М. (2026a). Рыночная микроструктура в MQL5 (Часть 1): Надежный базовый слой. Статьи сообщества MQL5.
- Браун, М. (2026b). Рыночная микроструктура в MQL5 (Часть 2): Измерение долгой памяти в MQL5 с помощью оценщиков Херста. Статьи сообщества MQL5.
- Браун, М. (2026c). Рыночная микроструктура в MQL5 (Часть 3): Оценка параметра d модели ARFIMA методом GPH. Статьи сообщества MQL5.
- Браун, М. (2026d). Рыночная микроструктура в MQL5 (Часть 4): Волатильность с эффектом памяти. Статьи сообщества MQL5.
- Brown, Max (2026). Intraday Microstructure Dynamics of E-mini S&P 500 Futures: Volatility Regimes, Liquidity Decay, and Long Memory in Realized Volatility (May 29, 2026). Available at SSRN: https://ssrn.com/abstract=6847024.
- Easley, D., López de Prado, M.M. & O'Hara, M. (2012). Flow toxicity and liquidity in a high-frequency world. Review of Financial Studies, 25(5), 1457–1493.
- Engle, R.F. (2000). The econometrics of ultra-high-frequency data. Econometrica, 68(1), 1–22.
- Garman, M.B. & Klass, M.J. (1980). On the estimation of security price volatilities from historical data. Journal of Business, 53(1), 67–78.
- Hasbrouck, J. (1993). Assessing the quality of a security market. Review of Financial Studies, 6(3), 405–434.
- Kyle, A.S. (1985). Continuous auctions and insider trading. Econometrica, 53(6), 1315–1335.
- Parkinson, M. (1980). The extreme value method for estimating the variance of the rate of return. Journal of Business, 53(1), 61–65.
- Njoroge, P.M. (2026). Beyond the Clock (Part 1): Building Activity and Imbalance Bars in Python and MQL5. Статьи сообщества MQL5.
- Roll, R. (1984). A simple implicit measure of the effective bid-ask spread in an efficient market. Journal of Finance, 39(4), 1127–1139.
- Brown, Max (2026). Measuring the Memory Structure of Intraday Returns: Evidence from E-mini S&P 500 Futures (April 01, 2026). Доступно на SSRN: https://ssrn.com/abstract=6809080 или http://dx.doi.org/10.2139/ssrn.6809080.
- López de Prado, M.M. (2018). Advances in Financial Machine Learning. Wiley.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22938
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
За пределами GARCH (Часть VI): Фракционное броуновское движение и мультипликативный каскад в MQL5
Нейронная сеть на практике: Когда использовать искусственный нейрон и как он работает в MQL5
Создание индикатора профиля объёма "Спектр ликвидности" (Liquidity Spectrum) на языке MQL5
За пределами GARCH (Часть V): Подгонка мультифрактального спектра в MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования


