English
preview
Двумерные копулы в MQL5 (Часть 3): Реализация и настройка смешанных моделей копул

Двумерные копулы в MQL5 (Часть 3): Реализация и настройка смешанных моделей копул

MetaTrader 5Примеры |
65 3
Francis Dube
Francis Dube

Введение

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


Что такое смешанная копула?

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

Математическое определение смешанной копулы приведено ниже.

Формула смешанной копулы

В этой формуле w_i представляет веса, назначенные каждой отдельной копуле, причем сумма всех весов должна равняться единице. Переменная s обозначает общее число отдельных копул, включенных в смесь. Параметры смешанной копулы включают индивидуальные параметры (θi) каждой составляющей копулы. Такая структура дает смешанным копулам гибкость для моделирования широкого диапазона структур зависимости, которые не смогла бы уловить однокомпонентная/отдельная копула. Цель состоит в том, чтобы построить модель из достаточного числа копул-кандидатов для полного описания структур зависимости набора данных.

Хотя определение оптимального числа и конкретных семейств копул для заданного набора данных выходит за рамки этой статьи, мы продемонстрируем реализацию смешанной копулы, состоящей из трех различных семейств. Этот подход следует методологии, предложенной Фернандо Сабино да Силвой, Флавио Цигельманном и Жуаном Калдейрой в их работе «Стратегия парного трейдинга на S&P 500 с использованием смешанных копул». Авторы предлагают стратегию парного трейдинга, использующую смеси Клейтона-Франка-Гумбеля и Клейтона-Стьюдента-t-Гумбеля. Эти комбинации предназначены для учета как асимметричной хвостовой зависимости, так и потенциального поведения со сменой режимов в данных. Эффективность этих смесей обусловлена уникальными преимуществами каждого отдельного компонента, как показано в таблице ниже. 

Копула  Характеристика  Значение 
Клейтона Зависимость нижнего хвоста Улавливает совместные экстремальные движения вниз.
Гумбеля Зависимость верхнего хвоста Улавливает совместные экстремальные движения вверх.
Франка Симметрия без хвостовой зависимости Улавливает обычную зависимость в центре распределения, не предполагая, что экстремальные события будут происходить совместно.
Стьюдента-t  Симметричная с хвостовой зависимостью Улавливает тяжелые хвосты в обоих направлениях. Предположение в данном случае состоит в том, что если происходит экстремальное событие (вверх или вниз), инструменты, вероятно, будут двигаться вместе. 

Веса смеси отдают приоритет той копуле, которая лучше всего описывает структуру зависимости в данных. Например, копула Франка наиболее эффективна, когда данные демонстрируют низкую или умеренную корреляцию без экстремального хвостового поведения. Напротив, компонент Стьюдента-t предназначен для учета экстремальных движений, которые не обязательно являются асимметричными. Смесь Клейтона-Франка-Гумбеля является чисто архимедовой, что делает ее весьма эффективной для различения поведения нижнего хвоста, верхнего хвоста и независимой средней части. Эта комбинация дает два преимущества: вычислительную эффективность и высокую интерпретируемость. В отличие от нее, смесь Клейтона-Стьюдента-t-Гумбеля представляет собой гибридную модель; копула Стьюдента-t добавляет уровень гибкости, которого не хватает копуле Франка, поскольку одновременно учитывает хвостовую зависимость в обоих направлениях. Это особенно актуально для данных на меньших таймфреймов, где заметны кластеры волатильности.

Хотя упомянутая выше работа излагает концепцию, которую мы стремимся воспроизвести, в ней отсутствуют конкретные детали реализации смешанных копул. Чтобы восполнить этот пробел, мы обращаемся ко второму академическому источнику: «Выбор моделей смешанных копул с помощью штрафованного правдоподобия» авторов Zongwu Cai и Xian Wang, опубликованному в Journal of the American Statistical Association (ASA).


Оценка параметров смешанной копулы

В статье ASA представлен основанный на данных подход к выбору и оценке моделей смешанных копул, использующий пенализованное/штрафное правдоподобие в сочетании с оператором сжатия. Авторы решают задачу учета сложных структур зависимости в финансовых данных, позволяя одновременно объединять и уточнять несколько семейств копул.

Их методика отфильтровывает избыточные или незначимые компоненты копул, применяя штраф Smoothly Clipped Absolute Deviation (SCAD) к процедуре максимизации правдоподобия. Этот подход предназначен для одновременного выбора модели и оценки параметров. Используя подход, штрафующий веса, метод сжимает коэффициенты менее значимых копул до нуля. Процесс начинается с первоначального выбора копул-кандидатов для смеси, после чего выполняется процедура кросс-валидации для определения оптимальных параметров SCAD.

SCAD

SCAD — это метод регуляризации, предложенный для одновременного отбора переменных и оценки параметров. Он был разработан для преодоления ограничений регуляризации Lasso (L1) и Ridge (L2), в частности смещения, которое Lasso вносит из-за чрезмерного сжатия больших коэффициентов. В общем случае идеальная штрафная функция должна обладать тремя свойствами, называемыми оракульными свойствами. 

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

SCAD широко используется, потому что это одна из немногих функций, обладающих всеми тремя свойствами. Штраф SCAD задается производной, а не одной простой формулой. Его поведение меняется в зависимости от величины параметра, к которому он применяется; в данном контексте это веса. Метод работает в три этапа.

  1. Для малых коэффициентов штрафная функция оказывает тот же эффект, что и регуляризация Lasso, принуждая коэффициенты стремиться к нулю.


    SCAD: малые коэффициенты

  2. Для коэффициентов среднего размера «эффект Lasso» штрафной функции уменьшается с квадратичным переходом.


    SCAD: средние коэффициенты

  3. Для больших коэффициентов штраф становится постоянным. Как только коэффициент становится достаточно большим, SCAD перестает его подавлять, устраняя смещение оценки, характерное для регуляризации Lasso.

    SCAD: большие коэффициенты

В приведенных выше формулах λ — это параметр настройки порога разреженности, a управляет тем, насколько быстро штраф выходит на плато, а θ — штрафуемый параметр. Основной недостаток штрафа SCAD состоит в том, что он делает целевую функцию невыпуклой. В отличие от чашеобразной формы Ridge или Lasso, целевая функция SCAD может иметь несколько локальных минимумов, что требует более сложных процедур оптимизации.

Для поиска оптимальных параметров настройки используется процедура кросс-валидации. Данные разделяются на обучающую и тестовую выборки, и выполняется решетчатый поиск по кандидатным значениям λ и a. Для каждой пары модель смешанной копулы подгоняется к обучающим данным, а ее пенализованное/штрафное правдоподобие рассчитывается на тестовых данных. Цель состоит в максимизации правдоподобия параметров составляющих копул за вычетом штрафа SCAD, примененного к весам копул.

После определения оптимальных параметров SCAD итоговые параметры модели оцениваются на полном наборе данных. Поскольку одновременная оценка нескольких копул вычислительно сложна, в процессе используется алгоритм максимизации ожидания (Expectation-Maximization, EM).

  1. Процесс начинается с двухэтапной оценки для поиска начальных значений. Веса обычно распределяются поровну, а начальные параметры копул устанавливаются в допустимые значения в соответствии с их ограничениями.
  2. Следующий этап — шаг ожидания. На этой фазе уточняются веса. Она вычисляет, в какой степени каждая точка данных относится к конкретному компоненту копулы.
  3. Последний этап алгоритма EM — шаг максимизации. На этой фазе обновляются индивидуальные параметры копул для максимизации правдоподобия, взвешенного значениями, рассчитанными на предыдущем шаге.

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


Реализация моделей смешанных копул в MQL5

Рассматриваемая здесь реализация адаптирована из репозитория Hudson and Thames, изначально написанного на Python. Основная логика нативной реализации смешанных копул в MQL5 содержится в заголовочном файле mixed.mqh. В этом файле абстрактный класс CMixedCopula служит основой для всех реализаций смешанных копул. Этот класс определяет несколько защищенных свойств для управления смесью.

Класс определяет массив mcopulas, представляющий отдельные компонентные копулы смеси. Веса смешивания, назначенные каждому компоненту копулы, хранятся в векторе m_weights. Конкретные параметры каждого отдельного компонента копулы хранятся в одном векторе m_cop_parameters. Благодаря использованию абстрактного базового класса архитектура остается модульной. Это позволяет строить различные комбинации копул, сохраняя единый интерфейс.

//+------------------------------------------------------------------+
//|base class for mixed copula                                       |
//+------------------------------------------------------------------+
class CMixedCopula:public CObject
  {
protected:
   uint              m_num_cops;
   CBivariateCopula* m_copulas[];
   vector            m_weights;
   vector            m_cop_params;
   ENUM_COPULA_MIX   m_mixture;
public:
                     CMixedCopula(void):m_weights(vector::Zeros(0)),
                     m_cop_params(vector::Zeros(0)),
                     m_num_cops(0),
                     m_mixture(WRONG_VALUE)
     {
     }
                    ~CMixedCopula(void)
     {
      for(uint i = 0; i<m_copulas.Size(); ++i)
         if(CheckPointer(m_copulas[i]) == POINTER_DYNAMIC)
            delete m_copulas[i];
     }

Класс CMixedCopula также предоставляет методы для статистического анализа, выборки и управления моделью. Для оценки статистических свойств моделей смешанных копул класс определяет метод Copula_PDF(), который вычисляет совместную плотность копулы.

double            Copula_PDF(double u, double v, double _eps = 1.e-5)
     {
      u = fmin(fmax(_eps,u),1. - _eps);
      v = fmin(fmax(_eps,v),1. - _eps);
      vector pdf_ = vector::Zeros(m_weights.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
         pdf_[i] = m_copulas[i]?m_weights[i]*m_copulas[i].Copula_PDF(u,v,false):0.0;
      return pdf_.Sum();
     }

vector            Copula_PDF(vector& u, vector& v, double _eps = 1.e-5)
     {
      vector pdf_ = vector::Zeros(u.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
        {
         if(!m_copulas[i])
            continue;
         m_copulas[i].Set_eps(_eps);
         pdf_ += m_weights[i]*m_copulas[i].Copula_PDF(u,v);
        }
      return pdf_;
     }

Метод Copula_CDF() вычисляет совместную функцию распределения копулы, а метод Conditional_Probability() вычисляет условную кумулятивную вероятность..

double            Copula_CDF(double u, double v, double _eps = 1.e-5)
     {
      u = fmin(fmax(_eps,u),1. - _eps);
      v = fmin(fmax(_eps,v),1. - _eps);
      vector cdf_ = vector::Zeros(m_weights.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
         cdf_[i] = m_copulas[i]?m_weights[i]*m_copulas[i].Copula_CDF(u,v,false):0.0;
      return cdf_.Sum();
     }

vector            Copula_CDF(vector& u, vector& v, double _eps = 1.e-5)
     {
      vector cdf_ = vector::Zeros(u.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
        {
         if(!m_copulas[i])
            continue;
         m_copulas[i].Set_eps(_eps);
         cdf_+=m_weights[i]*m_copulas[i].Copula_CDF(u,v);
        }
      return cdf_;
     }
double            Conditional_Probability(double u, double v, double _eps = 1.e-5)
     {
      u = fmin(fmax(_eps,u),1. - _eps);
      v = fmin(fmax(_eps,v),1. - _eps);
      vector result_ = vector::Zeros(m_weights.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
         result_[i] = m_copulas[i]?m_weights[i]*m_copulas[i].Conditional_Probability(u,v,false):0.0;
      return result_.Sum();
     }

vector            Conditional_Probability(vector& u, vector& v, double _eps = 1.e-5)
     {
      vector result_ = vector::Zeros(u.Size());
      for(uint i = 0; i<m_copulas.Size(); ++i)
        {
         if(!m_copulas[i])
            continue;
         m_copulas[i].Set_eps(_eps);
         result_+=m_weights[i]*m_copulas[i].Conditional_Probability(u,v);
        }
      return result_;
     }

Метод Sample() используется для генерации синтетических точек данных из подогнанной модели смешанной копулы.

matrix            Sample(ulong num)
     {
      vector r = np::arange(m_weights.Size());
      vector cop_identities = np::choice(r,m_weights,num);
      matrix sample_pairs = matrix::Zeros(num,2);
      matrix temp;
      for(ulong i = 0; i<cop_identities.Size(); ++i)
        {
         temp = m_copulas[uint(cop_identities[i])].Sample(1);
         sample_pairs.Row(temp.Row(0),i);
        }
      return sample_pairs;
     }

Методы Save() и Load() обеспечивают сохранение модели, позволяя сохранять и загружать состояние обученной модели без повторного обучения.

virtual bool      Save(const int file_handle)
     {
      if(file_handle!=INVALID_HANDLE)
        {
         if(FileWriteLong(file_handle,-1)==sizeof(long))
           {
            if(FileWriteInteger(file_handle,int(m_copulas.Size()),INT_VALUE)!=INT_VALUE)
               return(false);
            if(FileWriteInteger(file_handle,int(m_mixture),INT_VALUE)!=INT_VALUE)
               return(false);
            if(FileWriteLong(file_handle,long(m_weights.Size()))!=sizeof(long))
               return(false);
            if(m_weights.Size())
              {
               for(ulong i = 0; i<m_weights.Size(); ++i)
                  if(FileWriteDouble(file_handle,m_weights[i])!=sizeof(double))
                     return(false);
              }
           }
        }
      else
         return false;

      for(ulong i = 0; i<m_copulas.Size(); ++i)
        {
         if(CheckPointer(m_copulas[i])!=POINTER_INVALID)
           {
            if(!m_copulas[i].Save(file_handle))
               return false;
           }
        }
      return true;
     }
   virtual bool      Load(const int file_handle)
     {
      if(file_handle!=INVALID_HANDLE)
        {
         long ff = FileReadLong(file_handle);
         if(ff == -1)
           {
            int size = FileReadInteger(file_handle,INT_VALUE);
            ENUM_COPULA_MIX mixture = ENUM_COPULA_MIX(FileReadInteger(file_handle,INT_VALUE));
            if(ArraySize(m_copulas)!=size || mixture!=m_mixture)
              {
               Print(__FUNCTION__, " You are attempting to load a model incompatible with this class ");
               return false;
              }
            ulong size_weights = (ulong)FileReadLong(file_handle);
            if(size_weights)
              {
               m_weights = vector::Zeros(size_weights);
               switch(m_mixture)
                 {
                  case CFG_MIX:
                     m_cop_params = vector::Zeros(size_weights);
                     break;
                  case CTG_MIX:
                     m_cop_params = vector::Zeros(size_weights+1);
                     break;
                 }
               for(ulong i = 0; i<size_weights; ++i)
                  m_weights[i] = FileReadDouble(file_handle);
              }
           }
         else
            return false;
        }
      else
         return false;
      //---
      for(ulong i = 0,k = 0; i<m_copulas.Size(); ++i)
        {
         m_copulas[i] = bivariate_copula(file_handle);
         if(m_copulas[i]==NULL)
           {
            Print(__FUNCTION__, " failed to load a bivariate copula from file ", GetLastError());
            return false;
           }
         if(m_weights.Size())
           {
            switch(ENUM_COPULA_TYPE(m_copulas[i].Type()))
              {
               case CLAYTON_COPULA:
               case GUMBEL_COPULA:
               case JOE_COPULA:
               case N13_COPULA:
               case N14_COPULA:
               case FRANK_COPULA:
                  m_cop_params[k++] = m_copulas[i].Get_theta();
                  break;
               case STUDENT_COPULA:
                  m_cop_params[k++] = m_copulas[i].Get_nu();
                  m_cop_params[k++] = m_copulas[i].Get_rho();
                  break;
              }
           }
        }
      return true;
     }

Наконец, класс определяет два виртуальных метода, предназначенных для переопределения. Метод Fit() должен реализовывать логику оценки параметров копулы по выборке псевдонаблюдений путем минимизации функции правдоподобия, заданной методом Penalized_Log_Likelihood(), с учетом штрафа SCAD.

virtual double    Fit(matrix& qdata, ulong maxiter = 25, double gamma_scad = 0.0, double a_scad = 0.0,double weight_margin=1.e-2)
     {
      return EMPTY_VALUE;
     }

virtual double    Penalized_Log_Likelihood(matrix &data,double g_scad, double a_scad)
     {
      return EMPTY_VALUE;
     }

Также в mixed.mqh определено перечисление ENUM_COPULA_MIX, которое предоставляет список реализованных типов смешанных копул для удобного выбора.

//+------------------------------------------------------------------+
//| mixed copula type                                                |
//+------------------------------------------------------------------+
enum ENUM_COPULA_MIX
  {
   CFG_MIX=0,//Clayton-Frank-Gumbel
   CFJ_MIX,//Clayton-Frank-Joe
   CTG_MIX,//Clayton-Student-Gumbel
   JFG_MIX//Joe-Frank-Gumbel
  };

Класс CFGMixCop представляет смешанную копулу Клейтона-Франка-Гумбеля. Он содержит параметрический конструктор для случаев, когда пользователь предпочитает задавать параметры модели явно, а не подгонять их по данным.

public:
                     CFGMixCop(void)
     {
      m_mixture = CFG_MIX;
      m_num_cops = 3;
      ArrayResize(m_copulas,int(m_num_cops));
      for(uint i = 0; i<m_num_cops; ++i)
         m_copulas[i] = NULL;
     }

                     CFGMixCop(vector &params, vector& weights)
     {
      m_mixture = CFG_MIX;
      m_num_cops = 3;
      ArrayResize(m_copulas,int(m_num_cops));
      if(params.Size()<3 || weights.Size()<3)
         Print(__FUNCTION__, " invalid parameters ");
      else
        {
         m_weights = weights;
         m_cop_params = params;
         for(ulong i = 0; i<3; ++i)
           {
            if(!i)
               m_copulas[i] = new CClayton();
            else
               if(i==1)
                  m_copulas[i] = new CFrank();
               else
                  m_copulas[i] = new CGumbel();
            m_copulas[i].Set_theta(m_cop_params[i]);
           }
        }
     }

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

  1. параметр Клейтона
  2. параметр Франка
  3. параметр Гумбеля

Аналогично, вызов любых методов доступа возвращает значения в этом постоянном порядке, обеспечивая предсказуемую обработку данных во всей модели. Метод Fit() отвечает за оценку модели по данным. 

virtual double            Fit(matrix& qdata, ulong maxiter = 50, double gamma_scad = 0.0,double a_scad = 0.0, double weight_margin = 1.e-2) override
     {
      if(CheckPointer(m_copulas[0])==POINTER_INVALID)
         m_copulas[0] = new CClayton();
      if(CheckPointer(m_copulas[1])==POINTER_INVALID)
         m_copulas[1] = new CFrank();
      if(CheckPointer(m_copulas[2])==POINTER_INVALID)
         m_copulas[2] = new CGumbel();

      matrix wc = _fit_quantile_em(qdata,maxiter,gamma_scad,a_scad);
      if(!wc.Rows())
         return EMPTY_VALUE;
      wc.Row(adjust_weights(wc.Row(0),weight_margin),0);
      m_weights = wc.Row(0);
      m_cop_params = wc.Row(1);

      m_copulas[0].Set_theta(m_cop_params[0]);
      m_copulas[1].Set_theta(m_cop_params[1]);
      m_copulas[2].Set_theta(m_cop_params[2]);

      vector u1 = qdata.Col(0);
      vector u2 = qdata.Col(1);

      return _ml_qfunction(u1,u2,wc.Row(1),wc.Row(0),gamma_scad,a_scad,false);
     }  
  • Входной параметр qdata представляет собой матрицу псевдонаблюдений (минимум 2 столбца), то есть исходных значений, преобразованных к интервалу [0,1].
  • Входной параметр функции maxiter — это целое число, задающее максимальное число итераций алгоритма EM.
  • Входные параметры gamma_scad и a_scad имеют тип double и представляют гиперпараметры SCAD.
  • Входной параметр weight_margin — это пороговое значение: веса, опускающиеся ниже этого предела, усекаются до нуля для обеспечения разреженности.

При успешном выполнении Fit() возвращает нештрафованное логарифмическое правдоподобие; в противном случае возвращается EMPTY_VALUE. Основная логика оценки заключена в защищенном методе _fit_quantile_em(), который управляет итерационным процессом.

matrix            _fit_quantile_em(matrix &qd, ulong max_iter,double gamma_scad, double a_scad)
     {
      vector init_weights = {0.33, 0.33, 1. - 0.33 - 0.33};
      vector init_cop_params = {3,4,5};
      vector weights = _expectation_step(qd,gamma_scad,a_scad,init_cop_params,init_weights);
      if(!weights.Size())
         return matrix::Zeros(0,0);
      vector cop_params = _maximization_step(qd,gamma_scad,a_scad,init_cop_params,weights);
      if(!cop_params.Size())
         return matrix::Zeros(0,0);
      vector oldparams,newparams;
      oldparams = newparams = vector::Zeros(6);

      np::vectorCopy(oldparams,init_weights,0,3);
      np::vectorCopy(oldparams,init_cop_params,3);

      np::vectorCopy(newparams,weights,0,3);
      np::vectorCopy(newparams,cop_params,3);

      double ll_diff = MathAbs(oldparams-newparams).Sum();
      ulong i = 1;
      while(i<max_iter && ll_diff>(5.*1.e-2))
        {
         np::vectorCopy(oldparams,weights,0,3);
         np::vectorCopy(oldparams,cop_params,3);

         weights = _expectation_step(qd,gamma_scad,a_scad,cop_params,weights);
         if(!weights.Size())
           {
            Print(__FUNCTION__, " invalid weights ", i);
            return matrix::Zeros(0,0);
           }

         cop_params = _maximization_step(qd,gamma_scad,a_scad,cop_params,weights);
         if(!cop_params.Size())
           {
            Print(__FUNCTION__, " failed convergence at iteration ", i);
            return matrix::Zeros(0,0);
           }

         np::vectorCopy(newparams,weights,0,3);
         np::vectorCopy(newparams,cop_params,3);

         ll_diff = MathAbs(oldparams-newparams).Sum();
         i+=1;
        }

      matrix out = matrix::Zeros(3,3);
      out.Row(weights,0);
      out.Row(cop_params,1);
      return out;
     }
Защищенный метод _expectation_step() реализует этап ожидания алгоритма EM. Этот метод работает в двух разных режимах в зависимости от переданных значений параметров SCAD. Если оба параметра SCAD допустимы (параметр разреженности >0.0 и параметр смещения >2.0), используется пенализованное/штрафное правдоподобие, что позволяет модели выполнять автоматический отбор переменных, сдвигая незначимые веса к нулю. Напротив, если любой из параметров SCAD недопустим, штраф SCAD отключается, и метод по умолчанию использует стандартную оценку максимального правдоподобия (MLE).
vector            _expectation_step(matrix &qd,double gamma_scad, double a_scad,vector& cop_params,vector &weights)
     {
      //---
      ulong num = qd.Rows();
      vector u1 = qd.Col(0);
      vector u2 = qd.Col(1);
      //---
      double dif = 1;
      double tol_weight = 1.e-2;
      long iteration = 0;
      //---
      for(ulong i = 0; i<3; ++i)
         m_copulas[i].Set_theta(cop_params[i]);
      //---
      vector nweights;
      //---
      if(gamma_scad>0.0 && a_scad>2.0)
        {
         while(dif>tol_weight && iteration<10)
           {
            nweights = vector::Zeros(3);
            nweights.Fill(double("nan"));
            iteration+=1;
            for(ulong i = 0; i<3; ++i)
              {
               vector sum_ml_1st = vector::Zeros(u1.Size());
               double sum,sum_ml,numerator,denominator;
               sum = sum_ml = numerator = denominator = 1.e-12;
               for(ulong t = 0; t<num; ++t)
                 {
                  sum = 1.e-12;
                  for(ulong j = 0; j<3; ++j)
                     sum+=weights[j]*m_copulas[j].Copula_PDF(u1[t],u2[t],true,true);
                  sum_ml_1st[t] = weights[i]*m_copulas[i].Copula_PDF(u1[t],u2[t],true,true)/sum;
                 }
               sum_ml = sum_ml_1st.Sum();
               numerator = weights[i]*scad_derivative(weights[i],gamma_scad,a_scad)-sum_ml/double(num);
               for(ulong j = 0; j<3; ++j)
                  denominator+=weights[j]*scad_derivative(weights[j],gamma_scad,a_scad);
               denominator-=1.;
               nweights[i] = fabs(numerator/denominator);
              }
            dif = MathAbs(weights-nweights).Sum();
            weights = nweights;
           }
        }
      else
        {
         matrix wd = matrix::Zeros(weights.Size(),num);
         vector td, temp;
         while(dif>tol_weight && iteration<10)
           {
            nweights = vector::Zeros(weights.Size());
            iteration+=1;

            for(ulong i = 0; i<wd.Rows(); ++i)
               if(!wd.Row(weights[i]*m_copulas[i].Copula_PDF(u1,u2,true,true),i))
                 {
                  Print(__FUNCTION__, " row insertion error ", GetLastError());
                  return vector::Zeros(0);
                 }
            td = wd.Sum(0);
            td.Clip(1.e-12,DBL_MAX);
            for(ulong i = 0; i<weights.Size(); ++i)
              {
               temp = (wd.Row(i)/td);
               nweights[i] = fabs(temp.Sum()/double(num));
              }
            dif = MathAbs(weights-nweights).Sum();
            weights = nweights;
           }
        }
      //---
      return weights;
     }

Шаг максимизации определен как метод _maximization_step().

vector            _maximization_step(matrix&qd, double gamma_scad, double a_scad, vector& cop_params, vector& weights)
     {
      vector u1 = qd.Col(0);
      vector u2 = qd.Col(1);

      double eps = 1.e-3;

      double _x_[3];
      for(uint i = 0; i<_x_.Size(); _x_[i] = cop_params[i], ++i);

      double bndl[3] = {-1.,-50.,1.};
      double bndu[3] = {100.,50.,100.};

      CObject             obj;
      CNDimensional_Func1 ffunc;
      CNDimensional_Rep   frep;

      ffunc.set_params(this,u1,u2,weights,gamma_scad,a_scad,bool(gamma_scad>0.0&&a_scad>2.0),-1);

      CMinBLEICStateShell state;

      CMinBLEICReportShell rep;

      double epsg=eps;

      double epsf=0;

      double epsx=0;

      double epso=eps;

      double epsi=eps;

      double diffstep=1.0e-6;

      CAlglib::MinBLEICCreateF(_x_,diffstep,state);

      CAlglib::MinBLEICSetBC(state,bndl,bndu);

      CAlglib::MinBLEICSetInnerCond(state,epsg,epsf,epsx);

      CAlglib::MinBLEICSetOuterCond(state,epso,epsi);

      CAlglib::MinBLEICOptimize(state,ffunc,frep,false,obj);

      CAlglib::MinBLEICResults(state,_x_,rep);

      vector out = vector::Zeros(0);

      int termination_reason = rep.GetTerminationType();

      if(termination_reason<0)
        {
         Print(__FUNCTION__, " termination reason ", termination_reason);
         return out;
        }

      out.Assign(_x_);

      return out;
     }

На этом шаге используется алгоритм минимизации ALGLIB Boundary, Linear Equality-Inequality Constraints (BLEIC). Минимизатор обращается к методу _ml_qfunc(), который задает штрафованное логарифмическое правдоподобие. Доступ к нему открыт через публичный метод objective(), что позволяет экземпляру оптимизатора ALGLIB обращаться к функции во время выполнения.

double                   objective(vector& x,vector& u1, vector& u2,vector& weights, double gamma_scad, double a_scad, bool if_penalty = true, double multiplier= 1.)
     {
      return  _ml_qfunction(u1,u2,x,weights,gamma_scad,a_scad,if_penalty,multiplier);
     }

Метод Fit() запускает процедуру минимизации. Характер минимизируемой целевой функции зависит от параметров SCAD, переданных в метод. Если любой из параметров недопустим ( gamma_scad <= 0.0 или a_scad <= 2.0 ), целевая функция по умолчанию становится стандартной оценкой максимального правдоподобия (MLE) без штрафа. В противном случае целевой функцией является штрафованная MLE.

Класс CTGMixCop, реализующий смешанную копулу Клейтона-Стьюдента-t-Гумбеля, имеет почти тот же дизайн, что и класс CFGMixCop. Основное отличие заключается в замене копулы Франка копулой Стьюдента-t.

public:
                     CTGMixCop(void)
     {
      m_mixture = CTG_MIX;
      m_num_cops = 3;
      ArrayResize(m_copulas,int(m_num_cops));
      for(uint i = 0; i<m_num_cops; ++i)
         m_copulas[i] = NULL;
     }
                     CTGMixCop(vector& params, vector& weights)
     {
      m_mixture = CTG_MIX;
      m_num_cops = 3;
      ArrayResize(m_copulas,int(m_num_cops));
      if(params.Size()<4 || weights.Size()<3)
         Print(__FUNCTION__, " invalid parameters ");
      else
        {
         m_weights = weights;
         m_cop_params = params;
         for(ulong i = 0; i<3; ++i)
           {
            if(i == 0 || i == 2)
              {
               if(!i)
                 {
                  m_copulas[i] = new CClayton();
                  m_copulas[i].Set_theta(m_cop_params[0]);
                 }
               else
                 {
                  m_copulas[i] = new CGumbel();
                  m_copulas[i].Set_theta(m_cop_params[3]);
                 }
              }
            else
              {
               m_copulas[i] = new CStudent();
               matrix corr = {{1.,m_cop_params[1]},{m_cop_params[1],1.}};
               m_copulas[i].Set_covariance(corr);
               m_copulas[i].Set_nu(m_cop_params[2]);
              }
           }
        }
     }

Также в заголовочном файле mixed.mqh определена специализированная процедура настройки гиперпараметров SCAD. Функция tune_scad_parameters() реализует решетчатый поиск для оптимизации параметров регуляризации.

//+------------------------------------------------------------------+
//|Implements the 5-fold cross-validation tuning process of the SCAD |
//|parameters                                                        |
//+------------------------------------------------------------------+
bool tune_scad_parameters(matrix &data, double gama_start, double gama_stop, ulong gamma_count, double a_start, double a_stop,ulong a_count, ENUM_COPULA_MIX mix, bool shuffle, int seed, bool show_details, double& out_gamma, double& out_a)
  {
   np::Folds folds[];
   if(!np::kfold(data.Rows(),folds,5,shuffle,seed))
      return false;

   CMixedCopula* mixedcop = NULL;

   switch(mix)
     {
      case CFG_MIX:
         mixedcop = new CFGMixCop();
         break;
      case CTG_MIX:
         mixedcop = new CTGMixCop();
         break;
      case CFJ_MIX:
         mixedcop = new CFJMixCop();
         break;
      case JFG_MIX:
         mixedcop = new JFGMixCop();
         break;
     }

   if(CheckPointer(mixedcop) == POINTER_INVALID)
     {
      Print(__FUNCTION__, " failed to initialize ", EnumToString(mix), " copula ");
      return false;
     }

   matrix traindata,testdata;
   double ll, best_score;
   vector scores = vector::Zeros(folds.Size());
   vector gamma_grid = np::linspace(gama_start,gama_stop,gamma_count);
   vector a_grid = np::linspace(a_start,a_stop,a_count);
   best_score = -DBL_MAX;
   bool use_mle = false;
   double mle = 0;
   double p_mle = 0;
   for(ulong i = 0; i<gamma_grid.Size(); ++i)
     {
      for(ulong j = 0; j<a_grid.Size(); ++j)
        {
         for(uint k = 0; k<folds.Size() && !IsStopped(); ++k)
           {
            traindata = np::selectMatrixRows(data,folds[k].train_indices);
            testdata = np::selectMatrixRows(data,folds[k].test_indices);
            ll = mixedcop.Fit(traindata,25,gamma_grid[i],a_grid[j]);

            if(ll == EMPTY_VALUE)
              {
               Print(__FUNCTION__, " error fitting data ", "| sparcity ", gamma_grid[i], " bias ", a_grid[j]);
               continue;
              }

            scores[k] = mixedcop.Penalized_Log_Likelihood(testdata,gamma_grid[i],a_grid[j]);

           }
         ll = scores.Sum();
         if(show_details)
            Print(__FUNCTION__, "| sparcity: ", gamma_grid[i], "| bias: ", a_grid[j], "| likelihood  ", ll, " \nweights ", mixedcop.Weights(), "| params ", mixedcop.Params());
         if(ll>best_score)
           {
            best_score = ll;
            out_a = a_grid[j];
            out_gamma = gamma_grid[i];
           }
        }
     }

   delete mixedcop;
   return true;
  }

Она принимает следующие входные параметры.

  • Вход data — это матрица псевдонаблюдений, где каждый столбец представляет ряд.
  • Затем функция задает шесть входных переменных: первые три имеют префикс gamma, а последние три — префикс a. Они определяют границы и детализацию решетчатого поиска оптимальной пары гиперпараметров SCAD. Аргументы с суффиксом count задают число кандидатных значений, которые нужно оценить в диапазонах, определяемых аргументами с суффиксами start и stop соответственно.
  • Затем функция задает шесть входных переменных: первые три имеют префикс gamma, а последние три — префикс a. Они определяют границы и детализацию решетчатого поиска оптимальной пары гиперпараметров SCAD. Аргументы с суффиксом count задают число кандидатных значений, которые нужно оценить в диапазонах, определяемых аргументами с суффиксами start и stop соответственно.
  • Вход mix — это перечисление, указывающее, какую модель смешанной копулы оценивать во время поиска.
  • Вход shuffle — логический аргумент, определяющий, следует ли перемешивать данные перед разделением, а вход seed — начальное значение для генератора псевдослучайных чисел, обеспечивающее воспроизводимость результатов.
  • Логический флаг show_details задает, следует ли выводить подробности выполнения каждой итерации в журнал терминала.
  • Ссылочные входы out_a и out_gamma являются выходными параметрами, в которых сохраняются оптимальные параметры SCAD, найденные процедурой.

Штрафная функция SCAD и ее производная определены в utils.mqh. Они реализуют механизм сжатия, позволяя модели различать значимые и пренебрежимо малые веса копул.

//+------------------------------------------------------------------+
//|SCAD (smoothly clipped absolute deviation) penalty function.      |
//+------------------------------------------------------------------+
double scad_penalty(double x, double gamma, double a)
  {
   bool is_linear = (fabs(x)<=gamma);
   bool is_quadratic = (gamma<fabs(x)) & (fabs(x)<=a*gamma);
   bool is_constant = ((a*gamma)<fabs(x));

   double linear_part = gamma * fabs(x) * double(is_linear);
   double quadratic_part = (2.*a*gamma*fabs(x) - pow(x,2.)- pow(gamma,2.))/(2.*(a-1.))*double(is_quadratic);
   double constant_part = (pow(gamma,2.)*(a+1.))/2.*(double(is_constant));

//Print(__FUNCTION__, " linear part ", linear_part,"\n quadratic part ", quadratic_part, "\n constant part ", constant_part);

   return linear_part+quadratic_part+constant_part;
  }
//+------------------------------------------------------------------+
//|The derivative of SCAD penalty function w.r.t x.                  |
//+------------------------------------------------------------------+
double scad_derivative(double x, double gamma, double a)
  {
   double p1 = gamma*double(x<=gamma);
   double p2 = gamma *(a*gamma-x)*double((a*gamma-x)>0.)/((a - 1.)*gamma)*double(x>gamma);
   return p1+p2;
  }

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


Настройка гиперпараметров SCAD

В контексте штрафа SCAD параметры настройки λ (разреженность) и a (снижение смещения) управляют соответственно порогом разреженности и скоростью снижения смещения. Хотя оптимальные значения в конечном счете определяются данными, в академической литературе и практике используются устоявшиеся диапазоны. Параметр снижения смещения управляет тем, насколько быстро штраф выходит на плато. При настройке параметра смещения следует рассматривать только значения больше двух; параметр должен быть строго больше двух, чтобы функция сохраняла свойства пороговой обработки, отличающиеся от жесткого порогового отсечения.

Параметр разреженности, с другой стороны, чувствителен к масштабу обучающих данных и размеру выборки. Он задает точку отсечения, ниже которой коэффициенты сдвигаются к нулю. Диапазон значений, рассматриваемых при настройке этого параметра, обычно находится от 0 до 1. Нижняя граница должна быть достаточно малой, чтобы позволить модели приблизиться к нештрафованной оценке максимального правдоподобия (MLE), например 0.001. Напротив, верхняя граница должна быть достаточно большой, чтобы потенциально занулить все коэффициенты (веса, применяемые к компонентным копулам в смеси). Как правило, больший размер выборки требует меньшего параметра разреженности.

Когда параметр разреженности высок, модель становится разреженной, в результате чего многие веса смеси становятся нулевыми. Когда параметр снижения смещения высок, штраф действует дольше, из-за чего переход от штрафованной оценки к несмещенной происходит гораздо медленнее. Чем ближе параметр снижения смещения к 2, тем быстрее штраф уменьшается.

Чтобы проверить эти идеи на практике, скрипт Scad_CV.mq5 демонстрирует работу функции tune_scad_parameters().

//+------------------------------------------------------------------+
//|                                                      Scad_CV.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<Copulas\Bivariate\mixed.mqh>
#include<ECDF\linear_cdf.mqh>

input string   FirstSymbol="XAUUSD";
input string   SecondSymbol="XAUEUR";
input datetime TrainingDataStart=D'2025.01.01';
input ENUM_TIMEFRAMES TimeFrame = PERIOD_D1;
input ENUM_COPULA_MIX CopulaType = CTG_MIX;
input ulong    HistorySize=1200;
input bool     Show_details = true;
input bool     Shuffle_Data = false;
input int      Random_Seed = 0;
input double   g_from = 0.001;
input double   g_to = 1.0;
input ulong    g_count = 5;
input double   a_from = 2.0;
input double   a_to = 10.0;
input ulong    a_count_ = 5; 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
  //---
   vector p_1,p_2,p_3,p_n;
   matrix pdata,pobs;
   if(!p_1.CopyRates(FirstSymbol,TimeFrame,COPY_RATES_CLOSE,TrainingDataStart,HistorySize) ||
      !p_2.CopyRates(SecondSymbol,TimeFrame,COPY_RATES_CLOSE,TrainingDataStart,HistorySize) ||
      p_1.Size()!=p_2.Size() ||
      !pdata.Resize(p_1.Size(),2) ||
      !pdata.Col(p_1,0) ||
      !pdata.Col(p_2,1))
     {
      Print(" failed to collect and initialize rates matrix ", GetLastError());
      return;
     }
//---
   CLinearCDF qt();
   if(!qt.fit(pdata))
      return;
//---
   pobs = qt.to_quantile(pdata);
   
    double gamma_scad,a_scad;
   if(!tune_scad_parameters(pobs,g_from,g_to,g_count,a_from,a_to,a_count_,CopulaType,Shuffle_Data,Random_Seed,Show_details,gamma_scad,a_scad))
    {
     Print(" failed ");
     return;
    }
   
   Print(EnumToString(CopulaType)," sparcity -> ", gamma_scad, " || bias -> ", a_scad);
  }
//+------------------------------------------------------------------+

Настраиваемые пользователем входные параметры скрипта Scad_CV.ex5 по умолчанию задают довольно узкую сетку пробных значений разреженности и смещения. Такая конфигурация обеспечивает эффективное выполнение скрипта на выборках размером чуть больше одной тысячи наблюдений. Обратите внимание, что пробная сетка включает значение параметра смещения 2.0; поскольку это недопустимое значение для параметра смещения, оно полностью отключает штраф SCAD. Это позволяет определить, дает ли минимизация нештрафованного правдоподобия лучшие результаты. Запуск скрипта с выбранной смесью Клейтона-Франка-Гумбеля дает следующий вывод.

PE      0       07:19:59.780    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 2.0| likelihood  584.3449797418596 
NH      0       07:19:59.780    Scad_CV (XAUUSD,D1)     weights [0,0.2503264642029614,0.7496735357960386]| params [0.07777184570809823,4.98716120261952,2.938523462235503]
JG      0       07:20:04.549    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 4.0| likelihood  584.3388831186738 
GI      0       07:20:04.549    Scad_CV (XAUUSD,D1)     weights [0,0.2503330189828707,0.7496669810161294]| params [0.07783293123073377,4.987168638354295,2.938531600897639]
NJ      0       07:20:09.278    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 6.0| likelihood  584.5057912775926 
LF      0       07:20:09.278    Scad_CV (XAUUSD,D1)     weights [0,0.2475903622229043,0.7524096377760957]| params [0.08186119438950079,4.982135308682525,2.934272675707406]
OL      0       07:20:13.997    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 8.0| likelihood  584.5035349829863 
ED      0       07:20:13.997    Scad_CV (XAUUSD,D1)     weights [0,0.2475907179493826,0.7524092820496174]| params [0.08234560301866138,4.982135272704732,2.934273169230999]
JL      0       07:20:18.700    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 10.0| likelihood  584.5011307062207 
ER      0       07:20:18.700    Scad_CV (XAUUSD,D1)     weights [0,0.2475914144776672,0.7524085855213327]| params [0.08233571375095404,4.98213630991471,2.934274176680373]
FQ      0       07:20:23.187    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 2.0| likelihood  584.3449797418596 
PL      0       07:20:23.187    Scad_CV (XAUUSD,D1)     weights [0,0.2503264642029614,0.7496735357960386]| params [0.07777184570809823,4.98716120261952,2.938523462235503]
ES      0       07:20:27.501    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 4.0| likelihood  283.7391184805832 
RM      0       07:20:27.501    Scad_CV (XAUUSD,D1)     weights [0,0.9999999999989904,0]| params [0.1511818127315188,7.344139133115162,20.25573962738841]
ID      0       07:20:31.999    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 6.0| likelihood  5659.479627674678 
JO      0       07:20:31.999    Scad_CV (XAUUSD,D1)     weights [0,0.5844120297582153,0.4155879702407769]| params [0.1376209847408496,5.640552910020598,3.811839085505817]
JF      0       07:20:38.263    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 8.0| likelihood  334.8224130043968 
KJ      0       07:20:38.263    Scad_CV (XAUUSD,D1)     weights [0,0.02895916433928,0.97104083565972]| params [0.1037932306526668,4.669895137807593,2.641209026360089]
OK      0       07:20:42.346    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 10.0| likelihood  6420.623549439398 
ND      0       07:20:42.346    Scad_CV (XAUUSD,D1)     weights [0,0.04002009450317316,0.9599799054958269]| params [0.1193049328638911,4.63258676700795,2.654870180909923]
DH      0       07:20:46.831    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 2.0| likelihood  584.3449797418596 
KF      0       07:20:46.831    Scad_CV (XAUUSD,D1)     weights [0,0.2503264642029614,0.7496735357960386]| params [0.07777184570809823,4.98716120261952,2.938523462235503]
FN      0       07:20:49.490    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 4.0| likelihood  34.55576102335817 
KR      0       07:20:49.490    Scad_CV (XAUUSD,D1)     weights [0,0,0.9999999999989903]| params [0.1197956723049384,4.584548412565545,2.619284560176268]
NN      0       07:20:52.885    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 6.0| likelihood  15.363216317653269 
HR      0       07:20:52.885    Scad_CV (XAUUSD,D1)     weights [0,0.01784571505390212,0.9821542849450979]| params [0.1043980264613316,4.628446940510745,2.628580283819081]
QS      0       07:20:56.830    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 8.0| likelihood  6102.473148256049 
RP      0       07:20:56.830    Scad_CV (XAUUSD,D1)     weights [0,0.02673340177232474,0.9732665982266753]| params [0.1037622447823746,4.698345592025231,2.638332966096248]
IS      0       07:21:00.708    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 10.0| likelihood  3.301592693093255 
RM      0       07:21:00.708    Scad_CV (XAUUSD,D1)     weights [0,0.0300952343044497,0.9699047656945503]| params [0.1029115313643473,4.61667495323261,2.643127744938425]
KE      0       07:21:05.196    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 2.0| likelihood  584.3449797418596 
QH      0       07:21:05.196    Scad_CV (XAUUSD,D1)     weights [0,0.2503264642029614,0.7496735357960386]| params [0.07777184570809823,4.98716120261952,2.938523462235503]
JI      0       07:21:08.606    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 4.0| likelihood  -297.39396856892415 
GH      0       07:21:08.606    Scad_CV (XAUUSD,D1)     weights [0,0.01107956745287137,0.9889204325461286]| params [2.9984906563128,4.76765198814707,2.620207254516393]
KH      0       07:21:12.649    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 6.0| likelihood  -303.8281831341336 
CE      0       07:21:12.649    Scad_CV (XAUUSD,D1)     weights [0,0.01742743973978789,0.9825725602592119]| params [2.9984906563128,4.637005162809593,2.628040342667032]
FO      0       07:21:18.050    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 8.0| likelihood  5512.03076596105 
DG      0       07:21:18.050    Scad_CV (XAUUSD,D1)     weights [0,0.02364115960349121,0.9763588403955088]| params [2.9984906563128,4.667363798628771,2.635026851516195]
PM      0       07:21:23.276    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 10.0| likelihood  5953.875750743017 
OR      0       07:21:23.276    Scad_CV (XAUUSD,D1)     weights [0,0.03021524144755516,0.9697847585514447]| params [2.9984906563128,4.626181869637053,2.643168795782705]
KQ      0       07:21:27.767    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 2.0| likelihood  584.3449797418596 
ML      0       07:21:27.767    Scad_CV (XAUUSD,D1)     weights [0,0.2503264642029614,0.7496735357960386]| params [0.07777184570809823,4.98716120261952,2.938523462235503]
FQ      0       07:21:31.781    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 4.0| likelihood  -727.4266957539727 
GN      0       07:21:31.781    Scad_CV (XAUUSD,D1)     weights [0.4409776856415021,0,0.5590223143574978]| params [0.2842084359703793,8.160381312758863,3.894704689012704]
JG      0       07:21:35.806    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 6.0| likelihood  -727.4266957539727 
IH      0       07:21:35.806    Scad_CV (XAUUSD,D1)     weights [0.4409776856415021,0,0.5590223143574978]| params [0.2842084359703793,8.160381312758863,3.894704689012704]
JE      0       07:21:39.839    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 8.0| likelihood  -727.4266957539727 
GJ      0       07:21:39.839    Scad_CV (XAUUSD,D1)     weights [0.4409776856415021,0,0.5590223143574978]| params [0.2842084359703793,8.160381312758863,3.894704689012704]
MH      0       07:21:43.911    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 10.0| likelihood  -727.4266957539727 
QD      0       07:21:43.911    Scad_CV (XAUUSD,D1)     weights [0.4409776856415021,0,0.5590223143574978]| params [0.2842084359703793,8.160381312758863,3.894704689012704]
DK      0       07:21:43.911    Scad_CV (XAUUSD,D1)     CFG_MIX sparcity -> 0.25075 || bias -> 10.0

Результаты показывают, что оптимальные параметры SCAD для смеси Клейтона-Франка-Гумбеля — разреженность 0.25075 и смещение 10. Следующий набор результатов для смеси Клейтона-Стьюдента-Гумбеля особенно показателен.

JF      0       07:23:09.254    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 2.0| likelihood  599.6351926572415 
HK      0       07:23:09.254    Scad_CV (XAUUSD,D1)     weights [0,0.03267640297340715,0.9673235970255923]| params [0.026704520400007,0.5822697866462155,7,2.635263431033769]
NH      0       07:23:31.432    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 4.0| likelihood  599.6274718728072 
FI      0       07:23:31.433    Scad_CV (XAUUSD,D1)     weights [0,0.03267798343975953,0.96732201655924]| params [0.02669592147577486,0.5822648258079013,7,2.6352494228926]
FK      0       07:23:53.828    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 6.0| likelihood  599.6244749857701 
GD      0       07:23:53.828    Scad_CV (XAUUSD,D1)     weights [0,0.03267834828227598,0.9673216517167235]| params [0.02669671670147136,0.5822637634505266,7,2.635245690184943]
CN      0       07:24:16.075    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 8.0| likelihood  599.6215815910193 
ES      0       07:24:16.075    Scad_CV (XAUUSD,D1)     weights [0,0.03267864853568959,0.9673213514633099]| params [0.02669727070086427,0.5822626295795441,7,2.635242405736884]
ER      0       07:24:38.223    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.001| bias: 10.0| likelihood  599.61866242213 
MQ      0       07:24:38.223    Scad_CV (XAUUSD,D1)     weights [0,0.03267912346489563,0.9673208765341038]| params [0.02669711773038876,0.5822617798491028,7,2.635240094669517]
ES      0       07:24:59.495    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 2.0| likelihood  599.6351926572415 
KM      0       07:24:59.495    Scad_CV (XAUUSD,D1)     weights [0,0.03267640297340715,0.9673235970255923]| params [0.026704520400007,0.5822697866462155,7,2.635263431033769]
EE      0       07:25:12.505    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 4.0| likelihood  468.644205273507 
GK      0       07:25:12.505    Scad_CV (XAUUSD,D1)     weights [0,0.01620981928802398,0.983790180710976]| params [2.862242105331717,0.9975906244136006,7,2.566020598217]
PH      0       07:26:18.429    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 6.0| likelihood  359.9685977593964 
JD      0       07:26:18.429    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [0.02596060043458071,0.5877560061777306,7,2.60814677938215]
GJ      0       07:26:35.438    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 8.0| likelihood  341.04401153470667 
EE      0       07:26:35.438    Scad_CV (XAUUSD,D1)     weights [0,0.0101303301858873,0.9898696698131126]| params [0.03276229272869093,0.5873194208156135,7,2.6159294869276]
PL      0       07:26:50.782    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.25075| bias: 10.0| likelihood  331.81315199928497 
OE      0       07:26:50.782    Scad_CV (XAUUSD,D1)     weights [0,0.01344107163199763,0.9865589283670023]| params [0.01828802392046799,0.5853696555031691,7,2.618615230362836]
DL      0       07:27:12.118    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 2.0| likelihood  599.6351926572415 
OP      0       07:27:12.118    Scad_CV (XAUUSD,D1)     weights [0,0.03267640297340715,0.9673235970255923]| params [0.026704520400007,0.5822697866462155,7,2.635263431033769]
NR      0       07:28:58.374    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 4.0| likelihood  39.715438762939925 
NM      0       07:28:58.374    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.885268158668136,0.5815643981885116,7,2.60814677938215]
MR      0       07:29:14.021    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 6.0| likelihood  15.766736485645538 
PM      0       07:29:14.021    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.886114952573396,0.5885681475003665,7,2.60814677938215]
FR      0       07:31:20.013    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 8.0| likelihood  12.582743928163211 
KN      0       07:31:20.013    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.886068065102342,0.588279448169273,7,2.60814677938215]
GR      0       07:33:38.602    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.5005| bias: 10.0| likelihood  9.373323825700936 
HM      0       07:33:38.602    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.885905528406098,0.5876282323959312,7,2.60814677938215]
LR      0       07:33:59.933    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 2.0| likelihood  599.6351926572415 
NO      0       07:33:59.933    Scad_CV (XAUUSD,D1)     weights [0,0.03267640297340715,0.9673235970255923]| params [0.026704520400007,0.5822697866462155,7,2.635263431033769]
PF      0       07:34:12.979    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 4.0| likelihood  -342.3070420888405 
GK      0       07:34:12.979    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.999235107389794,0.5744384247930826,7,2.608146780258969]
MG      0       07:34:25.802    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 6.0| likelihood  -313.5880991228416 
NK      0       07:34:25.802    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.999235107389794,0.5712426547670767,7,2.608146780258969]
JG      0       07:34:38.627    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 8.0| likelihood  -315.3277234364691 
GK      0       07:34:38.627    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.999235107389794,0.5700039115725434,7,2.608146780258969]
PG      0       07:34:51.411    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 0.75025| bias: 10.0| likelihood  -316.34493893784384 
OJ      0       07:34:51.411    Scad_CV (XAUUSD,D1)     weights [0,0,0.999999999999]| params [2.999235107389794,0.5700489107543522,7,2.608146780258969]
RF      0       07:35:12.660    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 2.0| likelihood  599.6351926572415 
DJ      0       07:35:12.660    Scad_CV (XAUUSD,D1)     weights [0,0.03267640297340715,0.9673235970255923]| params [0.026704520400007,0.5822697866462155,7,2.635263431033769]
CH      0       07:35:14.020    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 4.0| likelihood  689.1665208571073 
DH      0       07:35:14.020    Scad_CV (XAUUSD,D1)     weights [0,0,0]| params [3,0.5,4,2.608146566441139]
DR      0       07:35:15.389    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 6.0| likelihood  689.1665208571073 
EN      0       07:35:15.389    Scad_CV (XAUUSD,D1)     weights [0,0,0]| params [3,0.5,4,2.608146566441139]
KL      0       07:35:16.734    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 8.0| likelihood  689.1665208571073 
PD      0       07:35:16.734    Scad_CV (XAUUSD,D1)     weights [0,0,0]| params [3,0.5,4,2.608146566441139]
CE      0       07:35:18.094    Scad_CV (XAUUSD,D1)     tune_scad_parameters| sparcity: 1.0| bias: 10.0| likelihood  689.1665208571073 
MJ      0       07:35:18.094    Scad_CV (XAUUSD,D1)     weights [0,0,0]| params [3,0.5,4,2.608146566441139]
DS      0       07:35:18.094    Scad_CV (XAUUSD,D1)     CTG_MIX sparcity -> 1.0 || bias -> 4.0

В этом случае оптимальные параметры SCAD показаны как 1.0 и 4.0. Разреженность 1.0 довольно высока и привела все веса к нулю — бессмысленный результат для модели смеси. Такой исход часто указывает на то, что конкретная модель смеси плохо подходит для данных или что штраф слишком агрессивен.

Когда это происходит, разумно сначала попытаться подогнать модель с помощью подхода MLE без штрафа SCAD. Если подход MLE дает осмысленные веса, проблема подтверждается как чрезмерно большой параметр разреженности. В таких случаях следует либо использовать метод MLE, либо снизить верхнюю границу параметров разреженности, проверяемых в процедуре кросс-валидации.

Следовательно, мы игнорируем прогоны, которые дали веса, полностью равные нулю. После этого оказывается, что нештрафованный подход MLE достигает наибольшего правдоподобия. На этом этапе мы определили оптимальные параметры SCAD для смеси Клейтона-Франка-Гумбеля (0.25075, 10) и смеси Клейтона-Стьюдента-Гумбеля (0.001, 2.0). Теперь эту информацию можно использовать для построения и сравнения моделей смешанных копул на полном обучающем наборе данных.


Выбор и сравнение моделей

Цель скрипта MixedCopulaSelection.mq5 — определить смешанную копулу, которая лучше всего описывает заданную выборку.

//+------------------------------------------------------------------+
//|                                         MixedCopulaSelection.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<Copulas\Bivariate\mixed.mqh>
#include<ECDF\linear_cdf.mqh>
#include<Files\FileBin.mqh>
//--- input parameters
input string   FirstSymbol="XAUUSD";
input string   SecondSymbol="XAUEUR";
input datetime TrainingDataStart=D'2025.01.01';
input ENUM_TIMEFRAMES TimeFrame = PERIOD_D1;
input ulong    HistorySize=1200;
input double   CTG_Scad_Gamma = 0.0;
input double   CTG_Scad_A = 2.0;
input double   CFG_Scad_Gamma = 0.25075;
input double   CFG_Scad_A = 10.0;
input uint     Max_Iterations = 25;
input bool     SaveModel = true;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   vector p_1,p_2,p_3,p_n;
   matrix pdata,pobs;
   if(!p_1.CopyRates(FirstSymbol,TimeFrame,COPY_RATES_CLOSE,TrainingDataStart,HistorySize) ||
      !p_2.CopyRates(SecondSymbol,TimeFrame,COPY_RATES_CLOSE,TrainingDataStart,HistorySize) ||
      p_1.Size()!=p_2.Size() ||
      !pdata.Resize(p_1.Size(),2) ||
      !pdata.Col(p_1,0) ||
      !pdata.Col(p_2,1))
     {
      Print(" failed to collect and initialize rates matrix ", GetLastError());
      return;
     }
//---
   CLinearCDF qt();
   if(!qt.fit(pdata))
      return;
//---
   pobs = qt.to_quantile(pdata);
//---
   if(SaveModel)
     {
      CFileBin file;
      string filename = FirstSymbol+"_"+SecondSymbol+".ecdf";
      file.Open(filename,FILE_WRITE|FILE_COMMON);
      if(!qt.save(file.Handle()))
         Print(" Failed to save ", filename);
      else
         Print("Ecdf model save to ", filename);
      file.Close();
     }

//---
   vector lowest = vector::Zeros(4);
   lowest.Fill(DBL_MAX);
//---
   Print("[[ Clayton - Student - Gumbel ]]");
//---
   CTGMixCop ctg;
   double fit;
//---
   fit = ctg.Fit(pobs,Max_Iterations,CTG_Scad_Gamma,CTG_Scad_A);
//---
   Print(" CTG loglikelihood ", fit);
   Print(" CTG weights ", ctg.Weights());
   Print(" CTG Params ", ctg.Params());
//---
   lowest[2] = aic(fit,int(pobs.Rows()));
   Print(" CTG sic ", sic(fit,int(pobs.Rows())));
   Print(" CTG aic ", lowest[2]);
   Print(" CTG hqic ", hqic(fit,int(pobs.Rows())));
//---
   Print("[[ Clayton - Frank - Gumbel ]]");
//---
   CFGMixCop cfg;
//---
   fit = cfg.Fit(pobs,Max_Iterations,CFG_Scad_Gamma,CFG_Scad_A);
//---
   Print(" CFG loglikelihood ", fit);
   Print(" CFG weights ", cfg.Weights());
   Print(" CFG Params ", cfg.Params());
//---
   lowest[0] = aic(fit,int(pobs.Rows()));
   Print(" CFG sic ", sic(fit,int(pobs.Rows())));
   Print(" CFG aic ", lowest[0]);
   Print(" CFG hqic ", hqic(fit,int(pobs.Rows())));
//---
   if(SaveModel)
     {
      CFileBin file;
      string filename,extension;
      ulong shift = lowest.ArgMin();
      CMixedCopula* mcop  = NULL;
      ENUM_COPULA_MIX mixtype = (ENUM_COPULA_MIX)shift;

      switch(mixtype)
        {
         case CFG_MIX:
            mcop = GetPointer(cfg);
            extension = ".cfgcopula";
            break;
         case CTG_MIX:
            mcop = GetPointer(ctg);
            extension = ".ctgcopula";
            break;
        }

      filename = FirstSymbol+"_"+SecondSymbol+extension;
      file.Open(filename,FILE_WRITE|FILE_COMMON);
      if(!mcop.Save(file.Handle()))
         Print("Failed to save ", filename);
      else
         Print("MixedCopula saved to ", filename, " in common folder.");
      file.Close();
      mcop = NULL;
     }
//---
  }
//+------------------------------------------------------------------+

Это включает сравнительную процедуру, в которой модели смешанных копул Клейтона-Франка-Гумбеля и Клейтона-Стьюдента-Гумбеля подгоняются к одному и тому же обучающему набору данных с использованием либо штрафованного, либо нештрафованного подхода правдоподобия, в зависимости от назначенных параметров SCAD. Скрипт определяет, какая смесь обеспечивает наилучшее статистическое соответствие, сравнивая соответствующие информационные критерии. Затем лучшая модель сериализуется и сохраняется в файл. При успешном выполнении терминал формирует журнал вывода, подтверждающий значения логарифмического правдоподобия и имя файла сохраненной модели. Ниже приведен вывод скрипта с использованием параметров SCAD, полученных в процедуре кросс-валидации.

JQ      0       11:26:22.119    MixedCopulaSelection (XAUUSD,D1)        Ecdf model save to XAUUSD_XAUEUR.ecdf
CO      0       11:26:22.119    MixedCopulaSelection (XAUUSD,D1)        [[ Clayton - Student - Gumbel ]]
IJ      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG loglikelihood 821.3707128783087
NR      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG weights [0,0.03552603447650678,0.9644739655224931]
NL      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG Params [-0.006169340694795771,0.4543309925241095,7,3.054494819573707]
ER      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG sic -1635.6513489208414
QI      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG aic -1640.7414257566174
HJ      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)         CTG hqic -1638.824033401239
QL      0       11:26:30.566    MixedCopulaSelection (XAUUSD,D1)        [[ Clayton - Frank - Gumbel ]]
QJ      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG loglikelihood 821.5620161629782
JD      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG weights [0,0.03875313891414744,0.9612468610848526]
HO      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG Params [0.002088006991783055,4.242903358363938,3.061932796342449]
MR      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG sic -1636.0339554901805
RI      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG aic -1641.1240323259565
RK      0       11:26:32.184    MixedCopulaSelection (XAUUSD,D1)         CFG hqic -1639.206639970578
PL      0       11:26:32.185    MixedCopulaSelection (XAUUSD,D1)        MixedCopula saved to XAUUSD_XAUEUR.cfgcopula in common folder.

Наилучшей моделью смешанной копулы оказывается смесь Клейтона-Франка-Гумбеля. Сравнение этих результатов с подогнанными моделями одиночных копул показывает, что лучшая смешанная копула обеспечивает худшее соответствие, чем лучшая модель одиночной копулы. 

QS      0       18:48:06.788    CopulaSelection (XAUUSD,D1)     CLAYTON_COPULA
RD      0       18:48:06.788    CopulaSelection (XAUUSD,D1)      sic 1009.0424155712477
HL      0       18:48:06.788    CopulaSelection (XAUUSD,D1)      aic 1003.9523387354716
DK      0       18:48:06.788    CopulaSelection (XAUUSD,D1)      hqic 1005.8697310908501
KL      0       18:48:06.795    CopulaSelection (XAUUSD,D1)     FRANK_COPULA
OH      0       18:48:06.795    CopulaSelection (XAUUSD,D1)      sic -1282.6359542297741
RP      0       18:48:06.795    CopulaSelection (XAUUSD,D1)      aic -1287.7260310655502
RD      0       18:48:06.795    CopulaSelection (XAUUSD,D1)      hqic -1285.8086387101716
NS      0       18:48:06.802    CopulaSelection (XAUUSD,D1)     GUMBEL_COPULA
PK      0       18:48:06.802    CopulaSelection (XAUUSD,D1)      sic -1620.5768024034212
FE      0       18:48:06.802    CopulaSelection (XAUUSD,D1)      aic -1625.6668792391972
PS      0       18:48:06.802    CopulaSelection (XAUUSD,D1)      hqic -1623.7494868838187
GK      0       18:48:11.931    CopulaSelection (XAUUSD,D1)     JOE_COPULA
NM      0       18:48:11.931    CopulaSelection (XAUUSD,D1)      sic -1917.6641571237963
DG      0       18:48:11.931    CopulaSelection (XAUUSD,D1)      aic -1922.7542339595723
NQ      0       18:48:11.932    CopulaSelection (XAUUSD,D1)      hqic -1920.8368416041938
LE      0       18:48:12.337    CopulaSelection (XAUUSD,D1)     N13_COPULA
CL      0       18:48:12.337    CopulaSelection (XAUUSD,D1)      sic -560.6680141126138
MJ      0       18:48:12.337    CopulaSelection (XAUUSD,D1)      aic -565.75809094839
EL      0       18:48:12.337    CopulaSelection (XAUUSD,D1)      hqic -563.8406985930114
JG      0       18:48:12.345    CopulaSelection (XAUUSD,D1)     N14_COPULA
MR      0       18:48:12.345    CopulaSelection (XAUUSD,D1)      sic -757.9453503106477
MH      0       18:48:12.345    CopulaSelection (XAUUSD,D1)      aic -763.0354271464238
HN      0       18:48:12.345    CopulaSelection (XAUUSD,D1)      hqic -761.1180347910453
QL      0       18:48:12.353    CopulaSelection (XAUUSD,D1)     GAUSSIAN_COPULA
ID      0       18:48:12.353    CopulaSelection (XAUUSD,D1)      sic -1189.1571905959859
ML      0       18:48:12.353    CopulaSelection (XAUUSD,D1)      aic -1194.2472674317619
IH      0       18:48:12.353    CopulaSelection (XAUUSD,D1)      hqic -1192.3298750763834
OR      0       18:48:12.830    CopulaSelection (XAUUSD,D1)     STUDENT_COPULA
PG      0       18:48:12.830    CopulaSelection (XAUUSD,D1)      sic -1233.3072453920256
OQ      0       18:48:12.830    CopulaSelection (XAUUSD,D1)      aic -1238.3973222278016
OD      0       18:48:12.830    CopulaSelection (XAUUSD,D1)      hqic -1236.479929872423

Тем не менее мы все еще можем использовать модель смешанной копулы вместо модели одиночной копулы в советнике SimpleCopulaStrategy.ex5, представленном во второй части серии статей «Двумерные копулы». Индикатор GoldMixedCopulaSignals.mq5 был изменен для использования модели смешанной копулы, а SimpleMixedCopulaStrategy.mq5 служит советником на основе смешанной копулы. 

GoldMixedCopulaSignals indicator


Заключение

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

Интеграция ALGLIB для невыпуклой оптимизации и модульная структура класса CMixedCopula создают основу для исследования смесей более высокой размерности. В конечном счете успех применения моделей копул при разработке стратегий зависит от репрезентативности обучающих данных и периодической перекалибровки моделей, чтобы они оставались согласованными с меняющейся рыночной динамикой. Весь код, упомянутый в статье, приложен ниже; описания файлов приведены в таблице. 

Файл  Описание 
MQL5/include/Copulas/ Эта папка содержит заголовочные файлы всех моделей копул, реализованных на данный момент, включая описанные в этой статье.
MQL5/include/ECDF/ Эта папка содержит заголовочные файлы, реализующие эмпирическую функцию распределения. 
MQL5/include/np.mqh Заголовочный файл с различными утилитами для векторов и матриц. 
MQL5/scripts/Scad_CV.mq5 Этот скрипт демонстрирует процедуру кросс-валидации, используемую для настройки гиперпараметров SCAD.
MQL5/scripts/MixedCopulaSelection.mq5 Скрипт подгоняет модели смешанных копул к выборочному набору данных и сравнивает их, чтобы выбрать копулу с наилучшим соответствием. 
MQL5/indicator s/ GoldMixedCopulaSignals.mq5 Индикатор, использующий модель смешанной копулы. 
MQL5/experts/ SimpleMixedCopulaStrategy.mq5 Советник, основанный на сигналах, генерируемых моделью смешанной копулы. 

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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Jacryptoking
Jacryptoking | 13 февр. 2026 в 08:08
стройте вещи, основанные на малазийском SnR... SMC
cemal
cemal | 15 февр. 2026 в 22:16

Я поместил файлы в точные папки плюс, я скачал и поместил файлы Algib https://www.mql5.com/ru/code/1146 в точные папки, но когда я компилирую индикатор GoldMixedCopula Signals, я получаю 100 ошибок. Что я делаю неправильно?

ошибка

Maxim Kuznetsov
Maxim Kuznetsov | 19 мая 2026 в 10:15
cemal #:

Я поместил файлы в точные папки плюс, я скачал и поместил файлы Algib https://www.mql5.com/ru/code/1146 в точные папки, но когда я компилирую индикатор GoldMixedCopula Signals, я получаю 100 ошибок. Что я делаю неправильно?


Вы неправильно сняли скриншот :-) 

если хотите помощи в разборе и исправлении ошибок, то надо чтобы на скриншоте была видна первая. Все прочие ошибки как правило "наведённые", то есть её следствия

Оптимизация долгосрочных сделок: Свечи поглощения и стратегии работы с ликвидностью Оптимизация долгосрочных сделок: Свечи поглощения и стратегии работы с ликвидностью
Это советник на основе высоких таймфреймов, который проводит долгосрочный анализ, принимает торговые решения и совершает сделки на базе анализа высоких таймфреймов W1, D1 и MN. В статье подробно рассматривается советник, специально разработанный для трейдеров, использующих долгосрочную торговлю и достаточно терпеливых, чтобы выдерживать волатильность младших таймфреймов и удерживать при этом свои позиции, не меняя слишком часто направление торговли, пока не достигнут целевых уровней фиксации прибыли.
MetaTrader 5: конструируйте рынок под стратегию — Renko/Range/Volume, синтетика и стресс-тесты на пользовательских символах MetaTrader 5: конструируйте рынок под стратегию — Renko/Range/Volume, синтетика и стресс-тесты на пользовательских символах
Показываем, как с помощью API пользовательских символов MetaTrader 5 превратить терминал в конструктор данных: генерировать вне‑временные графики Renko, Range и Equal‑Volume и собирать синтетические инструменты. Разбираем агрегацию тиков и модификацию истории для стресс‑тестов (расширение спреда, изменение стоп‑уровней) с учетом ограничений платформы. Даем практику работы с CiCustomSymbol и маршрутизацией приказов на реальный символ через обертку CustomOrder, с готовыми фрагментами кода.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Алгоритм андского кондора — Andean Condor Algorithm (ACA) Алгоритм андского кондора — Andean Condor Algorithm (ACA)
В статье реализован Andean Condor Algorithm (ACA) для MQL5 — компактный оптимизатор с многомасштабным оператором интенсификации. Выявлен эффект значимого роста качества при малой популяции: одна корректировка настроек выводит его в топ-45 — и за этим стоит характерная особенность алгоритма, о которой стоит знать. Материал даёт готовый код и практические ориентиры по применению.