Неопределённость как модель (Часть 8): Обобщение МНК
Оглавление
- Введение
- Принципы обобщения МНК
- Отклонения от условий МНК
- Модификации МНК
- Оценка Ньюи—Уэста
- Заключение
- Список приложенных файлов
Введение
Мы продолжаем развивать тему преодоления уязвимостей линейных моделей при работе со стохастическими регрессорами. На очереди — гетероскедастичность (непостоянство дисперсии ошибок) и автокорреляция остатков (зависимость текущих ошибок от прошлых). Эти эффекты грубо нарушают базовые предпосылки, делая стандартные оценки обычного МНК неэффективными. Для трейдера это не просто теоретический дефект, а критическая уязвимость с фатальными последствиями для торгового депозита:
- Искажение t-статистик: стандартные ошибки коэффициентов оказываются смещёнными (обычно заниженными).
- Ложные сигналы: заниженные ошибки приводят к искусственному завышению t-статистик и занижению p-value. В результате незначимые факторы кажутся значимыми.
- Ошибка в стационарности: искажённая оценка параметров может подтолкнуть к ложному выводу о стационарности или коинтеграции активов. Например, мы рискуем открыть арбитражную позицию на спреде, который на самом деле хаотично расходится.
Принципы обобщения МНК
Поскольку классический МНК в исходном виде здесь не работает, нам придётся либо модифицировать его, либо использовать альтернативные методы. Внедряя изменения, мы будем придерживаться нескольких правил, которые проистекают не столько из сухой теории, сколько из инженерного здравого смысла:
- Необходимость изменений. Не стоит цепляться за простоту привычного алгоритма, если его применение на реальных рыночных данных ведёт к неверным статистическим выводам и, как следствие, к ошибкам в торговле. Если предпосылки нарушены — алгоритм нужно менять.
- Принцип достаточности. Из всех подходящих модификаций мы выбираем самую простую и практичную. Нет смысла усложнять код и перегружать процессор "модными" тяжёлыми методами, если для решения задачи достаточно базовой корректировки. Наш приоритет — минимизация усилий при сохранении стабильного результата.
- Осознанность применения. Любое изменение алгоритма опирается на конкретные теоретические предположения. Каждую модификацию МНК нужно внедрять осознанно, понимая её внутреннюю логику и по возможности проверяя выполнение её условий на практике.
Отклонения от условий МНК
Как и было заявлено, мы будем иметь дело с двумя отклонениями: гетероскедастичностью и автокорреляцией ошибок модели. Рассмотрим их подробнее.
Гетероскедастичность
Мы уже упоминали это нарушение классических предпосылок в статьях о парной и множественной регрессии. Напомним: гетероскедастичность — это изменчивость (непостоянство) дисперсии случайной ошибки модели. В контексте финансовых временных рядов полезно разделять два её основных типа:
- Зависимость от регрессоров. Дисперсия ошибки меняется в зависимости от значений объясняющих переменных. Для первичного визуального анализа (EDA) используют график рассеяния остатков против прогнозных значений целевой переменной. Для строгого подтверждения (CDA) обычно применяют тест Бройша—Пагана, который мы подробно разобрали ранее. Именно этот тип гетероскедастичности станет главным предметом текущей статьи.
- Временная (динамическая) гетероскедастичность. Здесь природа изменчивости дисперсии связана исключительно с фактором времени или сменой рыночных состояний. Для её первичного визуального анализа (EDA) используют простой график зависимости остатков от времени. Саму же временную гетероскедастичность можно разделить на два вида:
- Детерминированная: дисперсия зависит напрямую от времени (например, плавный долгосрочный рост волатильности, сезонные колебания или резкий скачок — структурный разрыв). Для обнаружения скачка волатильности отлично подходит изученный нами тест Петтитта на квадратах остатков.
- Стохастическая: дисперсия меняется случайно под воздействием внешних факторов или скрытых процессов. Хрестоматийный пример — смена рыночных режимов (модели HMM / Markov Switching), когда рынок хаотично переходит из спокойного состояния в фазу высокой турбулентности.
Временная гетероскедастичность, а также модели смены режимов и стохастической волатильности останутся за рамками данной статьи.
Автокорреляция
Это отклонение от классических предпосылок МНК мы также определили в предыдущих статьях серии. Напомним: автокорреляция означает наличие взаимосвязи между случайными ошибками модели в разных наблюдениях (чаще всего в соседних по времени). Для первичного визуального анализа (EDA) идеально подходит коррелограмма остатков. В качестве строгого подтверждения (CDA) обычно используют тест Бройша—Годфри, либо адаптированный для остатков тест Льюнга—Бокса. Существует два классических способа математического описания автокорреляции остатков:
- Авторегрессионный процесс (Autoregressive, AR). В этом случае текущая ошибка модели линейно зависит от своих собственных прошлых значений (лагов) и нового случайного шока.
- Процесс скользящего среднего (Moving Average, MA). Здесь текущая ошибка модели формируется как взвешенная сумма (скользящее среднее) текущего и прошлых значений независимого белого шума.
Более основательно эти процессы, а также их комбинации (ARMA и ARIMA), мы изучим в отдельной статье. Сейчас для нас важно то, что наличие таких процессов в остатках принципиально меняет подход: обычный МНК (OLS) в явном виде для них уже не подходит, и нам придётся использовать совершенно другие методы расчёта коэффициентов и их стандартных ошибок.
Модификации МНК
В данной статье мы разберём два принципиально разных подхода, которые позволяют справиться с описанными выше отклонениями:
- Оценка Ньюи—Уэста (Newey–West estimator). Этот метод применяется, когда структура самой модели нас устраивает, но из-за гетероскедастичности и автокорреляции остатков искажены стандартные ошибки. Метод Ньюи—Уэста оставляет коэффициенты ОМНК неизменными, но пересчитывает их стандартные ошибки так, чтобы они стали устойчивыми (робастными) к этим нарушениям.
- Условный метод наименьших квадратов (CLS, Conditional Least Squares). Этот подход мы задействуем, когда в модели обнаруживается структура ARMA и классический МНК становится неприменим для расчёта коэффициентов. Для поиска параметров в таком случае мы реализуем алгоритм Гаусса—Ньютона.
Далее мы подробно опишем каждый метод — от математической теории до практической реализации в коде на MQL5.
Оценка Ньюи—Уэста
Рассмотрим последовательно математику, стоящую за этим подходом, условия его применимости и пошаговую реализацию в коде на языке MQL5. Затем проиллюстрируем подход его практическим применением.
Математика метода
Этот подход относится к классу оценок HAC (Heteroskedasticity and Autocorrelation Consistent), что расшифровывается как "оценки, устойчивые к гетероскедастичности и автокорреляции". Для расчёта самих коэффициентов модели мы по-прежнему используем обычный МНК, поскольку гетероскедастичность и автокорреляция остатков сохраняют их состоятельность и несмещённость:
, где
- X — матрица регрессоров размерности n×k.
- y — вектор-столбец значений зависимой переменной длиной n.
- b — вектор-столбец искомых оценок параметров длиной k.
Однако для оценки их стандартных ошибок требуется принципиально новый подход. Коррекция выполняется за счёт изменения структуры ковариационной матрицы случайных ошибок. В классическом ОМНК предполагается, что эта матрица является диагональной, а все элементы на главной диагонали равны между собой (условия гомоскедастичности и отсутствия автокорреляции). В методе Ньюи—Уэста структура матрицы усложняется:
- Для учёта гетероскедастичности элементы на главной диагонали могут принимать различные значения (индивидуальная дисперсия для каждого наблюдения).
- Для учёта автокорреляции элементы на побочных диагоналях, параллельных главной, также становятся отличными от нуля. При этом влияние далёких друг от друга по времени наблюдений постепенно затухает, и на определённом лаге (ширине окна) матрица искусственно усекается.
В реальности матрица ковариаций ошибок нам, конечно же, неизвестна, и мы работаем с её выборочной оценкой. Практический алгоритм выглядит следующим образом:
- Для диагональных элементов используется классический подход Уайта, где истинные дисперсии ошибок оцениваются квадратами полученных остатков модели eₜ2.
- Для элементов вне главной диагонали задаётся число L (максимальный лаг автокорреляции). Ровно столько побочных диагоналей выше и ниже главной заполняются произведениями смещённых по времени остатков eₜ * eₜ-ₖ, умноженными на специальные веса.
Нам остаётся определить, как выбирать число L и сами веса. Для этого применяются эмпирические формулы. Количество лагов L мы будем рассчитывать по стандартной формуле: L = [4(n/100)2/9], где n — количество наблюдений, а квадратные скобки означают взятие целой части числа.
В качестве весовых функций (ядер сглаживания) в эконометрике обычно используются функции Бартлетта или Парзена. Стоит отметить, что строго классическая оценка Ньюи—Уэста базируется именно на ядре Бартлетта. Однако мы сделаем шаг в сторону большей вычислительной стабильности и выберем ядро Парзена, которое обеспечивает более гладкое затухание весов дальних лагов.
Определившись с видом оценки матрицы ковариаций ошибок, мы можем рассчитать искомую ковариационную матрицу коэффициентов регрессии. На её главной диагонали будут находиться квадраты робастных стандартных ошибок параметров модели. Их мы и задействуем для проверки значимости регрессоров (через t-статистики) и для интервального прогнозирования. Итоговая формула матрицы ковариаций коэффициентов (известная по неформальному названию как "сэндвич"-оценка) выглядит так:
, где:
- X — матрица регрессоров (размер n×k).
- Ve — построенная нами выборочная оценка матрицы ковариации ошибок (размер n×n).
- Vb — искомая робастная ковариационная матрица коэффициентов регрессии (размер k×k).
Важное примечание для разработки на MQL5: матрицу Ve размером n×n не нужно формировать в памяти напрямую. Это привело бы к колоссальным затратам оперативной памяти на больших исторических данных. На практике мы сразу будем вычислять итоговую матрицу Q = XT * Ve * X размера k×k через суммы произведений векторов, используя формулу:
![]()
Где xₜ — строка матрицы регрессоров X для наблюдения t, а wᵢ — веса Парзена, вычисляемые по формуле:

Чтобы правильно интерпретировать формулу матрицы Q, необходимо учесть, что строки xᵢ в ней понимаются как вектор-столбцы. Выражение xᵢ * xⱼT представляет собой внешнее произведение двух векторов, результатом которого является не число, а квадратная матрица размером k×k.
Условия применимости
Оценка Ньюи—Уэста является асимптотической, поэтому её корректная работа гарантирована только на достаточно больших выборках (от нескольких сотен наблюдений и более). Для алготрейдинга, где анализ ведётся на тысячах исторических баров, это условие выполняется естественным образом. Метод хорошо подходит для следующих типов моделей:
- Классическая линейная регрессия, где изучается зависимость целевого ряда y от объясняющих рядов Xi, взятых в тот же момент времени t.
- Модели с распределёнными лагами (Distributed Lag, DL), в которых регрессорами выступают текущие и прошлые (лаговые) значения независимых переменных Xi,t-k.
Использование данного подхода для авторегрессионных моделей (AR), где в качестве регрессоров выступают прошлые значения самой зависимой переменной (Yt-k), ограничено специфическими случаями.
Реализация в коде
Весь алгоритм упакован в единую функцию:
void regression_Newey_West(const vector& y, const matrix &X, CoefficientStats& stats[], SPrognose& prog, bool useNWE=true)
Входные аргументы:
- y — вектор-столбец зависимой переменной.
- X — матрица объясняющих переменных (регрессоров).
- prog.xnew — вектор значений регрессоров для новой точки, в которой необходимо построить прогноз.
- useNWE — логический флаг (по умолчанию true), переключающий расчёт между робастной оценкой Ньюи—Уэста и классическим МНК.
Функция не возвращает явного значения. В случае критического сбоя выполнение прерывается с выводом диагностического сообщения в журнал терминала. На выходе мы получаем полностью заполненный массив структур stats со статистикой по каждому коэффициенту и структуру prog с прогнозными метриками.
Работа алгоритма начинается с обязательной проверки корректности входных данных. Прежде всего контролируется согласованность размерностей матриц и векторов, а также геометрия прогнозного вектора:
ulong n=y.Size(), k=X.Cols(); if(n<1) {Print("regression_Newey_West() error: empty y"); return;} if(n!=X.Rows()) {Print("regression_Newey_West() error: X rows matrix size must be equal to y size"); return;} if(n<=k) {Print("regression_Newey_West() error: not enough samples"); return;} if(X.Rank()<k) {Print("regression_Newey_West() error: multicollinearity in the X matrix"); return;} if(prog.xnew.Size()!=k) {Print("regression_Newey_West() error: wrong vector xnew size for prognose"); return;}
На следующем этапе вычисляются оценки коэффициентов модели b и вектор остатков e. Благодаря встроенной поддержке матричных операций в MQL5, этот шаг реализуется буквально в несколько строк. Код фактически дублирует математические формулы в матричном виде. Здесь же рассчитывается несмещённая оценка дисперсии остатков se2 с учётом числа степеней свободы:
matrix XT_X_1=X.Transpose().MatMul(X).Inv(); vector b=XT_X_1.MatMul(X.Transpose()).MatMul(y); vector e=y-X.MatMul(b); double se2=e.Dot(e)/(n-k);
Центральная часть алгоритма — вычисление ковариационной матрицы коэффициентов Vb. При выборе подхода Ньюи—Уэста применяется классическая "сэндвич"-структура. Роль внешних слоёв ("хлеба") в ней выполняет рассчитанная ранее матрица XT_X_1, а роль "начинки" — матрица Q, состоящая из двух компонент.
Первое слагаемое отвечает за корректировку на гетероскедастичность остатков:
for(ulong t=1;t<=n;++t) Q+=e[t-1]*e[t-1]*X.Row(t-1).Outer(X.Row(t-1));
Второе слагаемое вводится для учёта автокорреляции остатков. Здесь L задаёт максимальный лаг автокорреляции, а wj — веса ядра Парзена, определяющие степень затухания связи во времени:
ulong L=(ulong)MathFloor(4.0*MathPow(n/100.0,2.0/9.0)); double wj,tau; for(ulong j=1;j<=L;++j) { tau=double(j)/(L+1); wj=(tau<=0.5)?1.0-6.0*tau*tau+6.0*MathPow(tau,3.0):2.0*MathPow(1.0-tau,3.0); dq=matrix::Zeros(k, k); for(ulong t=j+1;t<=n;++t) dq+=e[t-1]*e[t-j-1]*(X.Row(t-1).Outer(X.Row(t-j-1))+X.Row(t-j-1).Outer(X.Row(t-1))); Q+=wj*dq; }
Финальная сборка "сэндвич"-оценки выполняется умножением матриц:
Vb=XT_X_1.MatMul(Q).MatMul(XT_X_1);
Если же используется классический МНК, вычисление ковариационной матрицы существенно упрощается и занимает всего одну строку:
Vb=se2*XT_X_1;
На следующем этапе для каждого коэффициента регрессии последовательно заполняется структура статистических метрик. Точечная оценка параметра извлекается напрямую из вектора b:
stats[i].estimate = b[i];
Стандартная ошибка вычисляется как квадратный корень из соответствующего диагонального элемента ковариационной матрицы Vb:
stats[i].std_error = MathSqrt(Vb[i][i]);
t-статистика, необходимая для проверки гипотезы о значимости коэффициента, рассчитывается стандартным делением оценки на её ошибку (с защитой от деления на нуль):
if(stats[i].std_error > 0) stats[i].t_stat = stats[i].estimate / stats[i].std_error; else stats[i].t_stat = 0.0;
Для оценки значимости коэффициента (проверки нулевой гипотезы H0: bi = 0) рассчитывается одностороннее p-value, которое при необходимости легко превратить в двустороннее обычным удвоением:
stats[i].p_value = MathCumulativeDistributionT(MathAbs(stats[i].t_stat),n-k,false,false,ner);
Интервальная оценка параметров модели строится для классического уровня доверия 95%. Для поиска критического значения распределения Стьюдента используется функция MathQuantileT:
t_crit=MathQuantileT(0.025,n-k,false,false,ner); stats[i].conf_low = stats[i].estimate - t_crit * stats[i].std_error; stats[i].conf_high = stats[i].estimate + t_crit * stats[i].std_error;
Заключительный этап работы функции посвящён заполнению прогнозной структуры prog для новых значений регрессоров prog.xnew. Точечный прогноз рассчитывается как скалярное произведение вектора новых данных и вектора оценённых коэффициентов модели:
prog.point_progn = prog.xnew.Dot(b);
Доверительный интервал (Confidence Interval) определяет границы, в которых с вероятностью 95% находится истинное среднее значение (линия регрессии). Его дисперсия вычисляется как квадратичная форма на основе ковариационной матрицы коэффициентов Vb и вектора новых регрессоров. Стандартная ошибка находится извлечением квадратного корня:
double var_ci = prog.xnew.Dot(Vb.MatMul(prog.xnew)); double std_ci = MathSqrt(var_ci); prog.conf_low = prog.point_progn + t_crit * std_ci; prog.conf_high = prog.point_progn - t_crit * std_ci;
Прогнозный интервал (Prediction Interval) шире доверительного, так как он определяет границы не для среднего значения, а для конкретной будущей реализации случайной величины. Чтобы учесть это, к дисперсии параметров var_ci добавляется дисперсия самой случайной ошибки регрессии. Моделирование дисперсии шума как функции регрессоров здесь не рассматривается; используем классический вариант Ньюи—Уэста. В данном примере используется базовый эконометрический подход — шум принимается условно гомоскедастичным на прогнозном горизонте и оценивается средним значением по выборке (se2):
double var_pi = var_ci + se2; double std_pi = MathSqrt(var_pi); prog.progn_low = prog.point_progn + t_crit * std_pi; prog.progn_high = prog.point_progn - t_crit * std_pi;
Практический пример(скрипт news.mq5)
В качестве иллюстрации исследуем влияние макроэкономических публикаций на динамику валютного курса, а именно — зависимость рыночной волатильности от времени, оставшегося до выхода новостей (или прошедшего после него).
Спецификация модели. Анализ проводится на внутридневном таймфрейме M5 для валютной пары EURUSD. В качестве зависимой переменной y используется логарифм отношения максимальной цены к минимальной за время формирования бара.
Матрица регрессоров X включает константу и один нелинейный фактор — нормализованное временное расстояние до момента публикации. Для нормализации применяется колоколообразная функция Гауссова ядра (схожая с плотностью нормального распределения). При точном совпадении времени бара с выходом новости её значение достигает максимума (единицы) и плавно затухает до нуля по мере удаления от события в любую сторону. Источником исторических данных о макроэкономических событиях служит встроенный в платформу MetaTrader 5 экономический календарь.
Реализация в коде. Скрипт начинается с объявления входных переменных (инпутов), назначение которых полностью отражено в комментариях:
input string symbol = "EURUSD"; // Торговый инструмент для анализа input datetime from_t = D'2025.10.15'; // Начало временного интервала анализа котировок input datetime to_t = D'2026.04.15'; // Конец временного интервала анализа котировок input ENUM_TIMEFRAMES tf = PERIOD_M5; // Таймфрейм (период графика) input string nsource = "US"; // Страна-источник макроэкономических новостей input double ntau = 3000.0; // Параметр масштаба для нормализации времени
Затем подключается заголовочный файл EconometricsM.mqh, содержащий спроектированную нами ранее основную функци regression_Newey_West():
#include "EconometricsM.mqh"
На следующем шаге задаётся статический массив идентификаторов конкретных макроэкономических событий, которые будут учитываться при построении регрессии:
ulong events_ids[]= {840010007,840020001,840030006,840030015,840050014};
Поиск этих идентификаторов выполняется вручную на основе полного перечня событий, доступных для выбранного региона в MetaTrader 5. Чтобы автоматизировать выгрузку этого списка, был написан вспомогательный скрипт events2file.mq5. Он сохраняет числовые ID и текстовые описания всех новостей заданной страны в текстовый файл.
Мы не будем подробно останавливаться на технических процедурах выгрузки котировок и новостных событий, поскольку они выходят за рамки данной статьи. Главное, что к моменту формирования массивов для анализа у нас уже есть массив баров rates[] и массив временных меток публикаций новостей ntime[], упорядоченные по времени. Заполнение вектора зависимой переменной y и матрицы регрессоров X выполняется в цикле. Для каждого бара с помощью функции бинарного поиска определяется ближайшее во времени новостное событие:
vector y(n); matrix X=matrix::Ones(n,2); for(ulong i=0;i<n;++i) { y[i]=MathLog(rates[i].high/rates[i].low); X[i][1]=tnorm(rates[i].time-ntime[ArrayBsearch(ntime,rates[i].time)]); }
Вспомогательная функция tnorm(), используемая для нелинейной нормализации временного расстояния, устроена очень лаконично. Она реализует математику Гауссова ядра:
double tnorm(datetime t) { double dt=t/ntau; return MathExp(-dt*dt); }
Чтобы повысить интерпретируемость результатов и уйти от микроскопических абсолютных значений логарифмического спреда, мы масштабируем вектор зависимой переменной, деля его на собственное стандартное отклонение (СКО):
y/=y.Std();
Далее инициализируются динамический массив структур stat для хранения оценок параметров и структура prog для вычисления прогнозных метрик:
CoefficientStats stat[]; SPrognose prog;
Задаются значения вектора xnew для построения точечного и интервальных прогнозов. Первый элемент вектора традиционно равен единице (для свободного члена/константы), а второй принимает значение 1.0, что соответствует моменту точного совпадения времени с выходом новости (максимальный импульс волатильности):
prog.xnew.Resize(2); prog.xnew[0]=1.0; prog.xnew[1]=1.0;
В финальной части скрипта последовательно запускаются расчёты — сначала для робастного метода Ньюи—Уэста, а затем для классического МНК. После каждого вызова результаты тестирования коэффициентов и прогнозные интервалы выводятся в журнал:
//--- Метод Ньюи-Уэста regression_Newey_West(y,X,stat,prog); ArrayPrint(stat); Print("==================================="); prog.print(); Print("==================================="); //--- Обычный МНК regression_Newey_West(y,X,stat,prog,false); Print("==================================="); ArrayPrint(stat); Print("==================================="); prog.print();
Анализ результатов расчётов. Запустим скрипт в торговой платформе при указанных значениях входных параметров. Наша главная задача — оценить различия в расчёте стандартных ошибок коэффициентов регрессии (std_error, выделено цветом) классическим МНК и робастным методом Ньюи—Уэста.
Результаты вычислений методом Ньюи—Уэста:
[estimate] [std_error] [t_stat] [p_value] [conf_low] [conf_high] [0] 1.32636 0.01201 110.46915 0.00000 1.30283 1.34989 [1] 1.85750 0.19297 9.62570 0.00000 1.47927 2.23573
Результаты вычислений классическим МНК:
[estimate] [std_error] [t_stat] [p_value] [conf_low] [conf_high] [0] 1.32636 0.00519 255.74145 0.00000 1.31620 1.33653 [1] 1.85750 0.05401 34.39346 0.00000 1.75164 1.96335
Сравнение результатов демонстрирует огромную количественную разницу: для свободного члена (константы) стандартная ошибка увеличилась более чем в два раза, а для коэффициента при нормализованном времени — почти в четыре раза. Соответственно, доверительные интервалы параметров модели в робастном варианте оказались значительно шире.
В данном конкретном эксперименте эта разница не повлекла за собой качественного изменения выводов, поскольку оба коэффициента сохранили свою статистическую значимость (p-value в обоих случаях практически равно нулю). Полученный результат математически подтверждает сильное и закономерное влияние времени публикации макроэкономических новостей на внутридневную волатильность выбранного валютного курса.
Условный метод наименьших квадратов (CLS)
Метод Ньюи—Уэста корректирует стандартные ошибки, не пытаясь детально описать внутреннюю структуру автокорреляции остатков. Теперь мы переходим к условному методу наименьших квадратов (Conditional Least Squares, CLS). Этот подход, напротив, позволяет строить модели, в которых структура автокорреляции остатков явно и глубоко описывается математическим аппаратом авторегрессии и скользящего среднего. Как и с предыдущим подходом, рассмотрим стоящую за ним математику и область его применений. Затем реализуем его в коде и применим на практике.
Математика метода
Модели авторегрессии (AR, ARMA и их модификации) станут центральной темой следующей статьи нашей серии, однако базовый минимум сведений необходим нам уже сейчас. Это позволит понять, зачем вообще требуется усложнение алгоритмов и внедрение таких моделей в практику алготрейдинга.
Проще всего логику этого семейства понять на примере моделей авторегрессии (AR). В этом случае мы просто добавляем прошлые значения (лаги) самой зависимой переменной в качестве дополнительных регрессоров. Если после добавления нескольких лагов автокорреляция в остатках исчезает, задачу можно считать решённой. Количество использованных лагов таргета определяет порядок модели p, а сама модель обозначается как AR(p).
Однако простым добавлением лагов зависимой переменной не всегда удаётся полностью устранить автокорреляцию. Кроме того, чрезмерное раздувание количества лагов — плохая практика, так как модель не должна быть перегружена параметрами. В таких ситуациях переходят к моделированию остатков с помощью процесса скользящего среднего (MA), где текущее отклонение зависит от текущего и прошлых значений ненаблюдаемого белого шума. Длина этого скользящего среднего определяет порядок модели q, которая обозначается как MA(q).
На финансовых рынках чаще всего одновременно присутствуют оба эффекта. Комбинация лагов таргета и MA-моделирования ошибок даёт модель ARMA(p, q). Пара (p, q) задаёт порядки AR- и MA-частей. Для простоты понимания можно представить, что в модели ARMA в качестве дополнительных регрессоров (помимо лагов самого таргета) используются лаги прошлых ошибок модели. Главная проблема заключается в том, что эти "регрессоры" изначально нам неизвестны.
Итак, наша задача — найти коэффициенты модели ARMA(p,q). Всего оценке подлежат p + q + 1 параметров (включая константу/свободный член). При q > 0 классический МНК становится неприменим из-за принципиальной ненаблюдаемости прошлых значений белого шума, на которых строится скользящее среднее. Тем не менее целевая задача остается прежней — минимизация суммы квадратов полученных остатков (ошибок прогноза), теоретическим обоснованием чего выступает концепция M-оценок из математической статистики.
Однако из-за наличия MA-компоненты задача минимизации больше не сводится к системе линейных уравнений. Она становится нелинейной, что вынуждает нас использовать итерационный численный метод Гаусса—Ньютона. На каждом шаге итерации мы уточняем значения всех коэффициентов модели. Для этого вычисляется матрица Якоби, состоящая из частных производных каждого отдельного значения ошибки по оцениваемым параметрам.
Важной проблемой здесь являются начальные условия: нам неизвестны значения лагов таргета и ошибок, находящиеся до самого первого доступного момента наблюдений. В рамках условного МНК (CLS) мы поступаем двояко: либо условно приравниваем эти неизвестные лаги к нулю, либо извлекаем их из предшествующей предыстории ряда (если алгоритм функционирует в режиме скользящего окна).
Сформировав матрицу Якоби и вектор ошибок с учётом выбранных начальных условий, мы находим вектор поправок к коэффициентам и обновляем их. Итерационный процесс продолжается до выполнения критерия остановки: алгоритм завершает работу либо после достижения заданного лимита итераций либо когда модуль очередного изменения параметров становится меньше установленного порога.
Условия применимости
Размер выборки. Как и поправка Ньюи—Уэста, метод CLS требует достаточно больших выборок (от нескольких сотен наблюдений). На малых данных точность условного МНК резко падает из-за высокой чувствительности к начальным условиям (приравниванию первых лагов к нулю) и статистического смещения оценок авторегрессии.
Интегрированные ряды (ARIMA). Помимо классических процессов AR и ARMA, метод применим для моделей ARIMA. Для этого нестационарный исходный ряд необходимо предварительно привести к стационарному виду путём последовательного взятия разностей (подбора порядка интеграции).
Экзогенные факторы (ARMAX). С минимальной модификацией алгоритм масштабируется на модели классов ARX, ARMAX и ARIMAX, где символ X обозначает включение в систему дополнительных внешних (экзогенных) регрессоров.
Реализация в коде
Мы продолжаем придерживаться процедурного стиля, при котором код организован предельно просто и линейно. Это необходимо для того, чтобы алгоритм было легко понять и напрямую перенести отдельными блоками в архитектуру наших советников или индикаторов. Такой "плоский" подход позволяет полностью контролировать логику вычислений и избегать нецелевых трат памяти. Подобные издержки часто неизбежны при использовании громоздких сторонних библиотек, перегруженных избыточными сложными абстракциями и неоптимальной объектно-ориентированной архитектурой (ООП).
Весь алгоритм реализован в одной функции. Входные параметры:
- y — вектор значений зависимой переменной.
- p, q — порядки AR и MA частей модели соответственно.
- y_, e_ — предыстория зависимой переменной и остатков для инициализации.
- max_iter — лимит итераций алгоритма Левенберга-Марквардта.
- min_change — критерий останова по сходимости параметров.
Выходные параметры (передаются по ссылке):
- e — рассчитанный вектор остатков (ошибок) модели.
- stats — массив структур с оценками и статистикой по каждому коэффициенту.
- prog — структура с результатами прогноза (точечная оценка, доверительный и прогнозный интервалы).
Функция не возвращает явного значения. При критической ошибке вычисления прерываются с выводом диагностики в журнал терминала.
void regression_CLS(const vector& y, const ulong p, const ulong q, const vector& y_, const vector& e_, vector& e, CoefficientStats& stats[], SPrognose& prog, const ulong max_iter=1000, const double min_change=1e-5)
Валидация входных данных. Перед запуском оптимизатора проверяется корректность переданных параметров и размерностей данных. Здесь переменная k определяет общее количество оцениваемых параметров модели ARMA(p, q) с учётом константы (свободного члена). Проверки контролируют:
- Регуляризаторы алгоритма: число итераций должно быть положительным, а порог сходимости — строго больше нуля.
- Степени свободы: объем выборки n должен превышать число оцениваемых коэффициентов k.
- Соответствие предыстории: размер векторов y_ и e_ должен строго соответствовать заявленным порядкам p и q.
При обнаружении несоответствий функция записывает ошибку в терминал и досрочно завершает работу:
ulong n=y.Size(),k = p + q + 1; if(max_iter==0) {Print("regression_CLS() error: max_iter==0"); return;} if(min_change<=0.0) {Print("regression_CLS() error: min_change<=0.0"); return;} if(n<=k) {Print("regression_CLS() error: y too short"); return;} if(y_.Size()!=p) {Print("regression_CLS() error: wrong y_ size"); return;} if(e_.Size()!=q) {Print("regression_CLS() error: wrong e_ size"); return;}
Коэффициенты при константе и AR-компонентах инициализируются с помощью обычного метода наименьших квадратов (МНК). Для этого формируется матрица регрессоров X0 размера n × (p + 1), куда закладывается вектор констант и лагированные значения y. Логика вычислений:
- Заполнение лагов: если глубина индекса i превышает доступную историю вектора y, данные берутся из вектора предыстории y_.
- Проверка на мультиколлинеарность: через метод Rank() проверяется линейная независимость столбцов матрицы X0. Если ранг меньше числа оцениваемых параметров p + 1, матрица вырождена, и МНК-оценка невозможна.
- Расчёт вектора весов: коэффициенты вычисляются по стандартной формуле МНК.
matrix X0=matrix::Ones(n,p+1); for(ulong i=0;i<n;++i) for(ulong j=1;j<=p;++j) X0[i][j]=(i>=j)?y[i-j]:y_[p+i-j]; if(X0.Rank()<p+1) {Print("regression_CLS() error: multicollinearity in the X0 matrix"); return;} vector b=X0.Transpose().MatMul(X0).Inv().MatMul(X0.Transpose()).MatMul(y);
Полученная МНК-оценка используется для расчёта начальных остатков модели. Коэффициенты MA-компонентов инициализируются нулями:
e=y-X0.MatMul(b0); b=b.Concat(vector::Zeros(q));
Перед запуском оптимизатора инициализируются матрицы производных, переменные состояния и параметры регуляризации алгоритма Левенберга—Марквардта. Назначение ключевых переменных:
- J и JT_J — матрица Якоби n×k и её Грамиан k×k (аппроксимация матрицы Гессиана JT * J), необходимые для вычисления шага обновления параметров.
- lambda (λ) — коэффициент демпфирования. Управляет компромиссом между методом градиентного спуска (при больших λ) и методом Гаусса—Ньютона (при λ → 0).
- v — шаг модификации λ. Если шаг оптимизации успешен, λ уменьшается в v раз, если неуспешен — увеличивается.
- rss_old — базовая сумма квадратов остатков (RSS), рассчитываемая через скалярное произведение вектора на себя e.Dot(e). Служит критерием успешности текущей итерации.
- b_old и e_old — буферы для отката состояния алгоритма, если предложенное обновление коэффициентов приведёт к росту ошибки вместо её минимизации.
//--- Матрица Якоби и сопутствующие структуры matrix J(n,k), JT_J(k,k); vector delta_b=vector::Zeros(k); double change=1.0; //--- Параметры алгоритма Левенберга-Марквардта double lambda = 0.01; // Начальный параметр демпфирования (damping factor) double v = 2.0; // Множитель изменения lambda double rss_old = e.Dot(e); // Стартовая сумма квадратов остатков (RSS) vector b_old = b; // Резервная копия вектора коэффициентов vector e_old = e; // Резервная копия вектора остатков //--- Главный цикл оптимизации ulong iter=0; for(iter=0;iter<max_iter;++iter) {
В начале каждой итерации вычисляется матрица Якоби J размера n×k. Её элементы представляют собой частные производные от остатков (ошибок) eₜ по всем оцениваемым параметрам модели. Поскольку остатки в модели ARMA зависят от своих прошлых значений, расчёт производных выполняется рекурсивно:
for(ulong t=0;t<n;++t) { J[t][0]=-1.0; for(ulong i=1;i<=q;++i) J[t][0]-=(t>=i)?b[p+i]*J[t-i][0]:0.0; for(ulong j=1;j<=p;++j) { J[t][j]=(t>=j)?-y[t-j]:-y_[p+t-j]; for(ulong i=1;i<=q;++i) J[t][j]-=(t>=i)?b[p+i]*J[t-i][j]:0.0; } for(ulong j=1;j<=q;++j) { J[t][p+j]=(t>=j)?-e[t-j]:-e_[q+t-j]; for(ulong i=1;i<=q;++i) J[t][p+j]-=(t>=i)?b[p+i]*J[t-i][p+j]:0.0; } }
На основе матрицы Якоби вычисляется матрица Грама (аппроксимирующая Гессиан), к которой применяется регуляризация Левенберга—Марквардта для обеспечения стабильности и сходимости алгоритма:
JT_J = J.Transpose().MatMul(J); for(ulong i = 0; i < k; ++i) JT_J[i][i] += lambda;
На основе регуляризованной матрицы Грама вычисляется вектор приращения параметров, после чего модель временно обновляется для оценки новой суммы квадратов остатков (RSS):
//--- Расчёт демпфированного шага оптимизации delta_b = -1.0 * JT_J.Inv().MatMul(J.Transpose()).MatMul(e); //--- Временное обновление вектора параметров b += delta_b; //--- Пересчёт вектора остатков e для новых коэффициентов for(ulong t = 0; t < n; ++t) { e[t] = y[t] - b[0]; for(ulong j = 1; j <= p; ++j) e[t] -= (t >= j) ? b[j] * y[t - j] : b[j] * y_[p + t - j]; for(ulong j = 1; j <= q; ++j) e[t] -= (t >= j) ? b[p + j] * e[t - j] : b[p + j] * e_[q + t - j]; } rss_new = e.Dot(e);
На каждом шаге алгоритм оценивает динамику целевой функции (RSS) и корректирует стратегию поиска: уменьшает lambda для ускорения сходимости или откатывает параметры назад при росте ошибки:
//--- Оценка эффективности текущего шага if(rss_new < rss_old) { // Успешная итерация: принимаем изменения lambda /= v; // Снижаем демпфирование для перехода к Гауссу-Ньютону rss_old = rss_new; b_old = b; // Обновляем базовое состояние e_old = e; // Проверка сходимости по норме приращения параметров change = delta_b.Norm(VECTOR_NORM_P, 2.0); if(change < min_change) { PrintFormat("L-M converged successfully after %d iterations.", iter + 1); break; } } else { // Неудачный шаг: откат параметров и усиление демпфирования lambda *= v; // Смещаем шаг в сторону градиентного спуска b = b_old; // Восстанавливаем коэффициенты e = e_old; // Восстанавливаем остатки // Защита от зацикливания при расходимости алгоритма if(lambda > 1e10) { Print("L-M stopped: lambda became too large (optimization stuck)."); break; } }
После успешного завершения итерационного цикла на основе финальной матрицы Якоби J и вектора остатков e вычисляется ковариационная матрица оценок параметров. Она необходима для проверки статистической значимости полученных коэффициентов и получения прогнозов:
//--- Несмещённая оценка дисперсии остатков (sigma^2) double se2 = e.Dot(e) / double(n - k); //--- Ковариационная матрица коэффициентов нелинейного МНК //--- Вычисляется как sigma^2 * (J^T * J)^(-1) на основе финального Якобиана matrix Vb = se2 * J.Transpose().MatMul(J).Inv();
Расчёт статистик коэффициентов и построение прогнозных интервалов идентичны алгоритмам, рассмотренным ранее для робастных оценок Ньюи—Уэста. Чтобы избежать повторений, отметим лишь специфику формирования прогноза. В данном случае модель строит точечную оценку на один шаг вперед (для момента времени t = n + 1). Для этого вектор новых регрессоров prog.xnew заполняется внутри функции:
//--- Формирование полного вектора регрессоров для прогнозного бара (размер: p + q + 1) prog.xnew.Resize(k); // 1. Установка константы prog.xnew[0] = 1.0; // 2. Заполнение AR-лагов из самого конца выборки вектора y for(ulong j = 1; j <= p; ++j) prog.xnew[j] = (n >= j) ? y[n - j] : y_[p + (n - j)]; // 3. Заполнение MA-лагов из самого конца вектора рассчитанных ошибок e for(ulong j = 1; j <= q; ++j) prog.xnew[p + j] = (n >= j) ? e[n - j] : e_[q + (n - j)];
Практический пример (скрипт spread.mq5)
Для иллюстрации работы метода рассмотрим моделирование динамики парного спреда.
Спецификация модели. Мы оценим параметры модели ARMA(1,1) для логарифмического спреда двух акций на основе цен закрытия дневных баров. В качестве базовых активов возьмём акции компаний Alphabet Inc. (GOOGL) и Apple Inc. (AAPL): y = ln(close₁/close₂). Особенности торговой логики:
- Рыночный нейтралитет: использование логарифмического спреда в алгоритмическом трейдинге подразумевает открытие позиций по обоим активам в противоположных направлениях на одинаковые денежные объёмы (не количество лотов).
- Фактор стационарности: для построения устойчивой среднесрочной стратегии (mean-reversion) исследуемый спред должен быть стационарным. Подробно математические критерии стационарности мы разберём в следующей статье серии. В контексте модели ARMA(1,1) базовым условием стационарности ряда является требование, чтобы модуль коэффициента при AR(1)-компоненте был строго меньше единицы. Для уверенного возврата спреда к среднему значению на практике этот коэффициент должен быть существенно меньше единицы.
Реализация в коде. Работа скрипта начинается с объявления входных параметров. Их назначение полностью отражено в комментариях:
input string symbol_stock1 = "GOOGL"; // Первый символ для расчёта спреда input string symbol_stock2 = "AAPL"; // Второй символ для расчёта спреда input datetime from_t = D'2024.04.01'; // Начало анализируемого интервала цен input datetime to_t = D'2026.04.01'; // Конец анализируемого интервала цен input ENUM_TIMEFRAMES tf = PERIOD_D1; // Таймфрейм input ulong p_ar = 1; // Порядок AR-компоненты модели input ulong q_ma = 1; // Порядок MA-компоненты модели
Затем подключается заголовочный файл EconometricsM.mqh, содержащий нужную нам функцию regression_CLS():
#include "EconometricsM.mqh"
Вычисления начинаются с запроса исторических котировок по обоим активам и проверки объема выборки на соответствие степеням свободы модели:
cpp ulong k=p_ar+q_ma+1; MqlRates rates_stock1[], rates_stock2[]; int n_rates_stock1 = CopyRates(symbol_stock1, tf, from_t, to_t, rates_stock1); int n_rates_stock2 = CopyRates(symbol_stock2, tf, from_t, to_t, rates_stock2); PrintFormat("Number of downloaded quotes for first stock %s: %d", symbol_stock1, n_rates_stock1); PrintFormat("Number of downloaded quotes for second stock %s: %d", symbol_stock2, n_rates_stock2); if(n_rates_stock1 <= int(k) || n_rates_stock2 <= int(k)) {Print("Not enough data"); return;}
Поскольку из-за праздников или корпоративных событий торговые календари акций могут не совпадать, перед расчётом спреда выполняется синхронизация баров по времени. Для синхронных баров вычисляется логарифмический спред и записывается в вектор y:
vector y((ulong)n_rates_stock1); int i_stock1 = 0, i_stock2 = 0; ulong n = 0; for(; i_stock1 < n_rates_stock1; ++i_stock1) { // Ищем бар второго инструмента с временем >= времени первого while(i_stock2 < n_rates_stock2 && rates_stock2[i_stock2].time < rates_stock1[i_stock1].time) i_stock2++; if(i_stock2 >= n_rates_stock2) break; // История второго инструмента закончилась if(rates_stock2[i_stock2].time > rates_stock1[i_stock1].time) continue; // Пропуск, если точного совпадения по времени нет // Временные метки совпали, рассчитываем логарифмический спред n++; y[n-1] = MathLog(rates_stock1[i_stock1].close / rates_stock2[i_stock2].close); i_stock2++; } y.Resize(n); if(n <= k) {Print("Not enough data after synchronization"); return;} Print("y length: ", n);
Перед вызовом основного метода инициализируются структуры данных, векторы предыстории и массивы для сбора статистики оценок:
//--- Подготовка векторов для CLS vector y_=vector::Zeros(p_ar), e_=vector::Zeros(q_ma), e(n); //--- Массив для статистики коэффициентов CoefficientStats stat[]; //--- Структура для прогноза на один шаг вперед SPrognose prog; //--- Запуск оптимизатора CLS regression_CLS(y, p_ar, q_ma, y_, e_, e, stat, prog);
Завершающий блок скрипта выводит расчётные метрики в журнал терминала и генерирует графики для визуального анализа:
//--- Вывод результатов в журнал Print("==================================="); //--- Статистика коэффициентов ArrayPrint(stat); Print("==================================="); //--- Метрики прогноза prog.print(); Print("==================================="); //--- График динамики остатков во времени (e vs t) t_residuals_plot(e); //--- График автокорреляционной функции остатков (ACF) correlogram(e);
Анализ результатов расчётов. Запустим скрипт в торговой платформе MetaTrader 5 при указанных входных параметрах. Наша ключевая задача — оценить коэффициент при AR(1)-компоненте модели для проверки спреда на стационарность. Таблица статистических метрик для всех коэффициентов модели в журнале Expert выглядит следующим образом:
[estimate] [std_error] [t_stat] [p_value] [conf_low] [conf_high] [0] -0.0004 0.0012 -0.3431 0.3658 -0.0028 0.0019 [1] 0.9942 0.0057 175.8232 0.0000 0.9831 1.0053 [2] 0.0638 0.0454 1.4055 0.0803 -0.0254 0.1530
Интерпретация оценок:
- Статистическая значимость. Оценки константы [0] и MA(1)-компоненты [2] имеют высокий уровень p-value (более чем 0.05), что говорит об их статистической незначимости. Единственным значимым параметром в модели является коэффициент AR(1) (строка [1]), для которого p-value равен 0.0000.
- Риск нестационарности. Точечная оценка AR(1) крайне близка к единице (0.9942). Более того, рассчитанный 95%-й доверительный интервал [0.9831; 1.0053] включает в себя единицу.
- Важный торговый вывод: наличие единичного корня (или значения, неотличимого от него) — это весомый эконометрический довод в пользу нестационарности исследуемого логарифмического спреда GOOGL/AAPL на данном временном интервале. Попытки торговать такой спред на возврат к среднему необоснованны и несут в себе высокие риски неконтролируемого убытка.
Ограничения текущего анализа и вектор развития. На данном этапе у нас пока недостаточно математических оснований для финального вердикта по спреду. Мы сталкиваемся с двумя слепыми зонами:
- Субъективный выбор спецификации: порядок модели ARMA(1,1) был выбран нами произвольно. На практике требуются строгие критерии для обоснованного подбора порядков p и q.
- Отсутствие формальных тестов: визуальный анализ доверительного интервала не заменяет полноценную проверку. Необходимы специализированные статистические тесты на стационарность.
Решению этих задач и будут посвящены следующие статьи нашей серии.
Заключение
Наличие автокорреляции и гетероскедастичности в рыночных данных требует принципиального изменения подхода к оценке моделей. В данной статье мы успешно решили эту проблему на двух уровнях:
- Мы внедрили расчёт робастных стандартных ошибок в форме HAC-коррекции Ньюи—Уэста. Этот метод не меняет сами МНК-коэффициенты, но пересчитывает их ковариационную матрицу, "очищая" стандартные ошибки от влияния автокорреляции и гетероскедастичности. Это позволяет трейдеру получать несмещённые t-статистики и корректно оценивать значимость факторов.
- Мы реализовали метод условных наименьших квадратов (CLS), управляемый регуляризацией Левенберга—Марквардта. Поскольку остатки модели ARMA рекурсивно зависят от собственных прошлых значений, обычный МНК здесь неприменим. Разработанный оптимизатор адаптивно переключается между методами градиентного спуска и Гаусса-Ньютона, гарантируя стабильную сходимость к глобальному минимуму целевой функции.
В следующей статье серии мы углублённо рассмотрим авторегрессионные процессы, которые служат математическим фундаментом для построения большинства прогнозных торговых стратегий.
Список приложенных файлов
Также все файлы выложены в хранилище Algo Forge по адресу: https://forge.mql5.io/alexeynikolaev2/Article-23435-OLS-Generalization
| # | Тип файла | Название | Описание |
|---|---|---|---|
| 1 | Заголовочный файл | EconometricsA.mqh | Файл со всеми необходимыми функциями (для массивов) для расчёта параметров простой регрессии, построения графиков и проведения статистических тестов. |
| 2 | Заголовочный файл | EconometricsM.mqh | Файл со всеми необходимыми функциями (для векторов и матриц) для расчёта параметров множественной регрессии, построения графиков и проведения статистических тестов. |
| 3 | MQL5 скрипт | news.mq5 | Анализ зависимости волатильности от времени до выхода новостей |
| 4 | MQL5 скрипт | events2file.mq5 | Вспомогательный скрипт для получения списка всех типов новостей для страны |
| 5 | MQL5 скрипт | spread.mq5 | Анализ спреда между двумя активами |
| 6 | ZIP архив | MQL5.zip | Архив со всеми файлами. |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Возможности Мастера MQL5, которые вам нужно знать (Часть 90): Управление капиталом на основе дерева Фенвика и одномерной CNN в MQL5
Изучение стандартной библиотеки MQL5 (Часть 8): Ведение журнала сделок гибридной системы с помощью CFileTxt
Изучение стандартной библиотеки MQL5 (Часть 9): Использование ALGLIB для фильтрации избыточного числа сигналов пересечения скользящих средних
Возможности Мастера MQL5, которые вам нужно знать (Часть 89): Использование побитовой векторизации с перцептронными классификаторами
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования