English Русский 中文 Español 日本語 Português
preview
Implementierung des Janus-Faktors in MQL5

Implementierung des Janus-Faktors in MQL5

MetaTrader 5Beispiele | 31 Mai 2023, 10:11
228 0
Francis Dube
Francis Dube

Einführung

Jeder Händler weiß, dass die Marktpreise einem von zwei großen Mustern folgen. Die Preise bilden entweder Trends oder sie bewegen sich seitwärts. Infolgedessen lassen sich die Strategien der Marktteilnehmer weitgehend darauf beschränken, entweder dem Trend zu folgen oder gegen den Trend zu handeln. Der Janus-Faktor ist eine Theorie des Marktverhaltens, die diese Dualität aufgreift. In diesem Artikel werden wir die Grundlagen dieser Methode erläutern und auch die Anwendung von Indikatoren aufzeigen, die diese Analyse erleichtern.

Rückmeldung

Nach der Janus-Theorie werden die Märkte durch das Zusammenspiel von Preisen und der Reaktion der Händler auf diese Preise bestimmt. Anderson verglich dieses Zusammenspiel mit einem Feedback-System. Weitere Informationen über Feedback-Systeme finden Sie bei Wikipedia. Wenn sich die Märkte im Trend befinden, zeigen die Marktteilnehmer Vertrauen, indem sie ihre Gewinne laufen lassen. In einem Aufwärtstrend bestätigen höhere Kurse die Erwartungen der Marktteilnehmer an den Trend, was wiederum zu mehr Käufen führt. Dies hat zur Folge, dass die Preise noch weiter steigen. 

Abb.1 Positive Rückkopplung in einem Aufwärtstrend

Abb.1 zeigt die positive Rückkopplung in einem Aufwärtstrend.


In einem Abwärtstrend können fallende Kurse den Händlern Angst einflößen und sie dazu veranlassen, zu verkaufen, um Verluste zu minimieren. Weitere Verkäufe üben Druck auf die Preise aus und drücken sie nach unten. Die Entwicklung der Märkte ist daher ein Beispiel für eine positive Rückkopplung. Die Preise werden sich weiter beschleunigen, bis die Händler auf die Preisänderungen reagieren. 

Abb.2 Poistive Rückkopplung in einem Abwärtstrend

Abb. 2 zeigt eine positive Rückkopplung in einem Abwärtstrend.

Eine negative Rückkopplung liegt dann vor, wenn Händler wenig Vertrauen in den Markt haben und daher beschließen, nach jeder Kursbewegung frühzeitig Gewinne mitzunehmen. Frühzeitige Gewinnmitnahmen zerstören das Momentum und begrenzen die Größe der Kursbewegungen. In Verbindung mit dem ständigen Kampf zwischen Bullen und Bären führt dies zu einer Stabilisierung der Preise.

Abb.3 Negative Rückkopplung

Abb. 3: Negative Rückkopplung auf dem Markt

Kapitalfluss

Ein wichtiges Element dieses Konzepts der Rückkopplung sind die Arten von Symbolen, die von den Händlern bevorzugt werden, wenn unterschiedliche Bedingungen vorherrschen. Andersons Analyse der Aktien legt nahe, dass die Händler in einem Aufwärtstrend Aktien bevorzugten, die eine relativ bessere Performance aufwiesen, während sie sich von ihren unterdurchschnittlichen Pendants trennten. In einem Abwärtstrend verloren unterdurchschnittlich abschneidende Aktien am meisten an Wert, da die Händler sie gezielt für Leerverkäufe nutzten. 

In Ermangelung eines Trends gab es nicht viel, was stärkere und schwächere Aktien voneinander trennte, da die Händler bestimmte Kursniveaus zum Ein- und Ausstieg wählten. Vor diesem Hintergrund stellte er die Hypothese auf, dass es durch die Analyse der relativen Performance einer Reihe von Aktien möglich sein würde, Perioden negativer und positiver Rückkopplung zu erkennen.


Janus-Berechnungen

Um die Performance zu ermitteln, werden die periodischen Ergebnisse berechnet. Die Ergebnisse einer Reihe von untersuchten Aktien werden kombiniert, um einen Durchschnittswert zu erhalten, der als Basismaßnahme verwendet wird. Ruft die Benchmark-Ergebnisse auf. Die Kollektion der zu bewertenden Aktien wird als Index bezeichnet.

In seinem Buch The Janus Factor - Trend Follower's Guide to Market Dialectics beschreibt Anderson verschiedene Metriken von Aktien, die er auf der Grundlage von Ergebnissen berechnet, um einen Einblick in das Verhalten des Marktes zu erhalten. 


Die Janus Bibliothek - janus.mqh

Die Daten und Routinen, die allen Janus-Berechnungen gemeinsam sind, sind in janus.mqh enthalten. Die Include-Datei enthält Deklarationen für drei nutzerdefinierte Typen:

    Die Klasse CSymboldata behandelt Symboldaten und damit verbundene Operationen. 

    //+------------------------------------------------------------------+
    //|Class which manage the single Symbol                              |
    //+------------------------------------------------------------------+
    class CSymbolData
      {
    
    private:
       string            m_name;    // name of the symbol
       ENUM_TIMEFRAMES   m_timeframe;// timeframe
       int               m_length;  // length for copy rates
       MqlRates          m_rates[]; // store rates
       datetime          m_first;   // first date on server or local history
    
       datetime          SetFirstDate(void)
         {
          datetime first_date=-1;
          if((datetime)SymbolInfoInteger(m_name,SYMBOL_TIME)>0)
             first_date=(datetime)SeriesInfoInteger(m_name,m_timeframe,SERIES_FIRSTDATE);
          //---
          if(first_date==WRONG_VALUE || first_date==0)
            {
             if(TerminalInfoInteger(TERMINAL_CONNECTED))
               {
                while(!SeriesInfoInteger(m_name,m_timeframe,SERIES_SERVER_FIRSTDATE,first_date) && !IsStopped())
                   Sleep(10);
               }
            }
          //---
    #ifdef DEBUG
          Print(m_name," FirstDate ",first_date);
    #endif
          return first_date;
         }
    
    public:
                         CSymbolData(string name,ENUM_TIMEFRAMES tf=PERIOD_CURRENT)
         {
          m_name = name;
          m_length = 0;
          m_timeframe = tf;
          SymbolSelect(m_name,true);
         }
    
                        ~CSymbolData(void)
         {
          ArrayFree(m_rates);
         }
       datetime          GetFirstDate(void)
         {
          m_first = SetFirstDate();
          return m_first;
         }
       string            GetName(void)
         {
          return m_name;
         }
    
       int               GetLength(void)
         {
          return m_length;
         }
    
       void              SetLength(const int set_length)
         {
          if(set_length>0)
            {
             m_length=set_length;
             ArrayResize(m_rates,m_length,m_length);
             ArraySetAsSeries(m_rates,true);
            }
         }
    
       bool              Update(void)
         {
          int copied = CopyRates(m_name,m_timeframe,0,m_length,m_rates);
    #ifdef DEBUG
          Print("copied ", copied, " requested ", m_length);
    #endif
          //--
          return copied == m_length;
         };
    
       MqlRates          GetRateAtPos(const int i)
         {
          if(i<0 || i>=m_length)
            {
    #ifdef DEBUG
             Print("Array out of range ",i,".Size of array is ",m_length);
    #endif
             return (i<0)?m_rates[0]:m_rates[m_length-1];
            }
          return m_rates[i];
         }
      };
    CSymbolCollection ist ein Container von CSymboldata-Objekten, der die Kollektion der zu analysierenden Symbole darstellt. Der Code für beide Klassen wurde einem Listing aus der Mql5-Codebasis entnommen. Es wurden Änderungen an der Indizierungsrichtung der zugrunde liegenden Puffer vorgenommen. Es ist zu beachten, dass alle Klassen ein Indexierungsformat von rechts nach links verwenden, wobei der Null-Index auf den letzten Balken verweist. 

    Um die Techniken des Janus-Faktors zu demonstrieren, werden wir ihn auf die Analyse von Devisenpaaren anwenden. Anderson hat die Methode ursprünglich für die Analyse von Aktien entwickelt, aber die Verfügbarkeit von Aktiensymbolen ist bei den meisten Brokern uneinheitlich.

    //+------------------------------------------------------------------+
    //| Class that mange the collection of symbols                       |
    //+------------------------------------------------------------------+
    class CSymbolCollection
      {
    
    private:
    
       int               m_calculation_length; // global length
       ENUM_TIMEFRAMES   m_collection_timeframe;  // timeframe of data
       string            m_raw_symbols;    // delimited symbol list
       datetime          m_synced_first;  // synced first bar opentime for all symbols
       bool              m_synced;        // flag of whether all symbols are synchronized
       CSymbolData       *m_collection[]; // Collection of Symbol Pointer
       int               m_collection_length; // Collection of Symbol Length
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       bool               CheckSymbolBars(const string __sym)
         {
          int bars=-1;
          bars=iBarShift(__sym,m_collection_timeframe,m_synced_first)+1;//SeriesInfoInteger(__sym,PERIOD_CURRENT,SERIES_BARS_COUNT);
    #ifdef DEBUG
          Print("Bars found in history for ",__sym," ",bars);
    #endif
          if(bars>=m_calculation_length)
             return(true);
          //---
          return(SyncSymbol(__sym));
         }
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       bool               SyncSymbol(const string __sym)
         {
          //--- load data step by step
          bool downloaded=false;
          datetime times[1];
          int bars=-1;
          
         /* if(MQLInfoInteger(MQL_PROGRAM_TYPE)==PROGRAM_INDICATOR)
            {
    #ifdef DEBUG
             Print(" cannot download ",__sym," history from an indicator");
    #endif
             return(downloaded);
            }*/
              
    #ifdef DEBUG
          Print(" downloading ",__sym," history");
    #endif
          while(!IsStopped() && !downloaded && TerminalInfoInteger(TERMINAL_CONNECTED))
            {
             //---
             while(!SeriesInfoInteger(__sym,m_collection_timeframe,SERIES_SYNCHRONIZED) && !IsStopped())
                Sleep(5);
             //---
             bars=Bars(__sym,PERIOD_CURRENT);
             if(bars>=m_calculation_length)
               {
                downloaded=true;
                break;
               }
             //--- copying of next part forces data loading
             if(CopyTime(__sym,m_collection_timeframe,m_calculation_length-1,1,times)==1)
               {
                downloaded=true;
                break;
               }
             //---
             Sleep(5);
            }
    #ifdef DEBUG
          if(downloaded)
             Print(bars," ",__sym," bars downloaded ");
          else
             Print("Downloading ",__sym," bars failed");
    #endif
          return(downloaded);
         }
    
    public:
    
                         CSymbolCollection(const ENUM_TIMEFRAMES tf=PERIOD_CURRENT)
         {
          m_raw_symbols="";
          m_collection_length = 0;
          m_calculation_length = -1;
          m_synced_first=0;
          m_synced=false;
          m_collection_timeframe=tf;
         }
    
                        ~CSymbolCollection(void)
         {
    
          for(int i=0; i<m_collection_length; i++)
            {
             if(CheckPointer(m_collection[i])==POINTER_DYNAMIC)
                delete m_collection[i];
            }
         }
       //+------------------------------------------------------------------+
       //|return the set timeframe for bars stored in the collection        |
       //+------------------------------------------------------------------+
       ENUM_TIMEFRAMES   GetTimeFrame(void)
         {
          return(m_collection_timeframe);
         }
       
       //+------------------------------------------------------------------+
       //|Checks the history available and syncs it across all symbols      |
       //+------------------------------------------------------------------+
       bool              CheckHistory(const int size)
         {
          if(size<=0)
             return(false);
    
          int available=iBarShift(NULL,m_collection_timeframe,m_synced_first)+1;
    
          if(available<size)
             m_calculation_length=available;
          else
             m_calculation_length=size;
    
    #ifdef DEBUG
          Print("synced first date is ", m_synced_first);
          Print("Proposed size of history ",m_calculation_length);
    #endif
    
          if(m_calculation_length<=0)
             return(false);
    
          ResetLastError();
    
          for(int i=0; i<m_collection_length; i++)
            {
             m_synced=CheckSymbolBars(m_collection[i].GetName());
             if(!m_synced)
               {
                Print("Not Enough history data for ", m_collection[i].GetName(), " > ", GetLastError());
                return(m_synced);
               }
             m_collection[i].SetLength(m_calculation_length);
            }
    
          return m_synced;
    
         }
    
    
       //+------------------------------------------------------------------+
       //| Add a symbol by name to the collection                           |
       //+------------------------------------------------------------------+
       int               Add(string name)
         {
          CSymbolData *ref = new CSymbolData(name,m_collection_timeframe);
          datetime f=ref.GetFirstDate();
          int found=GetIndex(name);
          if(f==WRONG_VALUE || found>-1)
            {
    #ifdef DEBUG
             if(f==WRONG_VALUE)
                Print("Failed to retrieve information for symbol ",name,". Symbol removed from collection");
             if(found>-1)
                Print("Symbol ",name,"already part of collection");
    #endif
             delete ref;
             return(m_collection_length);
            }
          ArrayResize(m_collection, m_collection_length + 1,1);
          m_collection[m_collection_length] = ref;
          //---
          if(f>m_synced_first)
             m_synced_first=f;
          //---
    
          return(++m_collection_length);
    
         }
       //+------------------------------------------------------------------+
       //|Return symbol name                                                |
       //+------------------------------------------------------------------+
       string            GetSymbolNameAtPos(int pos)
         {
          return m_collection[pos].GetName();
         }
       //+------------------------------------------------------------------+
       //|return index of symbol                                            |
       //+------------------------------------------------------------------+
       int               GetIndex(const string symbol_name)
         {
          for(int i=0; i<m_collection_length; i++)
            {
             if(symbol_name==m_collection[i].GetName())
                return(i);
            }
          //---fail
          return(-1);
         }
       //+------------------------------------------------------------------+
       //| Return Collection length                                         |
       //+------------------------------------------------------------------+
       int               GetCollectionLength(void)
         {
          return m_collection_length;
         }
    
       //+------------------------------------------------------------------+
       //| Update every currency rates                                      |
       //+------------------------------------------------------------------+
       bool              Update(void)
         {
    
          int i;
          for(i = 0; i < m_collection_length; i++)
            {
             bool res = m_collection[i].Update();
             if(res==false)
               {
                Print("missing data on " + m_collection[i].GetName());
                return false;
               }
            }
          return true;
         }
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       int               GetHistoryBarsLength(void)
         {
          return m_calculation_length;
         }
       //+------------------------------------------------------------------+
       //| Return MqlRates of currency at position                          |
       //+------------------------------------------------------------------+
       MqlRates          GetRateAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i);
         }
    
       //+------------------------------------------------------------------+
       //| Return Open price of currency at position                        |
       //+------------------------------------------------------------------+
       double            GetOpenAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).open;
         }
    
       //+------------------------------------------------------------------+
       //| Return Close price of currency at position                       |
       //+------------------------------------------------------------------+
       double            GetCloseAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).close;
         }
    
       //+------------------------------------------------------------------+
       //| Return High price of currency at position                        |
       //+------------------------------------------------------------------+
       double            GetHighAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).high;
         }
    
       //+------------------------------------------------------------------+
       //| Return Low price of currency at position                         |
       //+------------------------------------------------------------------+
       double            GetLowAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).low;
         }
    
       //+------------------------------------------------------------------+
       //| Return Median price of currency at position                      |
       //+------------------------------------------------------------------+
       double            GetMedianAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i))/2;
         }
    
       //+------------------------------------------------------------------+
       //| Return Typical price of currency at position                     |
       //+------------------------------------------------------------------+
       double            GetTypicalAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i) + GetCloseAtPos(pos,i))/3;
         }
    
       //+------------------------------------------------------------------+
       //| Return Weighted price of currency at position                    |
       //+------------------------------------------------------------------+
       double            GetWeightedAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i) + GetCloseAtPos(pos,i) * 2)/4;
         }
      };
    //+------------------------------------------------------------------+
    

     

    Der Code beginnt mit drei nutzerdefinierten Enumerationen, die im Folgenden dokumentiert werden.

    #include<Math\Stat\Math.mqh>
    #include <Arrays\ArrayObj.mqh>
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_INDEX_TYPE
      {
       INDEX_FOREX_MAJORS=0,//forex majors only list
       INDEX_CUSTOM,//custom symbol list
      };
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_PRICE
      {
       CLOSE=0,//close price
       MEDIAN,//median price
       TYPICAL,//typical price
       WEIGHTED//weighted price
      };
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_DIFF_TYPE
      {
       DIFF_PERCENT=0,//percent difference
       DIFF_LOG//log difference
      };
    
    
    Enumeration
    Beschreibung
    Optionen
    ENUM_INDEX_TYPE
    stellt die Art der Symbole dar, die in einer zu analysierenden Kollektion enthalten sind
    INDEX_FOREX - diese Option erstellt eine Kollektion von Symbolen, die aus allen Forex-Majors besteht. Ein Satz von 28 Symbolen.
    INDEX_CUSTOM - mit dieser Option muss die Kollektion der Symbole angegeben werden.
    ENUM_PRICE
    ermöglichen die Auswahl der Preisreihen, die für die Berechnungen verwendet werden sollen. Es ist zu beachten, dass die Preisinformationen als Zeitreihen gespeichert werden, sodass der letzte Preise bei Index 0 liegt.

    CLOSE - Schlusskurs
    MEDIAN - der Medianpreis - (Hoch+Tief)/2
    TYPICAL - der typische Preis - (Hoch+Tief+Schluss)/3
    WEIGHTED - der gewichtete Preis - (Hoch+Tief+Schluss+Schluss)/4

     ENUM_DIFF_TYPE
     Dies sind verschiedene Methoden der Differenzierung, die auf die ausgewählten Preisreihen angewendet werden können.
    DIFF_LOG - hier wird der Logarithmus des Quotienten verwendet
    DIFF_PERCENT - die prozentuale Differenz wird für diese Option verwendet

    Um die Klasse CJanus zu verwenden, müssen 5 Methoden verstanden werden. Nach der Erstellung eines CJanus-Objekts und bevor irgendeine Methode aufgerufen werden kann, sollte zunächst die Funktion Initialize aufgerufen werden. Die Methode erfüllt zwei wichtige Aufgaben: Zunächst initialisiert sie das Objekt CSymbolCollection und füllt es mit den ausgewählten Symbolen, die unseren Index bilden werden. Anschließend werden die historischen Balken der Symbole in der Kollektion überprüft und synchronisiert.

    //+------------------------------------------------------------------+
    //|Janus class for calculating janus family of indicators.           |
    //+------------------------------------------------------------------+
    class CJanus
      {
    private:
       CSymbolCollection* m_symbol_list;    //object container of symbols
       ENUM_PRICE         m_price_type;     //applied price for calculations
       ENUM_DIFF_TYPE     m_diff_type;      //method of differencing applied
       ENUM_INDEX_TYPE    m_index_type;     //type of index
       int                m_hist_size;      // synchronized size of history across all selected symbols in collection
       ENUM_TIMEFRAMES    m_list_timeframe; //timeframe for bars to be used in calculations
       //---private methods
       double            market_return(const uint barshift, const uint symbolshift);
       void              market_offense_defense(const uint barshift,const uint symbolshift,const uint rs_period,double& out_offense,double& out_defense);
       double            rs(const uint barshift,const uint symbolshift,const uint rs_period,uint lag=0);
       double            rs_off_def(const uint barshift, const uint symbolshift,const uint lag,const double median,const double index_offense, const double index_defense, double &array[]);
       //---
    public:
       //constructor
                         CJanus(void):m_symbol_list(NULL),
                         m_price_type(WRONG_VALUE),
                         m_diff_type(WRONG_VALUE),
                         m_index_type(WRONG_VALUE),
                         m_list_timeframe(WRONG_VALUE),
                         m_hist_size(0)
         {
    
         }
       // destructor
                        ~CJanus(void)
         {
          if(CheckPointer(m_symbol_list)==POINTER_DYNAMIC)
             delete m_symbol_list;
         }
       //public methods
       bool              Initialize(const ENUM_PRICE set_price_type, const ENUM_DIFF_TYPE set_diff_type, const ENUM_INDEX_TYPE set_index_type, ENUM_TIMEFRAMES set_timeframe,const int history_size, const string symbol_list);
       bool              Update(void);
       int               HistorySize(void);
       string            GetSymbolAt(const int sym_ind);
       int               GetSymbolsTotal(void);
       double            CalculateReturn(const uint barshift, const string symbol__);
       double            CalculateBenchMarkReturn(const uint barshift);
       double            CalculateSummedIndexReturn(const uint barshift);
       void              CalculateBenchMarkOffenseDefense(const uint barshift,const uint rs_period,double& out_offense,double& out_defense);
       void              CalculateMarketOffenseDefense(const uint barshift,const string symbol__,const uint rs_period,double& out_offense,double& out_defense);
       double            CalculateRelativeStrength(const uint barshift,const string symbol__,const uint rs_period,uint lag=0);
       void              CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard);
       double            CalculateRelativeStrengthSpread(const uint barshift,const uint rs_period,const double rs_percent_top);
       double            CalculateRelativeStrengthSpreadChange(const uint barshift,const uint rs_period,const double rs_percent_top);
    
      };

    Die Eingabeparameter der Methode Initialize() werden in der folgenden Tabelle erläutert.

    Parameter
    Beschreibung
     Datentyp
    set_price_type
    eine Enumeration, die die zugrunde liegenden Preisreihen festlegt, die für alle Berechnungen verwendet werden
    ENUM_PRICE
    set_diff_type
    legt die auf die Preisreihen angewandte Differenzierungsmethode fest
    ENUM_DIFF_TYPE
    set_index_type
    legt den Typ der Symbolkollektion fest, entweder nutzerdefiniert oder nur Forex-Majors; wenn nutzerdefiniert ausgewählt wird, müssen die Symbole in den Parametern von symbol_list angegeben werden
    ENUM_INDEX_TYPE
    set_timeframe
    gibt den Zeitrahmen der für die Berechnungen zu verwendenden Balken an
    ENUM_TIMEFRAME

    history_size
    definiert die maximale Anzahl von Balken, die angefordert werden sollen. Da wir es mit mehreren Symbolen zu tun haben, können wir auf diese Weise sicherstellen, dass für alle Symbole in der Kollektion die gleiche Anzahl historischer Balkendaten verfügbar ist.
    integer

     symbol_list
    wenn set_index_type auf INDEX_CUSTOM gesetzt ist, muss der Nutzer eine durch Komma getrennte Liste von Symbolen angeben, die die zu analysierende Symbolkollektion bilden soll
     string
    Andere erwähnenswerte Methoden sind:
    •  Die Methode Update() aktualisiert die Symboldaten, um die neuesten Kurse abzurufen.
    • Die Methode HistorySize() gibt die Anzahl der verfügbaren historischen Balken zurück. Beachten Sie, dass diese Zahl kleiner sein kann als die, die beim Aufruf der Methode Initialize angegeben wurde. Da es möglich ist, dass der Verlauf über alle Symbole hinweg variiert, gibt die Funktion die synchronisierte Verlaufsgröße zurück.
    • GetSymbolsTotals() gibt die Anzahl der Symbole zurück, die hinzugefügt und als im Terminal verfügbar bestätigt wurden.
    • GetSymbolAt() ermittelt den Symbolnamen nach Index.

    Die übrigen Methoden, denen das Präfix Calculate vorangestellt ist, führen Berechnungen durch und geben entweder einen oder zwei Werte zurück. Sie alle haben als ersten Eingabeparameter barshift, die Balkenverschiebung für den Balken, für den eine Berechnung durchgeführt werden soll.
    Die einzelnen Methoden werden bei der Umsetzung der verschiedenen Indikatoren erläutert.


    Messung der Leistung

    Wie bereits erwähnt, wird die Performance durch die Berechnung der Ergebnisse gemessen. Bei unserer Implementierung haben wir die Möglichkeit, den angewandten Preis und die Methode zur Berechnung der Ergebnisse zu wählen. Der Aufruf der Methode CalculateReturns() berechnet die Ergebnisse für den angegebenen barshift und das Symbol.

    //+------------------------------------------------------------------+
    //|private method that calculates the returns                        |
    //+------------------------------------------------------------------+
    double CJanus::market_return(const uint barshift, const uint symbolshift)
      {
       double curr,prev;
       curr=0;
       prev=1.e-60;
    
       switch(m_price_type)
         {
          case CLOSE:
             curr=m_symbol_list.GetCloseAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetCloseAtPos(symbolshift,barshift+1);
             break;
          case MEDIAN:
             curr=m_symbol_list.GetMedianAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetMedianAtPos(symbolshift,barshift+1);
             break;
          case TYPICAL:
             curr=m_symbol_list.GetTypicalAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetTypicalAtPos(symbolshift,barshift+1);
             break;
          case WEIGHTED:
             curr=m_symbol_list.GetWeightedAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetWeightedAtPos(symbolshift,barshift+1);
             break;
          default:
             return WRONG_VALUE;
         }
    
       if(prev==0)
          return(WRONG_VALUE);
    
       switch(m_diff_type)
         {
          case DIFF_PERCENT:
             return(((curr-prev)/prev)*100);
          case DIFF_LOG:
             return(MathLog(curr/prev));
          default:
             return(WRONG_VALUE);
         }
      }
    //+------------------------------------------------------------------+
    //|public method to calculate returns for single bar                 |
    //+------------------------------------------------------------------+
    double CJanus::CalculateReturn(const uint barshift, const string symbol_)
      {
       int sshift=m_symbol_list.GetIndex(symbol_);
       if(sshift>-1)
          return(market_return(barshift,sshift));
       else
          return(WRONG_VALUE);
      }

    Der nachstehende Code für den Indikator IndexReturns zeigt die Ergebnisse für das Chartsymbol und auch die Benchmark-Ergebnisse an.

    //+------------------------------------------------------------------+
    //|                                                 IndexReturns.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 2
    #property indicator_plots   2
    //--- plot IndexReturns
    #property indicator_label1  "IndexReturns"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrRed
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    //--- plot Returns
    #property indicator_label2  "Returns"
    #property indicator_type2   DRAW_LINE
    #property indicator_color2  clrBlue
    #property indicator_style2  STYLE_SOLID
    #property indicator_width2  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         IndexReturnsBuffer[];
    double         ReturnsBuffer[];
    CJanus *janus;
    
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,IndexReturnsBuffer,INDICATOR_DATA);
       SetIndexBuffer(1,ReturnsBuffer,INDICATOR_DATA);
      
       ArraySetAsSeries(IndexReturnsBuffer,true);
       ArraySetAsSeries(ReturnsBuffer,true);  
    //--- 
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetString(1,PLOT_LABEL,_Symbol+" Returns");
    //---
       IndicatorSetInteger(INDICATOR_DIGITS,8);
       IndicatorSetString(INDICATOR_SHORTNAME,"IndexReturns("+_Symbol+")");
    //---
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);     
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"IndexReturns("+_Symbol+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }   
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
       int limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-2;
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
          PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int bar=limit;bar>=1;bar--)
        {
         ReturnsBuffer[bar]=janus.CalculateReturn(bar,_Symbol);
         IndexReturnsBuffer[bar]=janus.CalculateBenchMarkReturn(bar);
        }    
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Der Indikator IndexReturns

    Der Indikator IndexReturns

    Die Benchmark

    Zur Berechnung der Benchmark-Ergebnisse verwenden wir die individuellen Ergebnisse der Symbole für jeden entsprechenden Zeitpunkt, um den Medianwert zu ermitteln. Diese Berechnung wird mit der Methode CalculateBenchmarkReturns() durchgeführt. 

    //+------------------------------------------------------------------+
    //|public method to calculate index returns                          |
    //+------------------------------------------------------------------+
    double CJanus::CalculateBenchMarkReturn(const uint barshift)
      {
       double sorted[];
       int size=m_symbol_list.GetCollectionLength();
    
       if(size<=0)
          return(WRONG_VALUE);
    
       ArrayResize(sorted,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=market_return(barshift,i);
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(0);
         }
    
       return(MathMedian(sorted));
      }


    Offensive und Defensive

    Um ein besseres Gefühl für die relative Leistung zu bekommen, entwickelte Anderson das Konzept der offensiven und defensiven Scores für ein Symbol. Der Offensiv-Score ist die Performance, die erzielt wird, wenn die Benchmark-Ergebnisse über dem Durchschnitt liegen. 

    Der defensive Score wird in ähnlicher Weise anhand der Ergebnisse berechnet, die erzielt werden, wenn die Ergebnisse der Benchmark unter dem Durchschnitt liegen. Dieser Wert zeigt, wie gut sich ein Symbol bei schwankenden Marktpreisen hält. 

    Zur Berechnung dieser Werte werden die Benchmark-Ergebnisse in zwei Teile aufgeteilt: die offensiven und die defensiven Benchmark-Ergebnisse. Diese werden berechnet, indem eine geeignete Fensterlänge gewählt wird, aus der ein durchschnittlicher Benchmark berechnet wird. 

    In der Klasse CJanus wird der Medianwert als Mittelwert über das angegebene Fenster verwendet. Die offensive Benchmark-Ergebnisse ergibt sich aus der Differenz zwischen den erzielten Ergebnissen und den durchschnittlichen Benchmark-Ergebnissen für die Benchmarks, die größer oder gleich dem Durchschnitt sind.

    Die defensiven Benchmark-Ergebnisse wird auf ähnliche Weise berechnet, indem Benchmark-Werte verwendet werden, die unter dem Durchschnitt des Fensters liegen.

    //+------------------------------------------------------------------+
    //|public method to calculate Index offense and defense scores       |
    //+------------------------------------------------------------------+
    void CJanus::CalculateBenchMarkOffenseDefense(const uint barshift,const uint rs_period,double& out_offense,double& out_defense)
      {
       out_defense=out_offense=WRONG_VALUE;
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return;
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return;
         }
    
       out_offense=i_offense;
       out_defense=i_defense;
    
       return;
      }

    Nach der Berechnung der offensiven und defensiven Benchmark-Ergebnisse wird die Funktion verlassen. Sie werden verwendet, um die Offensiv- und Defensiv-Scores für ein Symbol zu berechnen. Über dasselbe Zeitfenster, das für die Benchmarks verwendet wird, werden die erzielten Ergebnisse zu jedem Zeitpunkt, an dem die Benchmark-Ergebnisse größer oder gleich dem Fensterdurchschnitt ist, addiert. Diese Summe wird dann als ein Bruchteil des entsprechenden offensiven Benchmarks ausgedrückt und mit 100 multipliziert. 

    Eine ähnliche Berechnung wird mit der defensiven Benchmark-Ergebnisse durchgeführt. Die Methode CalculateSymbolOffenseDefense() implementiert die Berechnung die Offensiv- und Defensiv-Scores.

    //+------------------------------------------------------------------+
    //|public method to calculate market offense and defense                                                                  |
    //+------------------------------------------------------------------+
    void CJanus::CalculateSymbolOffenseDefense(const uint barshift,const string symbol__,const uint rs_period,double &out_offense,double &out_defense)
      {
       out_defense=out_offense=0.0;
       int sshift=m_symbol_list.GetIndex(symbol__);
       if(sshift>-1)
          market_offense_defense(barshift,sshift,rs_period,out_offense,out_defense);
       else
          return;
      }
    //+------------------------------------------------------------------+
    //|private method that calculates market defense and offense values  |
    //+------------------------------------------------------------------+
    void CJanus::market_offense_defense(const uint barshift,const uint symbolshift,const uint rs_period,double& out_offense,double& out_defense)
      {
       out_defense=out_offense=0.0;
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return;
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return;
         }
    
       double m_offense,m_defense;
       m_offense=m_defense=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             m_offense+=market_return(barshift+i,symbolshift);
          else
             m_defense+=market_return(barshift+i,symbolshift);
         }
    
       out_defense= (m_defense/i_defense) * 100;
       out_offense= (m_offense/i_offense) * 100;
    
      }

    Indem er die offensiven und defensiven Scores aufzeichnete, bemerkte Anderson die Muster, die sich je nach den vorherrschenden Marktbedingungen ergeben würden. In den Zeiten des positiven Feedbacks waren die Scores weit gestreut und es gab einen signifikanten Unterschied zwischen den Symbolen mit der besten und der schlechtesten Leistung. Im Gegensatz zu Zeiten des negativen Feedbacks, in denen sich der Unterschied zwischen den besten und den schlechtesten Leistungen verringert hat. Anderson bezeichnete dies als Expansion und Kontraktion des Indexes.

    Der folgende Code beschreibt das Skript OffensiveDefensiveScatterPlot. Es hat ähnliche Eingaben wie der obige Indikator und zeichnet Diagramme von offensiven und defensiven Scores als Animation.

    //+------------------------------------------------------------------+
    //|                                OffensiveDefensiveScatterPlot.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property script_show_inputs
    #include <Graphics\Graphic.mqh>
    #include<Janus.mqh>
    
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 25;
    input string BenchMarkSymbols="";
    input int MaxBars = 50;
    
    CJanus janus;
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //---
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         {
          Print("error with janus object");
          return;
         }
    //---
       janus.Update();
    //---
       double y[];
       double x[];
    
       int size=janus.GetSymbolsTotal();
    
       ArrayResize(x,size);
       ArrayResize(y,size);
    
       long chart=0;
       string name="OffenseDefense";
    
       for(int k=MaxBars-(int)AppliedPeriod-1; k>=0; k--)
         {
          for(int i=0; i<size; i++)
            {
             string ssy=janus.GetSymbolAt(i);
             janus.CalculateSymbolOffenseDefense(k,ssy,AppliedPeriod,y[i],x[i]);
            }
    
          CGraphic graphic;
          if(ObjectFind(chart,name)<0)
             graphic.Create(chart,name,0,0,0,780,380);
          else
             graphic.Attach(chart,name);
          //---
          graphic.CurveAdd(x,y,ColorToARGB(clrBlue),CURVE_POINTS,"DefensiveOffensive ");
          //---
          graphic.CurvePlotAll();
          //---
          graphic.Update();
          Sleep(1*1000);
          graphic.Destroy();
          ChartRedraw();
    
         }
    
       ChartSetInteger(0,CHART_SHOW,true);
    
      }
    //+------------------------------------------------------------------+
    

    Das Skript erzeugt die folgende Grafik:

    Offense/Defense Streudiagramm


    Relative Stärke

    Wenn die Offensiv- und Defensivwerte eines Symbols mit der nachstehenden Formel verknüpft werden, erhält man die relative Stärke eines Symbols.

    Formel der relativen Stärke

     Die Herleitung dieser Formel ist im Buch von Anderson dokumentiert. Die relative Stärke wird mit der Methode CalculateRelativeStrength() berechnet.

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength                      |
    //+------------------------------------------------------------------+
    double CJanus::CalculateRelativeStrength(const uint barshift,const string symbol__,const uint rs_period,uint lag=0)
      {
       int sshift=m_symbol_list.GetIndex(symbol__);
       if(sshift>-1)
          return(rs(barshift,sshift,rs_period,lag));
       else
          return(WRONG_VALUE);
      }
    


    //+------------------------------------------------------------------+
    //|private method that calculates the relative strength              |
    //+------------------------------------------------------------------+
    double CJanus::rs(const uint barshift,const uint symbolshift,const uint rs_period,uint lag=0)
      {
       if(lag>=rs_period)
          return(WRONG_VALUE);
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=(int)lag;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(EMPTY_VALUE);
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return(WRONG_VALUE);
         }
    
       return(rs_off_def(barshift,symbolshift,lag,median,i_offense,i_defense,index));
      }

    Der nachstehende Indikatorcode stellt die relative Stärke für ein Chartsymbol dar:

    //+------------------------------------------------------------------+
    //|                                             RelativeStrength.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //--- plot RelativeStrength
    #property indicator_label1  "RelativeStrength"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrRed
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 7;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         RelativeStrengthBuffer[];
    //---
    CJanus *janus;
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,RelativeStrengthBuffer,INDICATOR_DATA);
    //---    
       ArraySetAsSeries(RelativeStrengthBuffer,true);
    //---   
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       IndicatorSetString(INDICATOR_SHORTNAME,"RS("+_Symbol+")("+string(AppliedPeriod)+")");
    //---
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);     
    //---
       
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"RS("+_Symbol+")("+string(AppliedPeriod)+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }     
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],p
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
       int limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-int(AppliedPeriod+2);
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int i=limit;i>=1;i--)
        {
         RelativeStrengthBuffer[i]=janus.CalculateRelativeStrength(i,_Symbol,AppliedPeriod);
        } 
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Der Relative-Stärke-Indikator:

    Der Indikator RelativeStrength

    Anhand der Werte für die relative Stärke erhalten wir ein klareres Bild von Expansion und Kontraktion im Zeitverlauf. Dies wird im Folgenden durch den Indikatorplot der höchsten und niedrigsten Werte der relativen Stärke dargestellt. 

    Obere und untere RS

    Der Code für diesen Indikator ist unten dargestellt.

    //+------------------------------------------------------------------+
    //|                                    RelativeStrenghtBestWorst.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 2
    #property indicator_plots   2
    //--- plot Upper
    #property indicator_label1  "Upper"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrBlue
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    //--- plot Lower
    #property indicator_label2  "Lower"
    #property indicator_type2   DRAW_LINE
    #property indicator_color2  clrRed
    #property indicator_style2  STYLE_SOLID
    #property indicator_width2  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 25;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         UpperBuffer[];
    double         LowerBuffer[];
    double rsv[];
    
    CJanus *janus;
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,UpperBuffer,INDICATOR_DATA);
       SetIndexBuffer(1,LowerBuffer,INDICATOR_DATA);
    //---   
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
    //--- 
       ArraySetAsSeries(UpperBuffer,true);
       ArraySetAsSeries(LowerBuffer,true);
    //---    
       IndicatorSetString(INDICATOR_SHORTNAME,"RS_Upperlower("+_Symbol+")("+string(AppliedPeriod)+")");
    //---   
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);  
         
       ArrayResize(rsv,janus.GetSymbolsTotal());     
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"RS_Upperlower("+_Symbol+")("+string(AppliedPeriod)+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }       
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
       int limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-int(AppliedPeriod+2);
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int i=limit;i>=1;i--)
        {
          for(int k=0;k<ArraySize(rsv);k++)
            {
             string sym=janus.GetSymbolAt(k);
             rsv[k]= janus.CalculateRelativeStrength(i,sym,AppliedPeriod);
            }
            
          ArraySort(rsv);
          
         UpperBuffer[i]=rsv[ArraySize(rsv)-1];
         LowerBuffer[i]=rsv[0];    
        } 
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    


    Vorreiter und Nachzügler bei der relativen Stärke

    Wenn die relative Performance, die durch die Ergebnisse der besten und der schlechtesten Performer angegeben wird, gemittelt wird, erhält man die Vorreiter (leaders) bzw. die Nachzügler (laggards) der relativen Stärke. Diese Werte geben einen Hinweis auf die besten Zeitpunkte für den Handel in Abhängigkeit vom Stand der Rückmeldungen. Die relative Stärke (rs) der Vorreiter und Nachzügler wird berechnet, indem die Anzahl der besten und schlechtesten Performer angegeben wird, um einen Durchschnitt zu ermitteln. 

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength leaders and laggards |
    //+------------------------------------------------------------------+
    void CJanus::CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard)
      {
       leader=laggard=0;
       
       uint lag=rs_period;
    
       double sorted[];
       int iwork[],k,n,isub;
       k=isub=-1;
    
       int size=m_symbol_list.GetCollectionLength();
    
       ArrayResize(sorted,size);
       ArrayResize(iwork,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period,lag);
          iwork[i]=i;
         }
    
       MathQuickSortAscending(sorted,iwork,0,size-1);
    
       k=(int)(rs_percent_top*(size+1))-1;
       if(k<0)
          k=0;
       n=k+1;
    
       while(k>=0)
         {
          isub=iwork[k];
          for(uint i=0; i<lag; i++)
            {
             laggard+=market_return(barshift+i,isub);
            }
          isub=iwork[size-1-k];
          for(uint i=0; i<lag; i++)
            {
             leader+=market_return(barshift+i,isub);
            }
          --k;
         }
       leader/=n*lag;
       laggard/=n*lag;
    
       return;
      }

    Der Eingabeparameter rs_percent_top für die Methode CalculateRelativeStrengthLeadersLaggards() gibt den Anteil der Symbole an, die zur Berechnung der gemittelten relativen Stärke verwendet werden. Wenn Sie z. B. rs_percent_top auf 0,1 setzen, werden die obersten und untersten 10 % der Symbole für die Berechnung der Vorreiter und Nachzügler verwendet. 

    Unten sehen Sie einen Screenshot des Indikators der Vorreiter und Nachzügler:

    Vorreiter und Nachzügler


    Die Spanne

    Wenn die Märkte von einer positiven Rückkopplung erfasst werden, weichen die schwächsten und stärksten Wertpapiere je nach Trendrichtung voneinander ab. In einem Aufwärtstrend bevorzugen die Händler die stärkeren Wertpapiere und in einem Abwärtstrend werden die schwächeren Wertpapiere (leer-) verkauft.

    Wenn die Kurse in einer Bereich feststecken, ist der Unterschied zwischen dem schwächsten und dem stärksten Wertpapier relativ gering, es liegt eine Konvergenz vor. Um diese Entwicklungen zu erkennen, berechnen wir die durchschnittliche Differenz der relativen Stärke zwischen dem schwächsten und dem stärksten Wertpapier. Dies misst die Spanne (spread) der relativen Stärke.

    Die Spanne der relativen Stärke ist in der Methode CalculateRelativeStrengthSpread() implementiert.

    //+------------------------------------------------------------------+
    //|public method to calculate the relative strength spread.          |
    //+------------------------------------------------------------------+
    double CJanus::CalculateRelativeStrengthSpread(const uint barshift,const uint rs_period,const double rs_percent_top)
      {
       double sorted[],width,div;
       int k=0;
       int size=m_symbol_list.GetCollectionLength();
    
       width=div=0;
       ArrayResize(sorted,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period);
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(WRONG_VALUE);
         }
    
       k=(int)(rs_percent_top*(size+1))-1;
    
       if(k<0)
          k=0;
       double n=double(k+1);
    
       while(k>=0)
         {
          width+=sorted[size-1-k]-sorted[k];
          --k;
         }
    
       return(width/=n);
      }

    Der Indikator für die Spanne der relativen Stärke:

    RelativeStrengthSpread-Indikator

    Die Kombination der Indikatoren von Spanne und rs-Vorreiter und -Nachzügler kann zur Entwicklung konkreter Handelsregeln verwendet werden. Nach Anderson ergeben sich die besten Chancen, wenn man mit Symbolen handelt, die sich an der oberen oder unteren Grenze der relativen Stärke des untersuchten Index befinden.

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength leaders and laggards |
    //+------------------------------------------------------------------+
    void CJanus::CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard)
      {
       leader=laggard=0;
       
       uint lag=1;
    
       double sorted[];
       int iwork[],k,n,isub;
       k=isub=-1;
    
       int size=m_symbol_list.GetCollectionLength();
    
       ArrayResize(sorted,size);
       ArrayResize(iwork,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period,lag);
          iwork[i]=i;
         }
    
       MathQuickSortAscending(sorted,iwork,0,size-1);
    
       k=(int)(rs_percent_top*(size+1))-1;
       if(k<0)
          k=0;
       n=k+1;
    
       while(k>=0)
         {
          isub=iwork[k];
          for(uint i=0; i<lag; i++)
            {
             laggard+=market_return(barshift+i,isub);
            }
          isub=iwork[size-1-k];
          for(uint i=0; i<lag; i++)
            {
             leader+=market_return(barshift+i,isub);
            }
          --k;
         }
       leader/=n*lag;
       laggard/=n*lag;
    
       return;
      }


    Wenn der Trend der Spanne in Kombination mit den rs-Vorreiter ansteigt, ist dies ein Anzeichen für eine positive Rückkopplung in Verbindung mit den Nettokäufen der starken Hände. Ein günstiger Zeitpunkt, um Symbole zu kaufen, die die höchste relative Stärke aufweisen. Wenn stattdessen die rs-Nachzügler steigen, sollten Sie die schwächeren Symbole für eine Verkaufsposition ins Visier nehmen. In jeder dieser Situationen kann es ratsam sein, diese Regeln mit einer effektiven Eingabemethode zu kombinieren, die auf das/die gewählte(n) Symbol(e) angewendet wird. 

    Eine weitere Möglichkeit ist die Verwendung des Spread-Indikators als Risikofilter. Bei einer positiven Rückkopplung ist der Markt weniger riskant, während eine negative Rückkopplung ein Indikator für mögliche unruhige Bedingungen ist. Eine Zeit, in der man sich wahrscheinlich vom Markt fernhalten sollte. 


      Schlussfolgerung

       Obwohl die Analyse von Devisensymbolen vielleicht nicht die beste Anwendung des Janus-Faktors ist, besteht der Sinn dieses Artikels darin, die Leser mit der Methode vertraut zu machen. Weitere Informationen dazu finden Sie in dem unten erwähnten Buch von Anderson. 

      Die angehängte Zip-Datei enthält den gesamten Code für die im Artikel erwähnten Tools. In der nachstehenden Tabelle sind diese aufgeführt.

      Damit die Indikatoren funktionieren, sollten die Nutzer zunächst sicherstellen, dass historische Daten für alle Symbole, die den Index bilden, verfügbar sind.

      Dateiname Typ Beschreibung
      Mql5/include/Janus.mqh Include  Include-Datei mit Definition der Klassen CSymbolData, CSymbolCollection und CJanus
      Mql5/scripts/OffensiveDefensiveScatterPlot.mq5 Skript Skript, das ein animiertes Streudiagramm von Offensiv- und Defensivwerten zeichnet
      Mql5/indicators/IndexReturns.mq5 Indikator Code für Indikator zur Anzeige von Symbolen und Benchmark-Ergebnisse
      Mql5/indicators/RelativeStrengthBestWorst.mq5
      Indikator Indikator für die Darstellung der höchsten und niedrigsten Werte der relativen Stärke.
      Mql5/indicators/RelativeStrength.mq5
      Indikator Indikator der relativen Stärke für ein Symbol
      Mql5/indicators/RelativeStrengthSpread.mq5 Indikator Indikator, der die Spanne (spread) der relativen Stärke anzeigt
      Mql5/indicatorr/RssLeaderlaggards.mq5 Indikator Indikator, der die RS-Vorreiter und -Nachzügler aufzeigt



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

      Multibot in MetaTrader: Starten mehrerer Roboter von einem einzigen Chart aus Multibot in MetaTrader: Starten mehrerer Roboter von einem einzigen Chart aus
      In diesem Artikel werde ich eine einfache Vorlage für die Erstellung eines universellen MetaTrader-Roboters besprechen, der auf mehreren Charts verwendet werden kann, während er nur mit einem Chart läuft, ohne dass jede Instanz des Roboters auf jedem einzelnen Chart konfiguriert werden muss.
      Kategorientheorie in MQL5 (Teil 6): Monomorphe Pullbacks und epimorphe Pushouts Kategorientheorie in MQL5 (Teil 6): Monomorphe Pullbacks und epimorphe Pushouts
      Die Kategorientheorie ist ein vielfältiger und expandierender Zweig der Mathematik, der erst seit kurzem in der MQL5-Gemeinschaft Beachtung findet. In dieser Artikelserie sollen einige der Konzepte und Axiome erforscht und untersucht werden, mit dem übergeordneten Ziel, eine offene Bibliothek einzurichten, die Einblicke gewährt und hoffentlich auch die Nutzung dieses bemerkenswerten Bereichs für die Strategieentwicklung von Händlern fördert.
      Wie man MetaTrader 5 mit PostgreSQL verbindet Wie man MetaTrader 5 mit PostgreSQL verbindet
      Dieser Artikel beschreibt vier Methoden zur Verbindung von MQL5-Code mit einer Postgres-Datenbank und bietet eine Schritt-für-Schritt-Anleitung zum Einrichten einer Entwicklungsumgebung für eine dieser Methoden, eine REST-API, unter Verwendung des Windows Subsystem For Linux (WSL). Eine Demo-Anwendung für die API wird zusammen mit dem entsprechenden MQL5-Code zum Einfügen von Daten und Abfragen der entsprechenden Tabellen sowie einem Demo-Expert Advisor zum Abrufen dieser Daten bereitgestellt.
      Algorithmen zur Optimierung mit Populationen: Ein dem Elektro-Magnetismus ähnlicher Algorithmus (ЕМ) Algorithmen zur Optimierung mit Populationen: Ein dem Elektro-Magnetismus ähnlicher Algorithmus (ЕМ)
      Der Artikel beschreibt die Prinzipien, Methoden und Möglichkeiten der Anwendung des elektromagnetischen Algorithmus bei verschiedenen Optimierungsproblemen. Der EM-Algorithmus ist ein effizientes Optimierungswerkzeug, das mit großen Datenmengen und mehrdimensionalen Funktionen arbeiten kann.