English
preview
Построение моделей волатильности в MQL5 (Часть II): Реализация моделей GJR-GARCH и TARCH

Построение моделей волатильности в MQL5 (Часть II): Реализация моделей GJR-GARCH и TARCH

MetaTrader 5Примеры |
316 1
Francis Dube
Francis Dube

Оглавление

  1. Введение
  2. Почему и когда асимметрия имеет значение?
  3. Модель GJR-GARCH
  4. Модель TARCH
  5. Реализация в MQL5 и несколько примеров
  6. GJR-GARCH против TARCH
  7. Заключение


Введение

В первой части этой серии мы реализовали библиотеку волатильности MQL5. Мы построили процессы условного среднего, такие как AR и HAR, и объединили их со стандартными схемами моделирования волатильности ARCH/GARCH. Хотя эти традиционные модели эффективно описывают кластеризацию, их ограничивает симметричная природа. Они рассматривают рыночные ралли и рыночные обвалы как события с одинаковым влиянием на будущий риск.

В этой части мы устраняем данное ограничение, дополняя библиотеку поддержкой асимметричных процессов волатильности, а именно моделей GJR-GARCH и TARCH. Помимо определения этих моделей, мы объясним, почему асимметрия необходима с математической и поведенческой точек зрения, а также как эти подходы улучшают ARCH/GARCH. Наконец, статья продемонстрирует, как реализовать эти асимметричные процессы в составе полноценных моделей волатильности в MQL5 на реальных данных. Читатели получат готовый к использованию код, который учитывает фактор паники на финансовых рынках. Это расширяет инструментарий для управления рисками и анализа направленного движения рынка.


Почему и когда асимметрия имеет значение?

Чтобы понять переход к асимметрии, сначала нужно оценить линейные основы, заложенные ранними авторегрессионными моделями. До появления моделей ARCH и GARCH экономисты обычно предполагали, что дисперсия постоянна во времени, но это предположение не соответствовало реальным событиям. Революция началась в 1982 году с модели ARCH (Autoregressive Conditional Heteroskedasticity, авторегрессионная условная гетероскедастичность) Роберта Энгла. Прорыв Энгла состоял в осознании того, что, хотя мы не можем легко предсказать направление завтрашней доходности, мы можем предсказать интенсивность её колебаний. Он предложил рассматривать сегодняшнюю дисперсию как взвешенную функцию прошлых квадратов шоков.

Однако у моделей ARCH была проблема: для отражения устойчивости волатильности им требовалась длинная память (много лагов). В 1986 году Тим Боллерслев решил эту задачу с помощью GARCH (Generalized ARCH, обобщённая ARCH). Модель GARCH(1,1) Боллерслева была элегантной, потому что сделала волатильность рекурсивной. Она предполагала, что завтрашняя дисперсия зависит от двух факторов.

  • Последняя доходность.
  • Последняя оценка самой волатильности.

Это позволило простой модели всего с тремя параметрами описывать кластеры волатильности, наблюдаемые в рыночных данных. Несмотря на элегантность, модель GARCH(1,1) всё ещё имела ограничения. Математическая причина кроется в том, как она обрабатывает рыночные шоки. В стандартном уравнении GARCH:

формула GARCH

Прогнозируемая дисперсия зависит от квадрата наблюдения за предыдущий период. При возведении в квадрат знак теряется: и положительное, и отрицательное значение становятся положительными. Поэтому модель одинаково учитывает положительные и отрицательные ценовые шоки. GARCH предполагает, что рынок с одинаковой силой реагирует и на благоприятный сюрприз, и на резкое падение. Это ограничение симметрии, которое для фондовых рынков оказывается слишком грубым упрощением. По мере развития GARCH исследователи обнаружили в эмпирических данных устойчивую закономерность, которую модель не могла объяснить: эффект финансового рычага.

Эффект финансового рычага, впервые отмеченный Фишером Блэком в 1976 году, означает, что после падения цены волатильность обычно растёт гораздо резче, чем после роста цены той же величины. Обычно выделяют две основные причины этого явления.

  • Когда цена акций компании падает, её соотношение долга и собственного капитала растёт, поскольку долг остаётся относительно неизменным, а собственный капитал сокращается. Доля заёмных средств в структуре капитала растёт. Такая компания несёт для инвесторов больший риск. Этот возросший риск напрямую приводит к повышению будущей волатильности акции. В такой трактовке эффект финансового рычага — не просто название; это буквальное описание корпоративных финансов. По мере истончения «подушки» собственного капитала акция становится чувствительнее к каждой новой порции информации. Хотя гипотеза финансового рычага выглядит логически убедительной, она часто не объясняет сам масштаб всплесков волатильности, особенно в широких рыночных индексах, где эффект финансового рычага отдельных компаний должен сглаживаться диверсификацией. Именно здесь вступают в игру поведенческие финансы и эффект обратной связи волатильности.

  • Поведенческие финансы предполагают, что инвесторы болезненно воспринимают потери. Падение цены вызывает панику, маржин-коллы и принудительные ликвидации, что создаёт хаотичную среду с высокой волатильностью. Рост цены, напротив, обычно сопровождается рациональной фиксацией прибыли или спокойным накоплением позиции. В основе этой теории лежит неприятие потерь — психологическая реальность, согласно которой боль от убытка примерно вдвое сильнее радости от эквивалентной прибыли. Когда рынок начинает снижаться, инвесторы не просто рационально ребалансируют портфели. Они испытывают страх. Этот страх запускает каскад продаж, часто усиленный автоматическими стоп-лосс-ордерами и маржин-коллами. Кроме того, по мере роста волатильности инвесторы, не склонные к риску, требуют более высокую премию за риск за удержание актива. Чтобы обеспечить эту более высокую ожидаемую доходность, текущая цена актива должна снизиться ещё сильнее. Так возникает самоподдерживающийся цикл: падающие цены ведут к более высокой волатильности, она ведёт к более высоким премиям за риск, а те — к дальнейшему падению цен.

Эффект финансового рычага показывает, что волатильность определяется не только силой воздействия новостей, но и характером этих новостей. Это понимание подготовило почву для моделей GJR и TARCH, которые были специально разработаны, чтобы вернуть знак в уравнение волатильности.


Модель GJR-GARCH

Если стандартная модель GARCH — это простой термостат, реагирующий на любое изменение температуры, то модель GJR-GARCH — это более сложная система климат-контроля, распознающая различие между экстремальными температурными условиями. Модель GJR-GARCH была представлена Лоуренсом Глостеном, Рави Джаганнатаном и Дэвидом Ранклом в их основополагающей статье 1993 года «On the Relation between the Expected Value and the Volatility of the Nominal Excess Return on Stocks». Их цель была простой, но амбициозной: устранить «нечувствительность к знаку» исходной GARCH и предоставить более точный инструмент для оценки производных финансовых инструментов и управления портфельным риском.

Модель GJR-GARCH(1,1) расширяет классическое уравнение дисперсии, добавляя дополнительный член, специально предназначенный для учёта асимметричных шоков. Математическая структура выглядит следующим образом.

формула GJR-GARCH

  • Omega — это долгосрочная базовая дисперсия.
  • Alpha — влияние предыдущего шока (симметричная часть).
  • Beta — устойчивость волатильности (память модели).
  • Gamma — параметр асимметрии, ключевой элемент модели.
  • Индикаторная функция действует как условный переключатель, определяющий, когда применять дополнительный «вес паники».

Центральный элемент модели GJR-GARCH — индикаторная функция. Её можно представить как фиктивную переменную или двоичный переключатель, отслеживающий знак доходности предыдущего дня. Она работает по простому правилу: когда рынок растёт, индикатор равен 0. Член gamma фактически исчезает, и модель ведёт себя как стандартная GARCH. Однако когда рынок падает, индикатор принимает значение 1. В этот момент шок усиливается одновременно параметрами alpha и gamma. Следовательно, gamma выступает количественной мерой хрупкости рынка. Если gamma > 0, это подтверждает эффект финансового рычага. Это означает, что отрицательный шок значительно сильнее дестабилизирует рынок, чем положительный шок.

В управлении рисками высокая gamma является предупреждением: во время спадов волатильность может резко возрастать, повышая риск маржин-коллов и проблем с ликвидностью. Для трейдера опционов gamma объясняет волатильность, наблюдаемую в ценообразовании опционов. Это помогает понять, почему пут-опционы «вне денег» часто дороже эквивалентных колл-опционов. Количественно оценивая дополнительную волатильность, вызванную плохими новостями, GJR-GARCH гораздо реалистичнее отражает человеческую психологию перед лицом финансовых потерь.


Модель TARCH

Теперь, когда мы рассмотрели модель GJR-GARCH, стоит рассмотреть её ближайшего родственника — модель TARCH. В то время как GJR-GARCH моделирует условную дисперсию, Threshold ARCH (TARCH, пороговая ARCH) моделирует условное стандартное отклонение. Это тонкое изменение делает модель ещё более чувствительной к шокам. Пока модель GJR-GARCH набирала популярность в США, Жан-Мишель Закоян (1994) разрабатывал во Франции несколько иной подход к асимметрии. Его модель преследовала ту же цель, что и GJR-GARCH, — учёт эффекта рычага, но пришла к ней через иной математический подход. Самое существенное отличие модели TARCH от линии GARCH состоит в том, какую величину она прогнозирует. Большинство моделей типа GARCH (включая GJR-GARCH) моделируют условную дисперсию. Закоян утверждал, что часто более интуитивно и статистически устойчиво моделировать условное стандартное отклонение.

формула TARCH

В такой постановке модель реагирует на абсолютное значение шоков. При моделировании дисперсии мы фактически возводим шоки в квадрат. Это даёт экстремальным выбросам значительное влияние на итоговую модель. Моделируя стандартное отклонение, TARCH линейно реагирует на величину шока. Это может сделать её более стабильной и менее чувствительной к единичным выбросам, сохраняя при этом чувствительность к знаку шока. «Порог» в TARCH означает точку, в которой поведение модели меняется. Если доходность выше порога, волатильность развивается по одному сценарию. Если доходность опускается ниже порога, модель переключается в другой режим, где влияние шока усиливается.

Это создаёт кусочно-линейную кривую влияния новостей. В отличие от параболической формы стандартной GARCH, TARCH формирует асимметричную кривую влияния с более крутым наклоном для отрицательных доходностей, чем для положительных. News Impact Curve (NIC, кривая влияния новостей) — это диагностический инструмент эконометрики, используемый для визуализации того, как конкретная модель преобразует шоки или неожиданные движения цены в будущую волатильность. Если отложить текущий шок по оси x, а прогнозируемую волатильность на следующий период — по оси y, кривая показывает, является ли модель симметричной, асимметричной или устойчивой к выбросам. Ниже приведена иллюстрация NIC для моделей GARCH, GJR-GARCH и TARCH.

Кривая влияния новостей

Кривая влияния стандартной GARCH представляет собой идеально симметричную параболу, что напрямую связано с использованием квадратов шоков. Кривая GJR-GARCH также остаётся квадратичной, но слева от оси y её наклон намного круче, что показывает повышенную чувствительность к отрицательным доходностям. Кривая TARCH отличается двумя прямыми линиями, сходящимися в вершине при нулевом шоке и образующими кусочно-линейную форму «V». Показанный график создан скриптом Impact_Curves.ex5, приложенным к статье.


Реализация в MQL5 и несколько примеров

Рассмотренные асимметричные процессы волатильности реализованы как классы, наследующиеся от базового класса GARCH — CGarchProcess. В частности, класс CGjrGarchProcess отвечает за модели GJR-GARCH, а за модели TARCH отвечает класс CTarchProcess.

//+------------------------------------------------------------------+
//| GJR-GARCH process                                                |
//+------------------------------------------------------------------+
class CGjrGarchProcess: public CGarchProcess
  {
public:
                     CGjrGarchProcess(void)
     {
      m_initialized = CGarchProcess::_initialize(VOL_GJR_GARCH,true,false,0,"GJR-GARCH");
     }
                     CGjrGarchProcess(ulong p, ulong o, ulong q, int seed = 0,ulong nboots=100)
     {
      m_initialized = CGarchProcess::_initialize(VOL_GJR_GARCH,true,false,0,"GJR-GARCH",seed,p,o,q,2.0,0,-1,nboots);
     }
  };
//+------------------------------------------------------------------+
//| TARCH process                                                    |
//+------------------------------------------------------------------+
class CTarchProcess: public CGarchProcess
  {
public:
                     CTarchProcess(void)
     {
      m_initialized = CGarchProcess::_initialize(VOL_TARCH,true,false,0,"TARCH",0,1,1,1,1.0);
     }
                     CTarchProcess(ulong p,ulong o,ulong q,int seed = 0,ulong nboots=100)
     {
      m_initialized = CGarchProcess::_initialize(VOL_TARCH,true,false,0,"TARCH",seed,p,o,q,1.0,0,-1,nboots);
     }
  };

Чтобы задать любую из этих моделей на практике, установите свойство vol_model_type экземпляра ArchParameters в соответствующее значение перечисления ENUM_VOLATILITY_MODEL.

ArchParameters tarch_spec;
tarch_spec.vol_model_type = VOL_TARCH;
ArchParameters gjr_spec;
gjr_spec.vol_model_type = VOL_GJR_GARCH;  

Модель GJR-GARCH прежде всего отличается активным параметром "O" (асимметрия), который должен быть ненулевым. Если пользователь явно не задаёт иное, этот параметр по умолчанию равен 1. В отличие от неё, модели TARCH однозначно распознаются по степенному параметру, который строго зафиксирован на уровне 1. Скрипты GJR_Demo.ex5 и TARCH_Demo.ex5 демонстрируют настройку процессов волатильности GJR-GARCH и TARCH соответственно. Важно отметить, что любой из этих процессов можно объединить с любой моделью среднего, уже реализованной в библиотеке. В следующем разделе мы применим библиотеку для создания индикатора прогнозирования условной волатильности и стандартного отклонения.

//+------------------------------------------------------------------+
//|                                                     GJR_Demo.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs
#include<VolatilityModels\Arch\Univariate\mean.mqh>
//--- input parameters
input string   Symbol_="AUDUSD";
input ENUM_TIMEFRAMES   TimeFrame=PERIOD_D1;
input datetime StartDate=D'2025.01.01';
input ulong HistoryLen = 504;
input double ScaleFactor=100.;
input bool MeanConstant = true;
input ulong _P_ = 1;
input ulong _O_ = 1;
input ulong _Q_ = 1;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   if(_O_<1)
     {
      Alert("Invalid setting: GJR-GARCH models need an asymmetry parameter of at least 1.\nOtherwise, it becomes a regular GARCH model.");
      return;
     }
//---download data
   vector prices;
   if(!prices.CopyRates(Symbol_,TimeFrame,COPY_RATES_CLOSE,StartDate,HistoryLen))
     {
      Print("Failed to get close prices for ", Symbol_,". Error ", GetLastError());
      return;
     }
//---
   prices = log(prices);
//---
   vector returns = np::diff(prices);
//-- specify the model parameters
   ArchParameters gjr_spec;
   gjr_spec.observations=ScaleFactor*returns;
   gjr_spec.include_constant = MeanConstant;
   gjr_spec.vol_model_type = VOL_GJR_GARCH;
   gjr_spec.garch_p = _P_;
   gjr_spec.garch_q = _Q_;
   gjr_spec.garch_o = _O_;
//-- initialize the mean model
   ConstantMean gjr_model;
//---
   if(!gjr_model.initialize(gjr_spec))
      return;
//---get the model parameters
   vector empty;
   ArchModelResult gjr_params = gjr_model.fit(ScaleFactor);
//--check the model is fitted
   if(!gjr_params.params.Size())
     {
      Print("Convergence failed ", GetLastError());
      return;
     }
//---
   Print("GJR-GARCH model parameters ", gjr_params.params);
   vector pv = gjr_params.pvalues();
   Print("GJR-GARCH model pvalues: ");
   for(ulong i = 0; i<pv.Size(); ++i)
      Print("Pvalue for param at ", i, " :- ", pv[i]);
//---
  }
//+------------------------------------------------------------------
Индикатор ConditionalVolatility_forecaster.ex5 применяет библиотеку для прогнозирования в реальном времени. Для каждого нового бара он калибрует модель на скользящем историческом окне (исключая самый последний бар) и формирует одношаговый прогноз. Помимо самого прогноза, индикатор вычисляет z-показатель волатильности.

z-показатель волатильности

Эта метрика рассчитывает, на сколько стандартных отклонений текущий прогноз отклоняется от исторической нормы, фактически устраняя смещение, зависящее от торгового инструмента, чтобы лучше выявлять аномальные всплески волатильности. Пользователи могут задать конкретный порог для таких отклонений с помощью входного параметра Num_Stds.

Параметры индикатора задаются через настраиваемые входные параметры.

  • HistoryLen определяет размер выборки данных, используемой для калибровки.
  • BarsToDraw задаёт общее количество исторических баров, которые рассчитает индикатор.
  • Num_Stds определяет порог, выше которого волатильность считается относительно высокой.
//+------------------------------------------------------------------+
//|                             ConditionalVolatility_forecaster.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<VolatilityModels\Arch\Univariate\mean.mqh>
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot Forecast
#property indicator_label1  "Forecast"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_DOT
#property indicator_width1  1
//--- plot Mean
#property indicator_label2  "Vol_Zscore"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLimeGreen
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- input parameters
input int BarsToDraw = 50;
input int HistoryLen = 200;
input double Num_Stds = 1.5;
input double ScaleFactor=100.;
input ENUM_MEAN_MODEL MeanModel = MEAN_CONSTANT;
input bool MeanConstant = false;
input string MeanLags ="";
input ENUM_VOLATILITY_MODEL VolatilityModel = VOL_TARCH;
input ulong _P_ = 1;
input ulong _O_ = 1;
input ulong _Q_ = 1;
input int Volatility_Seed = 0;
input ENUM_DISTRIBUTION_MODEL ErrorDistribution = DIST_NORMAL;
input int Distribution_Seed = 0;
//--- indicator buffers
double         ForecastBuffer[];
double         ThresholdBuffer[];
double         VolZscoreBuffer[];
vector         returns = vector::Zeros(HistoryLen);
ArchParameters spec;
HARX* full_model;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(HistoryLen<30)
     {
      Print("Invalid input value for HistoryLen");
      return INIT_FAILED;
     }
//--- indicator buffers mapping
   SetIndexBuffer(0,ForecastBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,VolZscoreBuffer,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,BarsToDraw);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,BarsToDraw);
//---
   IndicatorSetInteger(INDICATOR_LEVELS,2);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,Num_Stds);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-1.0);
//---
   spec.mean_model_type = MeanModel;
   if(StringLen(MeanLags))
     {
      string lag_info[];
      int nlags = StringSplit(MeanLags,StringGetCharacter(",",0),lag_info);
      if(nlags>0)
        {
         for(uint i = 0; i<uint(nlags); ++i)
           {
            if(StringLen(lag_info[i])>0)
              {
               if(spec.mean_lags.Resize(spec.mean_lags.Size()+1,3))
                  spec.mean_lags[spec.mean_lags.Size()-1] = StringToDouble(lag_info[i]);
               else
                 {
                  Print("Error ", GetLastError());
                  return INIT_FAILED;
                 }
              }
           }
        }
     }

   spec.vol_rng_seed = Volatility_Seed;
   spec.garch_o = _O_;
   spec.garch_p = _P_;
   spec.garch_q = _Q_;
   spec.dist_type = ErrorDistribution;
   spec.dist_rng_seed = Distribution_Seed;

   switch(MeanModel)
     {
      case MEAN_CONSTANT:
         full_model = new ConstantMean();
         break;
      case MEAN_ZERO:
         full_model = new ZeroMean();
         break;
      case MEAN_AR:
         full_model = new AR();
         break;
      default:
         full_model = new ConstantMean();
         break;
     }

   if(CheckPointer(full_model)==POINTER_INVALID)
      return INIT_FAILED;

   switch(VolatilityModel)
     {
      case VOL_GJR_GARCH:
         spec.vol_model_type = VOL_GJR_GARCH;
         break;
      case VOL_TARCH:
         spec.vol_model_type = VOL_TARCH;
         break;
      default:
         spec.vol_model_type = VOL_GARCH;
         break;
     }

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(CheckPointer(full_model)==POINTER_DYNAMIC)
      delete full_model;
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
                const int32_t 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 int32_t &spread[])
  {
//---
   int32_t limit;
   static datetime last_time;
   if(time[rates_total-1]==last_time)
      return rates_total;
   else
      last_time = time[rates_total-1];
   if(prev_calculated<=0)
      limit = rates_total - int32_t(fabs(BarsToDraw));
   else
      limit = prev_calculated - 1;
   for(int32_t shift = limit; shift<rates_total; ++shift)
     {
      int from = shift - fabs(HistoryLen);
      for(int32_t i = from; i<(shift); ++i)
         returns[i-(from)] = log(close[i]/close[i-1]);
      returns*=fabs(ScaleFactor);
      spec.observations = returns;
      if(!full_model.initialize(spec))
        {
         Print("Initialization error ");
         return 0;
        }
      ArchModelResult result = full_model.fit(ScaleFactor);
      ulong size = result.conditional_volatility.Size();
      if(!size)
        {
         Print("Model fit error ");
         return 0;
        }

      ArchForecast forecast = full_model.forecast();
      ForecastBuffer[shift] = (spec.vol_model_type!=VOL_TARCH)?sqrt(forecast.variance[0,0]):forecast.variance[0,0];
      VolZscoreBuffer[shift] = (ForecastBuffer[shift] - result.conditional_volatility.Mean())/result.conditional_volatility.Std();
     }

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

График показывает две линии: красную линию одношагового прогноза волатильности и светло-зелёную линию z-показателя волатильности. Кроме того, на графике отображаются порог Num_Stds и базовый уровень по умолчанию на -1. Торговые сигналы получают, отслеживая положение z-оценка относительно этих уровней; например, если Num_Stds установлен на 2.0 и z-оценка превышает это значение, это означает аномальный всплеск волатильности. Изображение ниже показывает, как индикатор выглядит при применении к графику в MetaTrader 5.

индикатор ConditionalVolatility_forecaster


GJR-GARCH против TARCH

Следующий вопрос — когда выбрать одну модель вместо другой. Выбор между GJR-GARCH и TARCH часто сводится к компромиссу между точностью описания и устойчивостью. Хотя обе модели асимметричны, они по-разному реагируют на рыночные экстремумы. Выбор зависит от статистических свойств данных, класса актива и цели моделирования.

Одно из важнейших статистических свойств, которое нужно учитывать, — содержит ли набор данных экстремальные значения. Разница между возведением шока в квадрат и взятием его абсолютного значения здесь является решающим фактором. Модель TARCH следует использовать, если набор данных содержит экстремальные единичные всплески, а цель состоит в минимизации влияния этих экстремумов. Поскольку TARCH линейна, она рассматривает экстремальное падение как ровно вдвое более значимое, чем среднее падение. Если же цель — построить модель, чувствительную к экстремальным движениям цены, тогда используйте GJR-GARCH. GJR-GARCH — агрессивный выбор для моделирования хвостового риска.

Класс базового актива имеет значение, поскольку асимметрия — не универсальный закон финансов, а характеристика корпоративной структуры. Гипотеза финансового рычага (соотношение долга и собственного капитала) — это явление, основанное на дисперсии. GJR-GARCH лучше подходит для инструментов, связанных с акциями, где фактор паники стабильно высок и статистически значим. Поэтому для акций логичнее использовать модель GJR-GARCH. Валютные рынки часто демонстрируют меньшую асимметрию эффекта рычага, поскольку падение одной валюты пары является ростом второй валюты. Подход TARCH на основе абсолютных значений часто лучше подходит для таких более сбалансированных рынков, чем агрессивное возведение в квадрат, используемое в GJR-GARCH.

Это явление можно практически продемонстрировать, сравнив асимметричные профили волатильности разных классов активов. Данный анализ реализован в скрипте Equity_vs_Forex_Asymmetry_Significance_Test.ex5, который строит модели волатильности как для данных валютного рынка, так и для фондовых данных. Для обеспечения воспроизводимости и единого набора данных фондовые данные были получены с помощью Python-модуля yfinance.

//+------------------------------------------------------------------+
//|                  Equity_vs_Forex_Asymmetry_Significance_Test.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#resource "\\Files\\VolatilityModels\\sp500.csv" as string equity_data
#property script_show_inputs
#include<VolatilityModels\Arch\Univariate\mean.mqh>
//--- input parameters
input string   Symbol_="AUDUSD";
input ENUM_TIMEFRAMES   TimeFrame=PERIOD_D1;
input datetime StartDate=D'2025.01.01';
input ENUM_VOLATILITY_MODEL VolatilityModel = VOL_GJR_GARCH;
input double ScaleFactor=100.;
input ENUM_MEAN_MODEL MeanModel = MEAN_CONSTANT;
input bool MeanConstant = true;
input ulong _P_ = 1;
input ulong _O_ = 1;
input ulong _Q_ = 1;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   if(VolatilityModel != VOL_GJR_GARCH && VolatilityModel!=VOL_TARCH)
     {
      Print("INVALID INPUT: choose either TARCH or GJR-GARCH");
      return;
     }
//---
   matrix data = np::readcsv_from_string(equity_data,false,",",true,0);
   vector equity = data.Col(1);
   vector forex;
   if(!forex.CopyRates(Symbol_,TimeFrame,COPY_RATES_CLOSE,datetime(data[0,0]),datetime(data[data.Rows()-1,0])))
     {
      Print("Failed to get close prices for ", Symbol_,". Error ", GetLastError());
      return;
     }

   forex = log(forex);
   equity = log(equity);

   forex = np::diff(forex);
   equity = np::diff(equity);

   forex*=ScaleFactor;
   equity*=ScaleFactor;

   ArchParameters asym_vol_spec;
   asym_vol_spec.include_constant = MeanConstant;
   asym_vol_spec.mean_model_type = MeanModel;
   asym_vol_spec.vol_model_type = VolatilityModel;
   asym_vol_spec.garch_p = _P_;
   asym_vol_spec.garch_q = _Q_;
   asym_vol_spec.garch_o = _O_;

   asym_vol_spec.observations=equity;

   HARX*  equity_model = NULL;
   HARX*  forex_model = NULL;

   switch(MeanModel)
     {
      case MEAN_CONSTANT:
         equity_model = new ConstantMean();
         forex_model = new ConstantMean();
         break;
      case MEAN_ZERO:
         equity_model = new ZeroMean();
         forex_model = new ZeroMean();
         break;
      default:
         equity_model = new ConstantMean();
         forex_model = new ConstantMean();
         break;
     }

   if(!equity_model.initialize(asym_vol_spec))
     {
      delete equity_model;
      return;
     }

   ArchModelResult equity_result = equity_model.fit(ScaleFactor);
   Print("Results for ", EnumToString(VolatilityModel));
   if(!equity_result.params.Size())
     {
      Print("Equity model fit failed ");
      delete equity_model;
     }
   else
     {
      Print("Equity: Pvalue of gamma parameter is ", equity_result.pvalues()[3]);
      delete equity_model;
     }

   asym_vol_spec.observations = forex;
   if(!forex_model.initialize(asym_vol_spec))
     {
      delete forex_model;
      return;
     }

   ArchModelResult forex_result = forex_model.fit(ScaleFactor);

   if(!forex_result.params.Size())
     {
      Print("Forex model fit failed ");
      delete forex_model;
     }
   else
     {
      Print("Forex: pvalue of gamma parameter is ", forex_result.pvalues()[3]);
      delete forex_model;
     }
  }
//+------------------------------------------------------------------

Используя аналитические возможности библиотеки, мы можем проверить статистическую значимость параметра асимметрии модели (γ). Эта информация доступна через свойство pvalues() структуры ArchModelResult. Это свойство может быть полезно при оценке экономности модели — в частности, оправдана ли статистически дополнительная сложность асимметричной модели для конкретного набора данных. Результаты ниже получены после двух запусков скрипта: сначала для GJR-GARCH, затем для TARCH.

Результаты тестов на асимметрию

Различие p-значений заметно. В результатах GJR-GARCH фондовая модель даёт статистически значимое p-значение для параметра gamma, тогда как модель для валютного рынка даёт высокое p-значение, что указывает на отсутствие асимметричных эффектов. Для моделей TARCH, хотя оба p-значения выглядят значимыми, фондовая модель снова сохраняет существенно более низкое значение. Эти эмпирические результаты подкрепляют гипотезу о том, что «эффект финансового рычага» является доминирующей характеристикой фондовых рынков и часто отсутствует или ослаблен на валютных рынках.

Выбор модели волатильности должен соответствовать цели моделирования. TARCH часто даёт более стабильные прогнозы (более низкую MAE), тогда как GJR-GARCH лучше подходит для задач хвостового риска, таких как ценообразование деривативов и Value-at-Risk (VaR). Если есть сомнения, сравните AIC/BIC для обеих моделей. Можно оценить параметры обеих моделей и выбрать модель по статистическим критериям. Если модель TARCH даёт более низкий IC, это означает, что дополнительная сложность квадратных членов GJR-GARCH не помогает объяснять данные; возможно, она лишь добавляет шум.


Заключение

Эта часть показала, что волатильность — это не только мера величины рыночного движения: она также может быть чувствительна к его направлению. Выйдя за пределы симметричных ограничений стандартных моделей ARCH и GARCH, мы оснастили нашу библиотеку MQL5 возможностью учитывать эффект финансового рычага — эмпирическую реальность, в которой плохие новости воздействуют на рыночную психологию агрессивнее, чем хорошие. Мы рассмотрели два основных пути учёта этой асимметрии.

  • Модель GJR-GARCH. Подход на основе дисперсии, в котором применяется квадратичный штраф для моделирования экспоненциальной природы рыночной паники, что делает его основным выбором для анализа риска акций и событий в хвостах распределения.
  • Модель TARCH. Устойчивая альтернатива на основе стандартного отклонения, использующая абсолютные значения для получения стабильных прогнозов; она особенно эффективна для валютных рынков и наборов данных, склонных к экстремальным выбросам.

Благодаря реализации классов CGjrGarchProcess и CTarchProcess разработчики MQL5 теперь имеют встроенные средства для более сложного моделирования риска. Используя предоставленный индикатор ConditionalVolatility_forecaster.ex5 и расчёт z-оценки, трейдеры могут устранять смещение, специфичное для символа, и выявлять действительные режимы волатильности в реальном времени. Хотя выбор между GJR-GARCH и TARCH зависит от конкретного класса актива и цели трейдера — будь то устойчивое прогнозирование TARCH или агрессивная оценка риска GJR-GARCH, — включение параметра асимметрии (γ) гарантирует, что «знак» рыночных шоков больше не игнорируется.

Интеграция асимметричных моделей волатильности повышает полезность библиотеки. Она также приближает проект к встроенному набору инструментов MQL5, сопоставимому с Python-модулем arch. Некоторые трудности остаются: оценки параметров расходятся между двумя платформами, вероятно из-за различий в оптимизаторах.

Это расхождение станет центральной темой следующей части серии. Исходный код всех программ, упомянутых в тексте, приложен ниже.

Файл или папка Описание
MQL5/Include/VolatilityModels/np.mqh: Заголовочный файл различных вспомогательных функций для векторов и матриц.
MQL5/Include/VolatilityModels/Arch: Папка заголовочных файлов библиотеки моделирования условной волатильности.
MQL5/Files/VolatilityModels/sp500.csv: Этот файл содержит данные OHLC по S&P 500, используемые в скриптах.
MQL5/Scripts/VolatilityModels/Impact_Curves.mq5: Этот скрипт генерирует график кривых влияния новостей, упомянутый в статье.
MQL5/Scripts/VolatilityModels/GJR_Demo.mq5: Этот скрипт демонстрирует оценивание модели GJR-GARCH на наборе данных.
MQL5/Scripts/VolatilityModels/TARCH_Demo.mq5: Этот скрипт демонстрирует оценивание модели TARCH на наборе данных.
MQL5/Scripts/VolatilityModels/Equity_vs_Forex_Asymmetry_Significance_Test.mq5: Скрипт демонстрирует различие в асимметричной волатильности, присущее наборам данных валютного и фондового рынков.
MQL5/Indicators/VolatilityModels/ConditionalVolatility_forecaster.mq5: Этот индикатор демонстрирует прогнозирование волатильности в реальном времени.


Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22258

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Anil Varma
Anil Varma | 4 мая 2026 в 08:30
Хорошо объясненная концепция. Однако код не может быть скомпилирован, 100+ ошибок компиляции. Постарайтесь быть серьезнее.
Создание торговой системы (Часть 4): Как случайные выходы из сделок влияют на ожидаемую доходность Создание торговой системы (Часть 4): Как случайные выходы из сделок влияют на ожидаемую доходность
Многие трейдеры сталкивались с подобной ситуацией. Они часто придерживаются своих критериев входа, но испытывают трудности с сопровождением сделок. Даже при корректных торговых сетапах эмоциональное принятие решений, например, панический выход до того, как сделки достигнут уровней тейк-профита или стоп-лосса, - может привести к снижению кривой эквити. Как трейдеры могут преодолеть эту проблему и улучшить свои результаты? В данной статье мы рассмотрим эти вопросы, исследуя случайные винрейты (доля прибыльных сделок) и демонстрируя с помощью моделирования по методу Монте-Карло, как трейдеры могут совершенствовать свои стратегии, фиксируя прибыль на разумных уровнях до достижения первоначальной цели.
Архитектура машинного обучения для MetaTrader 5 (Часть 14): Моделирование транзакционных издержек для разметки методом тройного барьера в MQL5 Архитектура машинного обучения для MetaTrader 5 (Часть 14): Моделирование транзакционных издержек для разметки методом тройного барьера в MQL5
В статье заданные вручную предположения об издержках в разметке методом тройного барьера заменяются измеренными данными. Скрипт MQL5 собирает у брокера распределение спреда, ставки свопа и свойства символа, а модель Python преобразует эти данные в min_ret, откалиброванный с учётом условий брокера, который можно передать в get_events(). После этого метки учитывают фактические издержки полного цикла сделки для выбранного инструмента и периода удержания позиции.
Как реализовать R/S-анализ и индикатор Хёрста в MQL5 Как реализовать R/S-анализ и индикатор Хёрста в MQL5
Индикатор показателя Хёрста для MQL5 реализован на основе R/S-анализа с OLS-регрессией в log-log пространстве. Теоретическая опора — результаты Gatheral–Jaisson–Rosenbaum (2014), согласно которым волатильность — дробное броуновское движение с H ≈ 0.10. Индикатор оценивает H в скользящем окне, выделяет антиперсистентный (H < 0.3), нейтральный и трендовый (H > 0.5) режимы, окрашивает линию и подаёт алерт при смене режима, помогая выбирать тип стратегии и управлять риском.
Рекуррентный количественный анализ (RQA) в MQL5: Разработка полноценной библиотеки анализа Рекуррентный количественный анализ (RQA) в MQL5: Разработка полноценной библиотеки анализа
В этой статье создаётся полный набор средств для количественного анализа рекуррентности (Recurrence Quantification Analysis, RQA) для MetaTrader 5 на чистом MQL5. Мы рассмотрим реконструкцию фазового пространства, встраивание с временной задержкой (time-delay embedding), построение матрицы расстояний и рекуррентной матрицы, извлечение метрик RQA, автоматический выбор эпсилона и расчёт в скользящем окне с помощью модульной архитектуры библиотеки. В завершение статья показывает, как применить библиотеку в практическом индикаторе, который выводит RR, DET, LAM, ENTR и TREND непосредственно на график, создавая надёжную основу для нелинейного анализа временных рядов в MQL5.