English Русский 日本語
preview
Bivariate Copulas in MQL5 (Teil 3): Implementierung und Optimierung von Mixed-Copula-Modellen in MQL5

Bivariate Copulas in MQL5 (Teil 3): Implementierung und Optimierung von Mixed-Copula-Modellen in MQL5

MetaTrader 5Beispiele |
35 3
Francis Dube
Francis Dube

Einführung

In den ersten beiden Artikeln unserer Reihe über Copula-Funktionen wurden die beiden gängigsten Klassen behandelt: elliptische und archimedische Copulas. Diese Analyse zeigte, dass verschiedene Copulas unterschiedliche Arten von Datenabhängigkeiten erfassen. Da Finanzdaten jedoch von Natur aus komplex sind, reicht eine einzige Copula-Familie möglicherweise nicht aus, um das gesamte Spektrum der Abhängigkeitsstrukturen innerhalb eines Datensatzes angemessen abzubilden. In dieser Hinsicht könnten gemischte Copulas diese Einschränkung möglicherweise überwinden, indem sie die Stärken einzelner Copula-Familien kombinieren, um ein breiteres Spektrum an Abhängigkeiten zu modellieren. In diesem Artikel beschreiben wir die Implementierung von gemischten Copula-Modellen unter Verwendung der in unseren vorherigen Beiträgen vorgestellten Familien.


Was ist eine gemischte Copula?

Einfach ausgedrückt ist eine gemischte Copula eine multivariate Funktion, die aus mehreren Copula-Familien zusammengesetzt ist. Formal gesehen ist eine gemischte Copula-Funktion eine Linearkombination verschiedener Copulas, mit der komplexe Abhängigkeitsstrukturen zwischen den Daten erfasst werden.

Die mathematische Definition einer gemischten Copula lautet wie folgt:

Formel für gemischte Copulas

In dieser Formel steht w_i​ für die Gewichte, die den einzelnen Copulas zugewiesen werden, wobei die Summe aller Gewichte gleich eins sein muss. Die Variable s bezeichnet die Gesamtzahl der in der Mischung enthaltenen einzelnen Copulas. Die Parameter der gemischten Copula umfassen die einzelnen Parameter (θi​) jeder einzelnen Copula. Diese Struktur bietet gemischten Copulas die Flexibilität, ein breites Spektrum an Abhängigkeitsstrukturen zu modellieren, die eine einzelne Copula nicht erfassen könnte. Das Ziel besteht darin, aus einer hinreichenden Menge von Kandidaten-Copulas ein Modell zu konstruieren, das die Abhängigkeitsstrukturen eines Datensatzes vollständig beschreibt.

Die Ermittlung der optimalen Anzahl und der spezifischen Copula-Familien für einen bestimmten Datensatz würde den Rahmen dieses Artikels sprengen; wir werden jedoch die Implementierung einer gemischten Copula demonstrieren, die sich aus drei unterschiedlichen Familien zusammensetzt. Dieser Ansatz folgt der Methodik, die Fernando Sabino da Silva, Flavio Ziegelmann und João Caldeira in ihrer Arbeit „Mixed Copula Pairs Trading Strategy on the S&P 500“ entwickelt haben. Die Autoren schlagen eine Pairs-Trading-Strategie vor, bei der Clayton-Frank-Gumbel- und Clayton-Student-t-Gumbel-Mixed-Copula zum Einsatz kommen. Diese Kombinationen sind darauf ausgelegt, sowohl die asymmetrische Tail-Abhängigkeit als auch ein mögliches Regimewechselverhalten in den Daten zu erfassen. Die Wirksamkeit dieser Mischungen beruht auf den einzigartigen Stärken der einzelnen Bestandteile, wie aus der nachstehenden Tabelle hervorgeht. 

Copula  Charakteristik  Relevanz 
Clayton Untere Tail-Abhängigkeit Erfasst gemeinsame extreme Abwärtsbewegungen.
Gumbel Obere Tail-Abhängigkeit Erfasst gemeinsame extreme Aufwärtsbewegungen.
Frank Symmetrie ohne Tail-Abhängigkeit Erfasst die typische Abhängigkeit in der Mitte der Verteilung, ohne davon auszugehen, dass Extreme gemeinsam auftreten.
Student-t  Symmetrisch mit einer Tail-Abhängigkeit Erfasst Fat-Tails in beide Richtungen. In diesem Fall geht man davon aus, dass sich die Kurse bei einem Extremereignis (nach oben oder unten) wahrscheinlich gemeinsam bewegen werden. 

Die Mischungsgewichte geben derjenigen Copula den Vorrang, die die Abhängigkeiten in den Daten am besten beschreibt. Beispielsweise ist die Frank-Copula am effektivsten, wenn die Daten eine geringe bis mäßige Korrelation ohne ausgeprägtes Tail-Verhalten aufweisen. Umgekehrt ist die Student-t-Komponente darauf ausgelegt, extreme Schwankungen zu erfassen, die nicht unbedingt asymmetrisch sind. Die Clayton-Frank-Gumbel-Mischung ist rein archimedisch, wodurch sie sich hervorragend eignet, zwischen Lower-Tail-, Upper-Tail- und annähernd unabhängigen Verhaltensweisen im Zentrum der Verteilung zu unterscheiden. Diese Kombination bietet den doppelten Vorteil von Recheneffizienz und hoher Interpretierbarkeit. Im Gegensatz dazu fungiert die Clayton-Student-t-Gumbel-Mischung als Hybrid; die Student-t-Copula bietet ein Maß an Flexibilität, das der Frank-Copula fehlt, indem sie die Tail-Abhängigkeit in beide Richtungen gleichzeitig erfasst. Dies gilt insbesondere für Daten mit niedrigerem Zeitrahmen, bei denen Volatilitätscluster besonders ausgeprägt sind.

Zwar wird in der zuvor erwähnten Arbeit das Konzept beschrieben, das wir nachbilden möchten, doch fehlen darin konkrete Details zur Umsetzung für gemischte Copulas. Um diese Lücke zu schließen, greifen wir auf eine zweite wissenschaftliche Quelle zurück: „Selection of Mixed Copula Models via Penalized Likelihood“ von Zongwu Cai und Xian Wang, veröffentlicht im Journal of the American Statistical Association (ASA).


Schätzung der Parameter einer gemischten Copula

Der ASA-Artikel stellt einen datengestützten Rahmen für die Auswahl und Schätzung gemischter Copula-Modelle vor, bei dem ein Ansatz auf Basis der penalisierten Likelihood in Kombination mit einem Schrumpfungsoperator zum Einsatz kommt. Die Autoren gehen die Herausforderung an, komplexe Abhängigkeitsstrukturen in Finanzdaten zu erfassen, indem sie die gleichzeitige Kombination und Verfeinerung mehrerer Copula-Familien ermöglichen.

Ihre Methode filtert redundante oder unbedeutende Copula-Komponenten heraus, indem sie beim Verfahren zur Likelihood-Maximierung eine SCAD-Straffunktion (Smoothly Clipped Absolute Deviation) anwendet. Dieser Ansatz ist darauf ausgelegt, die Modellauswahl und die Parameterschätzung gleichzeitig durchzuführen. Mithilfe eines Ansatzes, bei dem die Gewichte mit einem Strafterm versehen werden, reduziert die Methode die Koeffizienten weniger relevanter Copulas auf Null. Der Prozess beginnt mit einer anfänglichen Auswahl geeigneter Copulas für die Mischung, gefolgt von einem Kreuzvalidierungsverfahren zur Bestimmung der optimalen SCAD-Abstimmungsparameter.

SCAD

SCAD ist ein Regularisierungsverfahren, das eingeführt wurde, um die Variablenselektion und die Parameterschätzung gleichzeitig durchzuführen. Es wurde entwickelt, um die Einschränkungen der Lasso- (L1) und Ridge-Regularisierung (L2​) zu überwinden. Insbesondere die Verzerrung, die Lasso durch eine übermäßige Verringerung großer Koeffizienten verursacht. Im Allgemeinen sollte eine ideale Straffunktion drei Eigenschaften aufweisen, die als Oracle-Eigenschaften bezeichnet werden. 

  • Erstens ist da die Eigenschaft der Sparsität: Die Straffunktion muss in der Lage sein, kleine, irrelevante Koeffizienten auf Null zu setzen.
  • Die zweite sogenannte Oracle-Eigenschaft ist die Unverzerrtheit. Das bedeutet, dass große, signifikante Koeffizienten nicht übermäßig reduziert werden dürfen.
  • Die letzte Eigenschaft des Oracles ist die Kontinuität. Die Funktion muss stabil bleiben und darf bei geringfügigen Änderungen der Daten nicht unvorhersehbar schwanken.

SCAD wird häufig eingesetzt, da es eine der wenigen Funktionen ist, die alle drei Anforderungen erfüllt. Die SCAD-Strafe wird nicht durch eine einzelne einfache Formel, sondern durch ihre Ableitung definiert. Ihr Verhalten ändert sich je nach Größe des Parameters, auf den sie angewendet wird – in diesem Zusammenhang sind das die Gewichte. Die Methode umfasst drei Schritte.

  1. Bei kleinen Koeffizienten hat die Straffunktion denselben Effekt wie die Lasso-Regularisierung, nämlich die Koeffizienten in Richtung Null zu drängen.


    SCAD mit kleinen Koeffizienten

  2. Bei Koeffizienten mittlerer Größe nimmt der Lasso-Effekt der Straffunktion quadratisch ab.


    SCAD mit mittleren Koeffizienten

  3. Für große Koeffizienten wird der Strafterm konstant. Sobald ein Koeffizient ausreichend groß ist, unterlässt SCAD dessen Unterdrückung, wodurch der bei der Lasso-Regularisierung auftretende Schätzfehler beseitigt wird.

    SCAD mit großen Koeffizienten

In den obigen formalen Darstellungen ist λ der Abstimmungsparameter für den Sparsitätsschwellenwert, während a bestimmt, wie schnell sich die Straffung abflacht, und θ der Parameter ist, auf den die Strafe angewendet wird. Der größte Nachteil der SCAD-Straffunktion besteht darin, dass sie die Zielfunktion nichtkonvex macht. Im Gegensatz zur Schalenform von Ridge oder Lasso kann die SCAD-Zielfunktion mehrere lokale Minima aufweisen, was ausgefeilte Optimierungsroutinen erfordert.

Um die optimalen Abstimmungsparameter zu ermitteln, wird ein Kreuzvalidierungsverfahren verwendet. Die Daten werden in Trainings- und Testdatensätze aufgeteilt, und es wird eine Rastersuche über mögliche λ- und a-Werte durchgeführt. Für jedes Paar wird das gemischte Copula-Modell an die Trainingsdaten angepasst, und seine penalisierte Likelihood wird anhand der Testdaten berechnet. Ziel ist es, die Likelihood in Abhängigkeit von den Parametern der Einzelcopulas abzüglich des auf die Gewichte angewandten SCAD-Strafterms zu maximieren.

Sobald die optimalen SCAD-Parameter ermittelt sind, werden die endgültigen Modellparameter anhand des gesamten Datensatzes geschätzt. Da die gleichzeitige Schätzung mehrerer Copulas rechenintensiv ist, kommt bei diesem Verfahren der Expectation-Maximization-Algorithmus (EM-Algorithmus) zum Einsatz.

  1. Der Prozess beginnt mit einer zweistufigen Schätzung zur Ermittlung der Startwerte. Die Gewichte werden in der Regel gleichmäßig verteilt, und die anfänglichen Copula-Parameter werden entsprechend ihrer jeweiligen Einschränkungen auf gültige Werte festgelegt.
  2. Der nächste Schritt ist der Erwartungsschritt. In dieser Phase werden die Gewichte verfeinert. Dabei wird berechnet, mit welcher Wahrscheinlichkeit bzw. welchem Anteil jeder Datenpunkt einer bestimmten Copula-Komponente zugeordnet wird.
  3. Der letzte Schritt des EM-Algorithmus ist der Maximierungsschritt. In dieser Phase werden die einzelnen Copula-Parameter mit einer Likelihood-Maximierung, gewichtet mit den im vorherigen Schritt berechneten Werten, aktualisiert.

Diese Schritte werden so lange wiederholt, bis eine Konvergenz erreicht ist, wodurch sichergestellt wird, dass die gemischte Copula die beobachteten Abhängigkeiten widerspiegelt. Im nächsten Abschnitt wird der Code erläutert, mit dem die soeben beschriebenen Prozeduren umgesetzt werden. 


MQL5-Implementierung gemischter Copula-Modelle

Die hier beschriebene Implementierung ist eine Adaption eines ursprünglich in Python geschriebenen Hudson- und Thames-Repositorys. Die Kernlogik für die native MQL5-Implementierung gemischter Copulas ist in der Header-Datei mixed.mqh enthalten. In dieser Datei bildet die abstrakte Klasse CMixedCopula die Grundlage für alle Implementierungen gemischter Copulas. Diese Klasse definiert mehrere geschützte Eigenschaften zur Verwaltung der Mischung.

Die Klasse definiert das Array m_copulas, das die einzelnen Copula-Familien darstellt. Die jeder Copula zugewiesenen Mischungsverhältnisse werden im Vektor m_weights gespeichert. Die spezifischen Parameter für jede einzelne Copula-Komponente sind alle in einem Vektor, m_cop_params, gespeichert. Durch die Verwendung einer abstrakten Basisklasse bleibt die Architektur modular. Dies ermöglicht die Erstellung verschiedener Copula-Kombinationen unter Beibehaltung einer einheitlichen Schnittstelle.

//+------------------------------------------------------------------+
//|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];
     }

Die Klasse CMixedCopula bietet außerdem Methoden für die statistische Analyse, die Generierung von Stichproben und die Modellverwaltung. Zur Auswertung der statistischen Eigenschaften gemischter Copula-Modelle definiert die Klasse die Methode Copula_PDF(), die die gemeinsame Dichte der Copula berechnet.

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_;
     }

Die Methode Copula_CDF() berechnet die gemeinsame kumulative Verteilungsfunktion der Copula, und die Methode Conditional_Probability() ermittelt die bedingte kumulative Wahrscheinlichkeit.

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_;
     }

Die Methode Sample() dient dazu, synthetische Datenpunkte aus einem angepassten gemischten Copula-Modell zu generieren.

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;
     }

Die Methoden Save() und Load() ermöglichen die Persistenz von Modellen, sodass der Zustand eines trainierten Modells gespeichert und wiederhergestellt werden kann, ohne dass ein erneutes Training erforderlich ist.

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;
     }

Abschließend definiert die Klasse zwei virtuelle Methoden, die zum Überschreiben vorgesehen sind. Die Methode Fit() sollte die Logik implementieren, mit der die Parameter der Copula anhand einer Stichprobe von Pseudo-Beobachtungen geschätzt werden, indem die als Methode Penalized_Log_Likelihood() definierte Likelihood-Funktion unter Einbeziehung der SCAD-Straffunktion minimiert wird.

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;
     }

In mixed.mqh ist außerdem die Aufzählung ENUM_COPULA_MIX definiert, die eine Liste der implementierten gemischten Copula-Typen bereitstellt, um die Auswahl zu erleichtern.

//+------------------------------------------------------------------+
//| 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
  };

Die Klasse CFGMixCop repräsentiert die gemischte Clayton-Frank-Gumbel-Copula. Sie verfügt über einen parametrischen Konstruktor für Fälle, in denen der Benutzer die Modellparameter lieber explizit definieren möchte, anstatt sie anhand von Daten anzupassen.

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

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

Bei Verwendung des parametrischen Konstruktors müssen die Parameter in Vektor-Containern angegeben werden. Sowohl die Copula-Parameter als auch die ihnen zugeordneten Gewichte müssen einer bestimmten Reihenfolge folgen:

  1. Clayton-Parameter
  2. Frank-Parameter
  3. Gumbel-Parameter

Ebenso geben alle aufgerufenen Getter-Methoden Werte in dieser einheitlichen Reihenfolge zurück, wodurch eine vorhersehbare Datenverarbeitung im gesamten Modell gewährleistet wird. Die Methode Fit() übernimmt die Schätzung des Modells anhand der Daten. 

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);
     }  
  • Die Eingabevariable ist eine Matrix (mindestens 2 Spalten) mit Pseudo-Beobachtungen (Rohwerte, die auf das Intervall [0,1] transformiert wurden).
  • Die Eingabe der Funktion maxiter ist eine ganze Zahl, die die maximale Anzahl der Iterationen für den EM-Algorithmus festlegt.
  • Die Eingabewerte gamma_scad und a_scad sind vom Typ double und stellen die SCAD-Hyperparameter dar.
  • Der Eingabewert weight_margin ist ein Schwellenwert, sodass Gewichte, die unter diesen Grenzwert fallen, auf Null gekürzt werden, um die Sparsität zu gewährleisten.

Bei erfolgreicher Ausführung gibt Fit() den Wert der nicht penalisierten Log-Likelihood zurück; andernfalls wird EMPTY_VALUE zurückgegeben. Die zentrale Schätzlogik ist in der geschützten Methode _fit_quantile_em() gekapselt, die den iterativen Prozess steuert.

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;
     }
Die geschützte Methode _expectation_step() implementiert die Erwartungsphase des EM-Algorithmus. Diese Methode arbeitet je nach den Werten der angegebenen SCAD-Parameter in zwei unterschiedlichen Modi. Sind beide SCAD-Parameter gültig (Sparsitätsparameter > 0,0 und Bias-Parameter > 2,0), wird die penalisierte Likelihood herangezogen, wodurch das Modell eine automatische Variablenselektion durchführen kann, indem es insignifikante Gewichte gegen Null drückt. Ist hingegen einer der beiden SCAD-Parameter ungültig, wird die SCAD-Strafe deaktiviert, und die Methode greift standardmäßig auf eine gewöhnliche Maximum-Likelihood-Schätzung (MLE) zurück.
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;
     }

Der Maximierungsschritt ist als Methode _maximization_step() definiert.

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;
     }

Dieser Schritt nutzt den BLEIC-Minimierungsalgorithmus (Boundary, Linear Equality-Inequality Constraints) von ALGLIB. Der Minimierer optimiert die in mlqfunc() definierte penalisiertе Log-Likelihood, die über die öffentliche Methode objective() an ALGLIB übergeben wird. Dies wird über die öffentliche Methode objective() bereitgestellt, wodurch die ALGLIB-Optimierer-Instanz während ihrer Ausführung auf die Funktion zugreifen kann.

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);
     }

Die Methode Fit() löst den Minimierungsprozess aus. Die Art der zu minimierenden Zielfunktion hängt von den an die Methode übergebenen SCAD-Parametern ab. Ist einer der beiden Parameter ungültig (gamma_scad <= 0,0 oder a_scad <= 2,0), wird als Zielfunktion standardmäßig auf die gewöhnliche Maximum-Likelihood-Schätzung (MLE) ohne Strafterm verwendet. Ansonsten ist die Zielfunktion der penalisierte MLE.

Die Klasse CTGMixCop, die die gemischte Clayton-Student-t-Gumbel-Copula implementiert, folgt einem Aufbau, der nahezu identisch mit dem der Klasse CFGMixCop ist. Der wesentliche Unterschied besteht darin, dass die Frank-Copula durch die Student-t-Copula ersetzt wird.

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]);
              }
           }
        }
     }

In der Header-Datei mixed.mqh ist außerdem eine spezielle Routine zur Optimierung der SCAD-Hyperparameter definiert. Die Funktion tune_scad_parameters() führt eine Rastersuche durch, um die Regularisierungsparameter zu optimieren.

//+------------------------------------------------------------------+
//|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;
  }

Es akzeptiert die folgenden Eingaben.

  • Die Eingabedaten bestehen aus einer Matrix von Pseudo-Beobachtungen, wobei jede Spalte eine Zeitreihe darstellt.
  • Die Funktion definiert dann sechs Eingabevariablen: Den ersten drei ist das Präfix gamma vorangestellt, den letzten drei das Präfix a. Diese legen die Grenzen und die Granularität der Rastersuche nach dem optimalen SCAD-Hyperparameterpaar fest.
  • Argumente mit dem Suffix count geben die Anzahl der zu prüfenden Werte an, die innerhalb der durch die Argumente mit den Suffixen start bzw. stop definierten Bereiche ausgewertet werden sollen.
  • Der Mix-Eingabewert ist eine Aufzählung, die angibt, welches gemischte Copula-Modell während der Suche ausgewertet werden soll.
  • Der Parameter shuffle ist ein boolescher Wert, der festlegt, ob die Daten vor der Aufteilung zufällig angeordnet werden sollen, und der Parameter seed ist ein Startwert für den Pseudozufallszahlengenerator, um reproduzierbare Ergebnisse zu gewährleisten.
  • Das boolesche Flag show_details legt fest, ob die Ausführungsdetails jeder Iteration im Protokoll des Terminals ausgegeben werden sollen.
  • Die Referenzeingaben out_a und out_gamma sind die Ausgabeparameter, in denen die von der Prozedur ermittelten optimalen SCAD-Parameter gespeichert werden.

Die SCAD-Straffunktion und ihre Ableitung sind in utils.mqh definiert. Diese implementieren den Schrumpfungsmechanismus, wodurch das Modell zwischen signifikanten und vernachlässigbaren Copula-Gewichten unterscheiden kann.

//+------------------------------------------------------------------+
//|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;
  }

Im nächsten Abschnitt befassen wir uns mit der Auswahl geeigneter Hyperparameter für die SCAD-Straffung. Nutzer sollten sich bestimmter Feinheiten bewusst sein, die über die bloße Durchführung des Kreuzvalidierungsverfahrens hinausgehen.


Optimierung der SCAD-Hyperparameter

Im Zusammenhang mit der SCAD-Straffunktion bestimmen die Abstimmungsparameter λ (Sparsität) und a (Bias-Reduktion) den Schwellenwert für die Sparsität bzw. die Rate der Bias-Reduktion. Zwar richten sich die optimalen Werte letztendlich nach den Daten, doch gibt es in der wissenschaftlichen Literatur und in der Praxis etablierte Bereiche. Der Parameter zur Verringerung der Verzerrung steuert, wie schnell sich die Strafe abflacht. Bei der Anpassung des Bias-Parameters sollten nur Werte größer als zwei berücksichtigt werden; der Parameter muss strikt größer als zwei sein, damit die Funktion Schwellenwerteigenschaften beibehält, die sich vom Hard-Thresholding unterscheiden.

Der Sparsitätsparameter hingegen reagiert empfindlich auf den Umfang der Trainingsdaten und die Stichprobengröße. Er legt den Schwellenwert fest, unterhalb dessen Koeffizienten auf Null gesetzt werden. Der Wertebereich, der bei der Anpassung dieses Parameters berücksichtigt wird, reicht in der Regel von 0 bis 1. Die Untergrenze sollte klein genug sein, damit sich das Modell dem unbestraften Maximum-Likelihood-Schätzer (MLE) annähern kann – zum Beispiel 0,001. Umgekehrt sollte die Obergrenze groß genug sein, um alle Koeffizienten (die Gewichte, die auf die einzelnen Copulas in der Mischung angewendet werden) potenziell auf Null zu setzen. Im Allgemeinen erfordert eine größere Stichprobengröße einen kleineren Sparsitätsparameter.

Ist der Sparsitätsparameter hoch, wird das Modell sparsamer, was dazu führt, dass viele Mischungsgewichte den Wert Null annehmen. Ist der Parameter zur Verringerung der Verzerrung hoch, bleibt die Strafe länger in Kraft, wodurch der Übergang vom verzerrten zum unverzerrten Zustand deutlich langsamer verläuft. Je näher der Parameter zur Bias-Reduktion an 2 liegt, desto schneller fällt die Strafe ab.

Um diese Konzepte in der Praxis zu testen, veranschaulicht das Skript Scad_CV.mq5 die Funktionsweise der Funktion 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);
  }
//+------------------------------------------------------------------+

Die standardmäßig vom Benutzer anpassbaren Eingabewerte des Skripts Scad_CV.ex5 legen ein relativ enges Raster an Werten für die Sparsität und den Bias fest. Diese Konfiguration gewährleistet, dass das Skript bei Stichprobengrößen von etwas mehr als tausend Beobachtungen effizient ausgeführt wird. Beachten Sie, dass das Testraster einen Wert von 2,0 für den Bias-Parameter enthält; da dies ein ungültiger Wert für den Bias-Parameter ist, deaktiviert dies die SCAD-Strafe vollständig. Auf diese Weise können wir feststellen, ob die Minimierung der nicht penalisierten Likelihood zu besseren Ergebnissen führt. Wird das Skript mit der ausgewählten Clayton-Frank-Gumbel-Mischung ausgeführt, ergibt sich folgende Ausgabe.

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

Die Ergebnisse zeigen, dass die optimalen SCAD-Parameter für die Clayton-Frank-Gumbel-Mischung eine Sparsität von 0,25075 und ein Bias von 10 sind. Die nächsten Ergebnisse, die sich auf die Clayton-Student-Gumbel-Mischung beziehen, sind besonders aufschlussreich.

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

In diesem Fall liegen die optimalen SCAD-Parameter bei 1,0 und 4,0. Ein Sparsitätswert von 1,0 ist recht hoch und hat dazu geführt, dass alle Gewichte auf Null gesetzt wurden – ein unsinniges Ergebnis für ein Mischmodell. Ein solches Ergebnis deutet oft darauf hin, dass das jeweilige Mischmodell möglicherweise nicht für die Daten geeignet ist oder dass die Straffunktion zu streng ist.

In diesem Fall ist es ratsam, zunächst zu versuchen, das Modell mithilfe des MLE-Ansatzes ohne die SCAD-Straffunktion anzupassen. Wenn der MLE-Ansatz sinnvolle Gewichte liefert, bestätigt sich, dass das Problem in einem zu großen Sparsitätsparameter liegt. In solchen Fällen sollte man entweder die MLE-Methode anwenden oder die Obergrenze der während des Kreuzvalidierungsverfahrens getesteten Sparsitätsparameter senken.

Daher lassen wir die Versuche außer Acht, bei denen ausschließlich Nullwerte ermittelt wurden. Dabei stellen wir fest, dass der MLE-Ansatz ohne Penalisation das Likelihood-Maximum erzielt. Zum jetzigen Zeitpunkt haben wir die optimalen SCAD-Parameter für die Clayton-Frank-Gumbel-Mischung (0,25075, 10) und die Clayton-Student-Gumbel-Mischung (0,001, 2,0) ermittelt. Diese Informationen können nun genutzt werden, um gemischte Copula-Modelle unter Verwendung des vollständigen Trainingsdatensatzes zu erstellen und zu vergleichen.


Modellauswahl und -vergleich

Das Skript MixedCopulaSelection.mq5 dient dazu, die gemischte Copula zu ermitteln, die am besten zu einer bestimmten Datenstichprobe passt.

//+------------------------------------------------------------------+
//|                                         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;
     }
//---
  }
//+------------------------------------------------------------------+

Dabei handelt es sich um ein Vergleichsverfahren, bei dem die gemischten Copula-Modelle nach Clayton-Frank-Gumbel und Clayton-Student-Gumbel anhand desselben Trainingsdatensatzes angepasst werden, wobei je nach den zugewiesenen SCAD-Parametern entweder ein penalisiertes oder ein nicht penalisiertes Likelihood-Verfahren zum Einsatz kommt. Das Skript ermittelt, welche Mischung die beste statistische Anpassung liefert, indem es die jeweiligen Informationskriterien vergleicht. Das besser passende Modell wird anschließend serialisiert und in einer Datei gespeichert. Nach erfolgreicher Ausführung gibt das Terminal ein Protokoll aus, in dem die Log-Likelihood-Werte und der Dateiname des gespeicherten Modells bestätigt werden. Nachfolgend finden Sie die Ausgabe des Skripts unter Verwendung der SCAD-Parameter, die im Rahmen des Kreuzvalidierungsverfahrens ermittelt wurden.

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.

Als am besten passendes gemischtes Copula-Modell hat sich die Clayton-Frank-Gumbel-Mischung erwiesen. Ein Vergleich dieser Ergebnisse mit angepassten Einzel-Copula-Modellen zeigt, dass die am besten angepasste gemischte Copula eine schlechtere Anpassung liefert als das am besten angepasste Einzel-Copula-Modell. 

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

Ungeachtet dessen können wir das gemischte Copula-Modell weiterhin anstelle des Einzel-Copula-Modells im Expert Advisor SimpleCopulaStrategy.ex5 verwenden, der in Teil 2 der Artikelserie Bivariate Copulas vorgestellt wurde. Der Indikator GoldMixedCopulaSignals.mq5 wurde so angepasst, dass er das Mixed-Copula-Modell nutzt, und SimpleMixedCopulaStrategy.mq5 dient als auf dem Mixed-Copula-Modell basierender EA. 

Indikator GoldMixedCopulaSignals


Schlussfolgerung

In diesem Artikel haben wir die Implementierung von Mixed-Copula-Modellen in MQL5 erfolgreich demonstriert. Indem wir über einkomponentige Copulas hinausgegangen sind, haben wir einen Rahmen geschaffen, mit dem sich die nuancierten Abhängigkeiten, die in finanziellen Zeitreihen häufig vorkommen, besser modellieren lassen. Zwar erfordert der Ansatz mit gemischten Copulas einen höheren Rechenaufwand und eine sorgfältige Abstimmung der Hyperparameter, doch das Potenzial zur Erstellung überlegener Copula-Modelle könnte den Aufwand lohnen.

Die Integration von ALGLIB für die nichtkonvexe Optimierung und die modulare Klassenstruktur von CMixedCopula bilden die Grundlage für die Untersuchung höherdimensionaler Mischungen. Letztendlich hängt der Erfolg der Anwendung von Copula-Modellen bei der Strategieentwicklung von der Repräsentativität der Trainingsdaten und der regelmäßigen Neukalibrierung der Modelle ab, um sicherzustellen, dass sie weiterhin mit der sich wandelnden Marktdynamik im Einklang stehen. Der gesamte im Artikel erwähnte Code ist im Anhang zu finden, zusammen mit Beschreibungen der in der Tabelle aufgeführten Dateien. 

Datei  Beschreibung 
MQL5/include/Copulas/ Dieser Ordner enthält die Header-Dateien aller bisher implementierten Copula-Modelle, einschließlich der in diesem Artikel beschriebenen.
MQL5/include/ECDF/ Dieser Ordner enthält die Header-Dateien zur Implementierung der empirischen kumulativen Verteilungsfunktion. 
MQL5/include/np.mqh Eine Header-Datei mit verschiedenen Hilfsfunktionen für Vektoren und Matrizen. 
MQL5/scripts/Scad_CV.mq5 Dieses Skript veranschaulicht ein Kreuzvalidierungsverfahren, das zur Optimierung der SCAD-Hyperparameter verwendet wird.
MQL5/scripts/MixedCopulaSelection.mq5 Das Skript passt gemischte Copula-Modelle an einen Beispieldatensatz an und vergleicht diese, um die am besten passende Copula auszuwählen. 
MQL5/indicators/GoldMixedCopulaSignals.mq5 Ein Indikator, der ein gemischtes Copula-Modell verwendet. 
MQL5/experts/SimpleMixedCopulaStrategy.mq5 Ein EA, der auf Signalen basiert, die aus einem gemischten Copula-Modell generiert werden. 

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19930

Beigefügte Dateien |
Letzte Kommentare | Zur Diskussion im Händlerforum (3)
Jacryptoking
Jacryptoking | 13 Feb. 2026 in 08:08
Dinge entwickeln, die auf dem malaysischen SnR basieren … SMC
cemal
cemal | 15 Feb. 2026 in 22:16

Ich habe die Dateien in die richtigen Ordner kopiert und außerdem die Algib-Dateien https://www.mql5.com/de/code/1146 heruntergeladen und in die richtigen Ordner kopiert, aber wenn ich den Indikator „GoldMixedCopula Signals“ kompiliere, erhalte ich 100 Fehlermeldungen. Was mache ich falsch?

Fehler

Maxim Kuznetsov
Maxim Kuznetsov | 19 Mai 2026 in 10:15
cemal #:

Ich habe die Dateien in die entsprechenden Ordner verschoben und ich habe die Dateien Algib https://www.mql5.com/de/code/1146“ heruntergeladen und in die entsprechenden Ordner verschoben, aber wenn ich den Indikator „GoldMixedCopula Signals“ kompiliere, erhalte ich 100 Fehlermeldungen. Was mache ich falsch?


Du hast den Screenshot falsch gemacht :-)

Wenn du Hilfe bei der Analyse und Behebung der Fehler benötigst, muss der erste Fehler auf dem Screenshot zu sehen sein. Alle anderen Fehler sind in der Regel „sekundär“, d. h. sie sind die Folge davon.

MetaTrader 5 – Blaupause für maschinelles Lernen (Teil 7): Von verstreuten Experimenten zu reproduzierbaren Ergebnissen MetaTrader 5 – Blaupause für maschinelles Lernen (Teil 7): Von verstreuten Experimenten zu reproduzierbaren Ergebnissen
Im neuesten Teil dieser Reihe gehen wir über einzelne Techniken des maschinellen Lernens hinaus und befassen uns mit dem Forschungschaos, unter dem viele quantitative Trader leiden. Im Mittelpunkt dieses Artikels steht der Übergang von Ad-hoc-Experimenten in Notebooks zu einer klar strukturierten, produktionsreifen Pipeline, die Reproduzierbarkeit, Nachvollziehbarkeit und Effizienz gewährleistet.
Workshop zu benutzerdefinierten Indikatoren (Teil 2): Entwicklung eines praxistauglichen Supertrend-Expert-Advisors in MQL5 Workshop zu benutzerdefinierten Indikatoren (Teil 2): Entwicklung eines praxistauglichen Supertrend-Expert-Advisors in MQL5
Erfahren Sie, wie Sie einen auf dem Supertrend basierenden Expert Advisor in MQL5 von Grund auf erstellen. Der Artikel behandelt die Einbindung des Indikators als Ressource, das Auslesen von Pufferwerten bei geschlossenen Kerzen, das Erkennen bestätigter Trendwenden, das Ausrichten offener Positionen am Signal sowie die Konfiguration von Stop-Loss-Modi und der Positionsgrößenbestimmung. Abschließend werden die Einrichtung des Strategy Testers und reproduzierbare Tests behandelt, sodass Sie am Ende über einen konfigurierbaren EA sowie einen klaren Rahmen für weitere Untersuchungen und Erweiterungen verfügen.
Verwendung des MQL5-Wirtschaftskalenders zur Nachrichtenfilterung (Teil 1): Implementierung von Zeitfenstern vor und nach Nachrichtenereignissen in MQL5 Verwendung des MQL5-Wirtschaftskalenders zur Nachrichtenfilterung (Teil 1): Implementierung von Zeitfenstern vor und nach Nachrichtenereignissen in MQL5
Wir entwickeln einen kalendergesteuerten Nachrichtenfilter vollständig in MQL5 und verzichten dabei auf Webanfragen und externe DLLs. Teil 1 behandelt das Laden und Zwischenspeichern von Ereignissen, deren Zuordnung zu Symbolen nach Währung, das Filtern nach Bedeutung, die Definition von Vor- und Nachlaufzeitfenstern sowie das Sperren neuer Trades während aktiver Nachrichten, mit der optionalen Möglichkeit, Positionen vor Nachrichtenereignissen zu schließen. Das Ergebnis ist ein konfigurierbarer, prop-firm-tauglicher Kontrollmechanismus, der falsche Handelspausen vermeidet und Einstiegspunkte bei hohen Schwankungen schützt.
Einführung in MQL5 (Teil 40): Einsteigerleitfaden zur Dateiverarbeitung in MQL5 (II) Einführung in MQL5 (Teil 40): Einsteigerleitfaden zur Dateiverarbeitung in MQL5 (II)
Erstellen Sie in MQL5 ein CSV-Trading-Journal, indem Sie den Kontoverlauf für einen festgelegten Zeitraum auslesen und strukturierte Datensätze in eine Datei schreiben. Der Artikel erläutert die Zählung von Transaktionen, das Abrufen von Tickets, das Zuordnen von Symbolen und Ordertypen sowie die Erfassung von Daten zur Eröffnung (Losgröße, Zeitpunkt, Kurs, SL/TP) und zur Schließung (Zeitpunkt, Kurs, Gewinn, Ergebnis) mithilfe dynamischer Arrays. Das Ergebnis ist ein übersichtliches, dauerhaft verfügbares Trading-Journal, das sich für Analysen und Berichte eignet.