Двумерные копулы в MQL5 (Часть 3): Реализация и настройка смешанных моделей копул
Введение
Первые две статьи в нашем исследовании функций копул были посвящены двум наиболее распространенным классам копул: эллиптическим и архимедовым. В ходе этого анализа было установлено, что разные копулы отражают различные типы зависимости данных. Однако, поскольку финансовые данные по своей природе сложны, одно семейство копул может недостаточно полно описывать весь спектр структур зависимости в наборе данных. В этом отношении смешанные копулы могут устранить это ограничение, объединяя сильные стороны отдельных семейств копул для моделирования более широкого диапазона зависимостей. В этой статье мы описываем реализацию моделей смешанных копул с использованием семейств, представленных в предыдущих частях.
Что такое смешанная копула?
Проще говоря, смешанная копула — это многомерная функция, построенная из нескольких семейств копул. Формально функция смешанной копулы представляет собой линейную комбинацию различных копул, используемую для описания сложных структур, определяющих взаимосвязь данных.
Математическое определение смешанной копулы приведено ниже.

В этой формуле 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 — это метод регуляризации, предложенный для одновременного отбора переменных и оценки параметров. Он был разработан для преодоления ограничений регуляризации Lasso (L1) и Ridge (L2), в частности смещения, которое Lasso вносит из-за чрезмерного сжатия больших коэффициентов. В общем случае идеальная штрафная функция должна обладать тремя свойствами, называемыми оракульными свойствами.
- Первое — свойство разреженности: функция должна иметь возможность занулять малые, нерелевантные коэффициенты.
- Второе оракульное свойство — несмещенность. Это означает, что функция не должна чрезмерно сжимать большие, значимые коэффициенты.
- Последнее оракульное свойство — непрерывность. Функция должна оставаться устойчивой и не колебаться хаотично при небольших изменениях данных.
SCAD широко используется, потому что это одна из немногих функций, обладающих всеми тремя свойствами. Штраф SCAD задается производной, а не одной простой формулой. Его поведение меняется в зависимости от величины параметра, к которому он применяется; в данном контексте это веса. Метод работает в три этапа.
- Для малых коэффициентов штрафная функция оказывает тот же эффект, что и регуляризация Lasso, принуждая коэффициенты стремиться к нулю.

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

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

В приведенных выше формулах λ — это параметр настройки порога разреженности, a управляет тем, насколько быстро штраф выходит на плато, а θ — штрафуемый параметр. Основной недостаток штрафа SCAD состоит в том, что он делает целевую функцию невыпуклой. В отличие от чашеобразной формы Ridge или Lasso, целевая функция SCAD может иметь несколько локальных минимумов, что требует более сложных процедур оптимизации.
Для поиска оптимальных параметров настройки используется процедура кросс-валидации. Данные разделяются на обучающую и тестовую выборки, и выполняется решетчатый поиск по кандидатным значениям λ и a. Для каждой пары модель смешанной копулы подгоняется к обучающим данным, а ее пенализованное/штрафное правдоподобие рассчитывается на тестовых данных. Цель состоит в максимизации правдоподобия параметров составляющих копул за вычетом штрафа SCAD, примененного к весам копул.
После определения оптимальных параметров SCAD итоговые параметры модели оцениваются на полном наборе данных. Поскольку одновременная оценка нескольких копул вычислительно сложна, в процессе используется алгоритм максимизации ожидания (Expectation-Maximization, EM).
- Процесс начинается с двухэтапной оценки для поиска начальных значений. Веса обычно распределяются поровну, а начальные параметры копул устанавливаются в допустимые значения в соответствии с их ограничениями.
- Следующий этап — шаг ожидания. На этой фазе уточняются веса. Она вычисляет, в какой степени каждая точка данных относится к конкретному компоненту копулы.
- Последний этап алгоритма 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 ¶ms, 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]); } } }
При использовании параметрического конструктора параметры должны передаваться в векторных контейнерах. И параметры копул, и соответствующие им веса должны следовать определенной последовательности:
- параметр Клейтона
- параметр Франка
- параметр Гумбеля
Аналогично, вызов любых методов доступа возвращает значения в этом постоянном порядке, обеспечивая предсказуемую обработку данных во всей модели. Метод 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 служит советником на основе смешанной копулы.

Заключение
В этой статье мы успешно продемонстрировали реализацию моделей смешанных копул в 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
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Оптимизация долгосрочных сделок: Свечи поглощения и стратегии работы с ликвидностью
MetaTrader 5: конструируйте рынок под стратегию — Renko/Range/Volume, синтетика и стресс-тесты на пользовательских символах
Алгоритм андского кондора — Andean Condor Algorithm (ACA)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Я поместил файлы в точные папки плюс, я скачал и поместил файлы Algib https://www.mql5.com/ru/code/1146 в точные папки, но когда я компилирую индикатор GoldMixedCopula Signals, я получаю 100 ошибок. Что я делаю неправильно?
Я поместил файлы в точные папки плюс, я скачал и поместил файлы Algib https://www.mql5.com/ru/code/1146 в точные папки, но когда я компилирую индикатор GoldMixedCopula Signals, я получаю 100 ошибок. Что я делаю неправильно?
Вы неправильно сняли скриншот :-)
если хотите помощи в разборе и исправлении ошибок, то надо чтобы на скриншоте была видна первая. Все прочие ошибки как правило "наведённые", то есть её следствия