MQL5における二変量コピュラ(第3回):混合コピュラモデルの実装とチューニング
はじめに
これまでの2つの記事では、コピュラ関数について代表的な2種類のクラスである楕円コピュラとアルキメデスコピュラを扱いました。その分析を通じて、異なるコピュラがそれぞれ異なる種類のデータ依存構造を捉えることが確認されました。しかし、金融データは本質的に複雑であり、単一のコピュラファミリーだけでは、データセット内に存在するすべての依存構造を十分に表現できない場合があります。この問題を解決する方法の1つが混合コピュラです。混合コピュラは、複数のコピュラファミリーの長所を組み合わせることで、より広範囲の依存関係をモデル化できます。本記事では、前回までに紹介したコピュラファミリーを利用した混合コピュラモデルの実装について説明します。
混合コピュラとは何か
簡単に言えば、混合コピュラとは複数のコピュラファミリーから構成される多変量関数です。数学的には、混合コピュラ関数は複数の異なるコピュラを線形結合したものであり、データ間の複雑な依存構造を表現するために使用されます。
混合コピュラの数学的な定義は以下のとおりです。

この式では、w_i は各コピュラへ割り当てられる重みを表します。すべての重みの合計は1になる必要があります。変数sは、混合モデルに含まれる単一コピュラの数を示します。混合コピュラのパラメータには、それぞれの構成要素となるコピュラの個別パラメータ(θi)が含まれます。この構造により、混合コピュラは単一のコピュラでは表現できない幅広い依存構造を柔軟にモデル化できます。目的は、十分な候補コピュラを組み合わせ、データセット内に存在する依存構造をできるだけ網羅的に表現できるモデルを構築することです。
最適なコピュラ数や、対象データセットに適したファミリーを決定する方法については本記事の範囲外ですが、ここでは3種類の異なるファミリーから構成される混合コピュラの実装を紹介します。この手法は、Fernando Sabino da Silva、Flavio Ziegelmann、João Caldeiraによる論文「Mixed Copula Pairs Trading Strategy on the S&P 500」で提案された方法論に基づいています。著者らは、Clayton-Frank-Gumbel混合モデルおよびClayton-Student-t-Gumbel混合モデルを利用したペアトレード戦略を提案しています。これらの組み合わせは、非対称なテール依存性や、データ内のレジーム切替的な挙動を捉えることを目的としています。これらの混合コピュラの有効性は、以下の表に示すように、各成分が持つ独自の強みに由来します。
| コピュラ | 特徴 | 関連性 |
|---|---|---|
| Clayton | 下側テール依存 | 共同で発生する極端な下落を捉える |
| Gumbel | 上側テール依存 | 共同で発生する極端な上昇を捉える |
| Frank | 対称性、テール依存なし | 極端なイベントを仮定せず、分布の中央部における通常の依存構造を捉える |
| Student-t | 対称性、テール依存あり | 上下方向の極端な変動やファットテールを捉える(極端な出来事(上昇または下降)が発生した場合、銘柄は一緒に動く可能性が高いことを前提とする) |
混合比率は、データ内の依存構造を最も適切に説明するコピュラへ大きな重みを与えます。たとえば、Frankコピュラは、極端なテール挙動を伴わない低〜中程度の相関を持つデータで効果を発揮します。一方、Student-tコピュラは、非対称ではないものの極端な変動を伴う状況を捉えるために設計されています。Clayton-Frank-Gumbel混合モデルは完全なアルキメデス型構成であり、下側テール、上側テール、そして中央部分の独立的な挙動を区別することに優れています。この組み合わせは、計算効率と解釈性という2つの利点を持ちます。対照的に、Clayton-Student-t-Gumbel混合モデルはハイブリッド型です。Student-tコピュラを追加することで、Frankコピュラにはない、上下両方向のテール依存性を同時に捉える柔軟性を得られます。これは、ボラティリティクラスタリングが顕著な低時間足データにおいて特に重要です。
前述の論文では、この手法の概念は説明されていますが、具体的な実装の記述は十分ではありません。そこで本記事では、補足として、Zongwu CaiおよびXian WangによるJournal of the American Statistical Association(ASA)掲載論文「Selection of Mixed Copula Models via Penalized Likelihood」を参照します。
混合コピュラのパラメータ推定
ASAの論文では、ペナルティ付き尤度法と縮小演算子を組み合わせた、混合コピュラモデルの選択および推定フレームワークが紹介されています。著者らは、複数のコピュラファミリーを同時に組み合わせ、調整することで、金融データに存在する複雑な依存構造を捉える方法を提案しています。
この手法では、尤度最大化処理にSCAD (Smoothly Clipped Absolute Deviation)ペナルティを適用することで、不要または重要度の低いコピュラ要素を除外します。つまり、モデル選択とパラメータ推定を同時に実行できます。重みに対してペナルティを適用することで、関連性の低いコピュラ成分の係数を0へ縮小します。処理は、まず候補となるコピュラを選択し、その後クロスバリデーションによって最適なSCAD調整パラメータを決定する流れになります。
SCADは、変数選択とパラメータ推定を同時に実行するために導入された正則化手法です。これは、Lasso(L1正則化)やRidge(L2正則化)が持つ問題を克服するために設計されました。特に、Lassoが大きな係数を過剰に縮小してしまうことによるバイアス問題を解決することを目的としています。理想的なペナルティ関数には、一般的に「オラクル特性」と呼ばれる3つの特性が求められます。
- スパース性:小さく重要でない係数を0へ設定できる能力
- 不偏性:大きく重要な係数を過度に縮小しない性質
- 連続性:データの小さな変化に対して不安定な変動を起こさない性質
SCADは、この3つすべてを満たす数少ない手法の1つとして広く利用されています。SCADペナルティは単純な1つの式ではなく、導関数によって定義されます。適用されるパラメータ(ここでは重み)の大きさによって動作が変化します。この手法は次の3つのステップで機能します。
- 小さい係数では、Lasso正則化と同様に係数を0へ近づけます。

- 中程度の係数では、Lasso効果が二次的な変化によって徐々に弱まります。

- 大きな係数に対しては、ペナルティは一定値となります。大きな係数では、SCADは一定値へ変化し、それ以上の抑制を行いません。これにより、Lassoで発生する推定バイアスを回避できます。

上記の式では、λはスパース性の閾値を決定する調整パラメータ、aはペナルティが飽和する速度を制御するパラメータ、θはペナルティ対象となるパラメータを表します。SCADペナルティの主な欠点は、目的関数が非凸になる点です。RidgeやLassoのような単純な鉢型の形状とは異なり、SCADでは複数の局所最小値が存在する可能性があります。そのため、高度な最適化アルゴリズムが必要になります。
最適な調整パラメータを求めるために、クロスバリデーションを使用します。データを学習用とテスト用に分割し、候補となるλおよびaの組み合わせについてグリッドサーチを実行します。各組み合わせについて、学習データで混合コピュラモデルを適合させ、テストデータ上でペナルティ付き尤度を評価します。目的は、構成コピュラのパラメータによる尤度最大化と、コピュラ重みに適用されるSCADペナルティの差を最大化することです。
最適なSCADパラメータが決定された後、最終モデルのパラメータは全データを使用して推定されます。複数のコピュラを同時に推定する処理は計算量が大きいため、ここではEMアルゴリズムを使用します。
- 処理は、初期値を求めるための2段階の初期推定から始まります。通常、重みは均等に分配され、各コピュラパラメータは制約条件を満たす有効な初期値に設定されます。
- 次に、EMアルゴリズムのExpectationステップを実行します。この段階では、各データポイントがどのコピュラ成分に属するかの確率を計算し、重みを更新します。
- 最後に、Maximizationステップで、前段階で計算された重みを利用しながら、各コピュラパラメータを尤度最大化方向へ更新します。
これらの処理を収束するまで繰り返すことで、観測された依存構造を反映した混合コピュラモデルが完成します。次のセクションでは、これらの手順を実装するコードについて説明します。
MQL5による混合コピュラモデルの実装
ここで説明する実装は、もともとPythonで作成されたHudson and ThamesのリポジトリをベースにMQL5向けへ適応したものです。ネイティブMQL5による混合コピュラの中核ロジックは、mixed.mqhヘッダーファイルに含まれています。このファイルでは、抽象クラスであるCMixedCopulaが、すべての混合コピュラ実装の基盤として機能します。このクラスでは、混合モデルを管理するために複数のprotectedプロパティを定義しています。
クラス内では、個々の構成コピュラファミリーを表すm_copulas配列を定義しています。各コピュラ成分へ割り当てられる混合比率は、ベクトルm_weightsに保存されます。また、それぞれのコピュラ成分が持つ固有のパラメータは、1つのベクトル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; }
最後に、このクラスはオーバーライドされることを前提とした2つの仮想メソッドを定義しています。Fit()メソッドは、擬似観測値のサンプルが与えられた場合に、SCADペナルティを組み込んだPenalized_Log_Likelihood()メソッドで指定された尤度関数を最小化することにより、コピュラのパラメータを推定するロジックを実装する必要があります。
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クラスは、Clayton-Frank-Gumbel混合コピュラを表します。ユーザーがデータからモデルパラメータを推定するのではなく、明示的に定義することを好む場合のために、パラメトリックコンストラクタを備えています。
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]); } } }
パラメトリックコンストラクタを使用する場合、パラメータはベクトルコンテナで指定する必要があります。コピュラパラメータとそれに対応する重みは、いずれも特定の順序に従う必要があります。
- Claytonパラメータ
- Frankパラメーター
- Gumbelパラメータ
同様に、ゲッターメソッドを呼び出すと、値がこの一貫した順序で返されるため、モデル全体で予測可能なデータ処理が保証されます。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); }
- データ入力変数は、擬似観測値([0,1]の区間に変換された生の値)の行列(最低2列)です。
- maxiter関数の入力は、EMアルゴリズムの最大反復回数を定義する整数です。
- 入力値gamma_scadとa_scadは、SCADハイパーパラメータを表すdouble型の値です。
- 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;
}protectedメソッド_expectation_step()は、EMアルゴリズムの期待値計算段階を実装します。このメソッドは、指定されたSCADパラメータの値に応じて、2つの異なるモードで動作します。両方のSCADパラメータが有効な場合(スパース性パラメータが0.0より大きく、バイアス制御パラメータが2.0より大きい場合)、ペナルティ付き尤度法が有効になります。これにより、モデルは重要度の低い重みを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の境界線形等式不等式制約(BLEIC, Boundary, Linear Equality-Inequality Constraints)最小化アルゴリズムを活用します。この最小化アルゴリズムは、ペナルティ付き対数尤度を定義する_ml_qfunc()メソッドを対象としています。これは、public 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クラスは、Clayton-Student-t-Gumbel混合コピュラを実装するクラスであり、設計構造はCFGMixCopクラスとほぼ同一です。主な違いは、構成要素となるコピュラの1つをFrankコピュラからStudent-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; }
この関数は、以下の入力を受け取ります。
- データ入力は、疑似観測値で構成された行列です。各列は、それぞれ1つの系列を表します。
- 次に、この関数では6つの入力変数を指定します。最初の3つはgamma接頭辞を持ち、残りの3つはa接頭辞を持ちます。これらは、最適なSCADハイパーパラメータの組み合わせを探索するグリッドサーチの範囲と粒度を定義します。count接尾辞を持つ引数は、startおよびstop接尾辞を持つ引数によって定義された範囲内で評価する候補値の数を指定します。
- 次に、この関数では6つの入力変数を指定します。最初の3つはgamma接頭辞を持ち、残りの3つは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(バイアス低減)が、それぞれスパース化の閾値とバイアス低減速度を制御します。最適な値は最終的にはデータによって決まりますが、学術研究や実務で一般的に使用される範囲が存在します。バイアス低減パラメータは、ペナルティが飽和するまでの速度を制御します。このパラメータを調整する場合、2より大きい値のみを検討する必要があります。これは、関数がハード閾値処理とは異なる閾値処理特性を維持するためには、パラメータが厳密に2より大きくなければならないためです。
一方、スパース性パラメータは、学習データのスケールやサンプル数に大きく依存します。このパラメータは、係数を0へ縮小する基準となるカットオフ値を定義します。調整時に検討される値の範囲は、一般的に0から1の間です。下限値は、モデルがペナルティなしのMLEに近づける程度に十分小さく設定する必要があります。たとえば、0.001などの値が使用されます。一方、上限値は、すべての係数(つまり混合モデル内の各構成コピュラへ割り当てられる重み)を0にできる可能性がある程度、大きく設定する必要があります。一般的に、サンプル数が多いほど、必要となるスパース性パラメータは小さくなります。
スパース性パラメータが高い場合、モデルはよりスパースになり、多くの混合重みが0になります。バイアス低減パラメータが高い場合、ペナルティ効果がより長く維持されるため、ペナルティ付き推定から不偏推定への移行がより緩やかになります。バイアス低減パラメータが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スクリプトのデフォルト設定では、ユーザーが調整可能な入力として、スパース性パラメータとバイアスパラメータの試行値を比較的狭い範囲のグリッドで指定しています。この設定により、1000件強の観測データ数に対しても、スクリプトを効率的に実行できます。ここで注意すべき点は、試行グリッドにバイアスパラメータ値2.0が含まれていることです。この値はバイアスパラメータとして無効であるため、SCADペナルティは完全に無効化されます。これにより、ペナルティを適用しない尤度最大化が、より良い結果をもたらすかどうかを比較できます。Clayton-Frank-Gumbel混合モデルを選択してスクリプトを実行すると、以下の結果が出力されます。
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
結果から、Clayton-Frank-Gumbel混合モデルにおける最適なSCADパラメータは、スパース性パラメータが0.25075、バイアスパラメータが10であることが分かります。次に示すClayton-Student-t-Gumbel混合モデルの結果は、特に興味深い内容となっています。
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という値は非常に高く、その結果、すべての重みが0へ縮小されています。これは混合モデルとしては意味を成さない結果です。このような結果は、多くの場合、選択した混合モデルが対象データに適していない、またはペナルティが過度に強く設定されていることを示しています。
このような場合、まずSCADペナルティを適用しないMLEアプローチでモデルを適合させることが推奨されます。MLEによって妥当な重みが得られる場合、問題の原因はスパース性パラメータが大きすぎることであると判断できます。この場合、MLEを使用するか、クロスバリデーションで試行するスパース性パラメータの上限値を下げる必要があります。
したがって、すべての重みが0になった試行結果は無視します。その結果、ペナルティなしのMLEアプローチが最も高い尤度を達成していることが確認できます。この段階で、Clayton-Frank-Gumbel混合モデルの最適なSCADパラメータ(0.25075, 10)と、Clayton-Student-t-Gumbel混合モデルの最適なSCADパラメータ(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; } //--- } //+------------------------------------------------------------------+
この処理では、Clayton-Frank-Gumbel混合コピュラモデルとClayton-Student-t-Gumbel混合コピュラモデルを、同じ学習データセットに対して適合させ、比較を行います。使用する推定方法は、設定されたSCADパラメータに応じて、ペナルティ付き尤度法またはペナルティなしのMLEアプローチのいずれかが選択されます。スクリプトは、それぞれのモデルの情報量基準を比較することで、どの混合モデルが最も高い統計的適合度を持つかを判定します。その後、より優れたモデルはシリアライズされ、ファイルへ保存されます。正常に実行されると、ターミナルにはログ尤度の値と、保存されたモデルのファイル名を確認できる出力ログが表示されます。以下は、クロスバリデーション手順によって取得した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.
最も適合度の高い混合コピュラモデルとして、Clayton-Frank-Gumbel混合モデルが選択されました。しかし、これらの結果を単一コピュラモデルの適合結果と比較すると、最良の混合コピュラモデルであっても、最良の単一コピュラモデルと比べて適合度が劣ることが分かります。
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
それでも、単一コピュラモデルの代わりに混合コピュラモデルを使用することは可能です。これは、「Bivariate Copulae」連載第2回の記事で紹介したSimpleCopulaStrategy.ex5エキスパートアドバイザー(EA)へ適用できます。インジケータGoldMixedCopulaSignals.mq5は、混合コピュラモデルを利用するように変更されており、SimpleMixedCopulaStrategy.mq5は、混合コピュラをベースとしたEAとして機能します。

結論
本記事では、MQL5上で混合コピュラモデルを実装する方法を紹介しました。単一コンポーネントのコピュラを超えたアプローチを採用することで、金融時系列データにしばしば存在する複雑な依存関係を、より適切にモデル化できる可能性を持つフレームワークを構築しました。混合コピュラ手法は、より大きな計算コストを必要とし、ハイパーパラメータの慎重な調整も必要になります。しかし、より優れたコピュラモデルを構築できる可能性を考慮すると、その価値は十分にあると言えます。
非凸最適化のためのALGLIB統合と、モジュール化されたCMixedCopulaクラス構造により、より高次元の混合モデルへ発展させるための基盤が提供されます。最終的に、コピュラモデルを戦略開発へ適用する際の成功は、学習データが実際の市場状況をどれだけ適切に代表しているか、そして変化する市場ダイナミクスに対応し続けるために、モデルを定期的に再調整できるかどうかに大きく依存します。本記事で参照したすべてのコードは以下に添付されています。各ファイルの説明は、次の表にまとめています。
| ファイル | 説明 |
|---|---|
| MQL5/include/Copulas/ | 本記事で説明したモデルを含め、これまでに実装されたすべてのコピュラモデルのヘッダーファイルを含むフォルダ |
| MQL5/include/ECDF/ | 経験累積分布関数(ECDF, Empirical Cumulative Distribution Function)を実装するヘッダーファイルを含むフォルダ |
| MQL5/include/np.mqh | ベクトルおよび行列処理用の各種ユーティリティをまとめたヘッダーファイル |
| MQL5/scripts/Scad_CV.mq5 | SCADハイパーパラメータを調整するためのクロスバリデーション手順を実装したスクリプト |
| MQL5/scripts/MixedCopulaSelection.mq5 | サンプルデータセットへ混合コピュラモデルを適合させ、各モデルを比較して最適なコピュラモデルを選択するスクリプト |
| MQL5/indicators/GoldMixedCopulaSignals.mq5 | 混合コピュラモデルを使用するインジケータ |
| MQL5/experts/SimpleMixedCopulaStrategy.mq5 | 混合コピュラモデルから生成されたシグナルを利用するEA |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19930
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
ラリー・ウィリアムズの『市場の秘密』(第11回):カスタムインジケータによるスマッシュデー反転の検出
MQL5取引ツール(第16回):改良版スーパーサンプリング・アンチエイリアシング(SSAA)と高解像度レンダリング
プライスアクション分析ツールキットの開発(第61回):3スイング検証による構造的傾斜トレンドラインブレイクアウト
MQL5とデータ処理パッケージの統合(第7回):銘柄間連携のためのマルチエージェント環境の構築
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
ファイルを指定されたフォルダに正確に配置しました。さらに、Algibのファイル(https://www.mql5.com/ja/code/1146) もダウンロードして指定されたフォルダに配置しましたが、GoldMixedCopula Signalsインジケーターをコンパイルしようとすると、100件のエラーが発生します。どこが間違っているのでしょうか?
ファイルを指定されたフォルダに配置し、さらに Algibのhttps://www.mql5.com/ja/code/1146 ファイルをダウンロードして適切なフォルダに配置しましたが、GoldMixedCopula Signalsインジケーターをコンパイルしようとすると、100件のエラーが発生します。どこが間違っているのでしょうか?
スクリーンショットの撮り方が間違っています :-)
エラーの解析や修正の手助けが必要な場合は、スクリーンショットに最初のエラーが表示されている必要があります。他のすべてのエラーは、通常「派生した」ものであり、つまりそのエラーの結果に過ぎません