English Русский 中文 Español 日本語 Português
preview
USD- und EUR-Index-Charts — Beispiel für einen MetaTrader 5-Dienst

USD- und EUR-Index-Charts — Beispiel für einen MetaTrader 5-Dienst

MetaTrader 5Beispiele | 6 Mai 2025, 10:59
58 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Einführung

Der U.S. Dollar Index ist der beliebteste Index des Währungsmarktes. Er ermöglicht es uns, die Entwicklung der Wechselkurse vorherzusagen. Dies ist ein wichtiger Indikator für den relativen Wert des USD. Der US Dollar Index (USDX) wurde im März 1973 eingeführt. Sein Basiswert wurde auf 100 Punkte festgelegt. Mit anderen Worten: Ein Indexstand von 90 Punkten würde heute einen Rückgang des USD um 10 % gegenüber 1973 bedeuten, während ein Indexstand von 110 Punkten einen Anstieg um 10 % bedeuten würde.

Im Anschluss an die Ergebnisse der internationalen Jamaika-Konferenz traten die variablen Kurse für die im Index enthaltenen Währungen in Kraft. Seitdem wird der USD-Index kontinuierlich auf der Grundlage der von den 500 größten Banken der Welt bereitgestellten Devisenhandelsdaten berechnet. Die Berechnungsmethode für den US-Dollar-Index wurde 1999 nach der Einführung des EUR geändert, der die nationalen Währungen einer Reihe europäischer Länder ersetzte.

Der USDX wird als gewichtetes geometrisches Mittel eines Währungskorbs berechnet, der die wichtigsten Weltwährungen umfasst. Jede Währung in diesem Korb gehört zu einer Gruppe von sechs wichtigen Handelspartnern der Vereinigten Staaten, die in ihrer Wirtschaftskraft nicht gleich stark sind, sodass jeder Währung im Index ein bestimmter Anteil an Einfluss (Gewicht) zugewiesen wird:

Währung
Verhältnis
EUR
0,576 (57,6%)
Japanischer Yen (JPY)
0,136 (13,6%)
Britisches Pfund (GBP)
0,119 (11,9%)
Kanadischer Dollar (CAD)
0,091 (9,1%)
Schwedische Krone (SEK)
0,042 (4,2%)
Schweizer Franken (CHF)
0,036 (3,6%)

Gleichung zur Berechnung des US-Dollar-Index:

USDX = 50.14348112 * EURUSD^(-0.576) * USDJPY^(0.136) * GBPUSD^(-0.119) * USDCAD^(0.091) * USDSEK^(0.042) * USDCHF^(0.036) 

Die Werte, mit dem die Kurse potenziert werden, entsprechen dem Gewicht der Währungen im verwendeten Korb. Das Verhältnis 50,14348112 bringt den Dollar-Index auf einen Wert von 100,0, wenn die Wechselkurse vom März 1973 in die Gleichung eingesetzt werden. Der aktuelle USDX spiegelt also die Veränderung des Wertes des US-Dollars gegenüber einem Währungskorb im Vergleich zu den Notierungen von 1973 wider. Ein Indexwert von weniger als 100 Punkten bedeutet eine Abwertung des USD, während ein Wert von mehr als 100 Punkten eine Aufwertung des USD im Vergleich zu 1973 anzeigt.

Der Euro-Währungsindex ist die durchschnittliche Veränderungsrate der Wechselkurse von fünf Weltwährungen (dem US-Dollar, dem britischen Pfund, dem japanischen Yen, dem Schweizer Franken und der schwedischen Krone) gegenüber dem Euro.

Als Handelsinstrument wurde der Euro-Index (EURX) am 13. Januar 2006 an der New York Board of Trade (NYBOT) unter den Tickersymbolen ECX, EURX oder E eingeführt.

Der EUR-Index ist für die Teilnehmer an den internationalen Finanzmärkten zu einem Standard für den aktuellen Wert der europäischen Einheitswährung und zu einem Instrument für die Durchführung von Handelsgeschäften geworden.

Die Berechnung des EUR-Index auf der Grundlage eines Korbs von fünf Währungen stimmt mit den Daten überein, die von der Europäischen Zentralbank bei der Berechnung des handelsgewichteten Euro-Index auf der Grundlage der Währungen der Länder verwendet werden, die den größten Außenhandelsumsatz der Länder der Eurozone machen. Der größte Anteil des internationalen Handels der Länder der Eurozone entfällt auf die Vereinigten Staaten (31,55 %), gefolgt vom Vereinigten Königreich - 30,56 %, Japan - 18,91 %, der Schweiz - 11,13 % und Schweden - 7,85 %.

Die Grundprinzipien für die Berechnung des aktuellen Wertes des UER-Index sind ähnlich wie bei der Berechnung des USDX. Der EUR-Index wird nach der Methode des gewichteten, geometrischen Mittels berechnet:

EURX = 34.38805726 * EURUSD^(0.3155) * EURGBP^(0.3056) * EURJPY^(0.1891) * EURCHF^(0.1113) * EURSEK^(0.0785)

wobei die Potenz dem Gewicht der Währungen im verwendeten Korb entspricht.


Formulierung der Aufgabe

Wir müssen ein synthetisches Instrument erstellen, dessen Preis gemäß den oben genannten Gleichungen berechnet wird. Wir benötigen einen vollwertigen Chart des Instruments, der mit jedem neuen Tick der im Instrumentenkorb verwendeten Symbole aktualisiert wird, und auf diesem Chart können wir alle Indikatoren, Skripte und EAs ausführen.

Im Allgemeinen müssen wir einen Chart eines synthetischen Instruments erstellen, der sich praktisch nicht von den Charts der Standardinstrumente unterscheidet. Wir brauchen ein Dienstprogramm, das alles in seinem eigenen Ablauf erledigt, unabhängig von anderen geöffneten Charts und Programmen, die auf ihnen laufen.

Beim Start des Dienstes prüfen wir, ob das erforderliche synthetische Instrument vorhanden ist, erstellen es gegebenenfalls und platzieren es im Fenster Market Watch. Die Minuten- und Tick-Historie des synthetischen Instruments wird anschließend erstellt, gefolgt von dem Chart des erstellten Instruments. Nach diesen Manipulationen erhält der Dienst neue Ticks für jedes der Symbole, auf deren Grundlage der Preis des Instruments berechnet wird, und die neuen Ticks werden der Historie des erstellten nutzerdefinierten Symbols hinzugefügt. Nach dem Neustart des Terminals wird das Dienstprogramm automatisch gestartet, wenn es beim Schließen des Terminals gestartet wurde. Wenn der erforderliche Dienst einmal gestartet ist, wird er immer automatisch neu gestartet, wenn das Terminal gestartet wird.

Wir werden zwei solcher nutzerdefinierten Symbole erstellen: den US-Dollar-Index und den EUR-Index. Für jedes dieser Symbole wird ein eigenes Dienstprogramm erstellt. Sie werden auf einer einzigen Include-Datei basieren, in der wir alle Funktionen zur Erstellung des gewünschten Tools unterbringen. Das Serviceprogramm gibt nur die Korbgröße, das Basisverhältnis und die Symbolstruktur mit ihren Gewichten an, die für die Berechnung des Instruments erforderlich sind. Alles andere geschieht innerhalb der eingeschlossenen Datei, wenn die darin festgelegten Funktionen aufgerufen werden. Dies ermöglicht es uns, unsere eigenen Indizes auf der Grundlage der erstellten Funktionen mit einem anderen Satz von Währungen und deren Gewichtung zu erstellen, indem wir einfach eine Liste von Symbolen und die Gewichtung jedes einzelnen von ihnen angeben.


Implementierung der Datei mit den Funktionen zur Berechnung der Währungsindizes

Im Terminalverzeichnis \MQL5\Services\ erstellen wir den neuen Ordner Indexes\, der eine neue Include-Datei CurrencyIndex.mqh enthält. Dort werden alle für den Projektbetrieb erforderlichen Funktionen gespeichert.

Ganz am Anfang der Datei geben wir die Strukturen und Enumerationen ein, die notwendig sind, damit die Makrosubstitution funktioniert:

//+------------------------------------------------------------------+
//|                                                CurrencyIndex.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"

#define SECONDS_IN_DAY    (24*60*60)   // number of seconds in a day
#define SECONDS_IN_MINUTE 60           // number of seconds in a minute
#define MSECS_IN_MINIUTE  (60*1000)    // number of milliseconds in a minute

//--- basket symbol structure
struct SymbolWeight
  {
    pair            symbol;           // symbol
   double            weight;           // weight
  };
  
//--- historical data structure
struct str_rates
  {
   int               index;            // data index
   MqlRates          rates[];          // array of historical data
  };
  
//--- tick data structure
struct str_ticks
  {
   int               index;            // data index
   MqlTick           ticks[];          // array of ticks
  };
  
//--- enumeration of price types
enum ENUM_RATES_VALUES
  {
   VALUE_OPEN,                         // Open price
   VALUE_HIGH,                         // High price
   VALUE_LOW,                          // Low price
   VALUE_CLOSE                         // Close price
  };

int ExtDigits=5;                       // symbol price measurement accuracy

Die Struktur für die Symbole des Korbs enthält zwei Felder: den Namen des Symbols und sein Gewicht im Währungskorb. Bei der Zusammenstellung eines Korbes von Instrumenten für die Berechnung des Indexes ist es zweckmäßig, ein Array solcher Strukturen zu verwenden: Gleich bei der Initialisierung werden wir die Namen der Symbole und ihre Gewichte in dieses Array schreiben, und dann aus diesem Array das Symbol und sein Gewicht in einer Schleife für die Berechnung der Indexpreise erhalten. In diesem Fall können beliebige Symbole mit ihren Gewichten in das Array geschrieben werden, was Flexibilität bei der Erstellung beliebiger Körbe von Instrumenten zur Berechnung von Indizes bietet.

Die historischen und Tick-Datenstrukturen verwenden Arrays der entsprechenden Strukturen: MqlRates und MqlTick — sie enthalten die Daten zu jedem der Symbole im Währungskorb. Außerdem gibt es in jeder dieser Strukturen einen Datenindex. Der Index wird benötigt, um die Nummer des vorhandenen Balkens anzugeben, aus dem die Daten zur Berechnung des Index entnommen werden. Um beispielsweise einen Index auf einem Balken zu berechnen, ist es notwendig, dass jedes der Symbole des Währungskorbs, das an der Indexberechnung teilnimmt, Daten auf diesem Minutenbalken hat. Sie müssen sich nicht unbedingt für jedes Symbol befinden - irgendwo gibt es Lücken in den Balken (wenn es in dieser Minute keine Ticks auf einem der Symbole gab). In diesem Fall muss der Index des Balkens angegeben werden, aus dem die Daten für die Berechnung entnommen werden (wenn es keine Daten auf dem Symbol gibt, erhöht sich der Index), um die Daten aus dem vorherigen Balken zu übernehmen. Und da wir die Anzahl der Symbole im Korb der Instrumente für die Berechnung des Index nicht im Voraus kennen, können wir die erforderliche Anzahl der Indizes für jedes Instrument im Programm nicht im Voraus angeben. Daher ist es praktisch, sie in einer solchen Struktur zu lagern und zu verwenden.

Die Enumeration des Preistyps spezifiziert einfach Konstanten, die den Preis angeben, der für die Berechnung der Balkenpreise benötigt wird.

Wenn Sie den Dienst starten, müssen Sie zunächst ein nutzerdefiniertes Symbol erstellen, historische М1-Balken in einem Monat bilden und eine Tick-Historie erstellen. Dies wird die Initialisierung des Dienstes sein. Lassen Sie uns die folgende Funktion implementieren:

//+------------------------------------------------------------------+
//| Initializing the service                                         |
//+------------------------------------------------------------------+
bool InitService(const string custom_symbol,const string custom_group)
  {
   MqlRates rates[100];
   MqlTick  ticks[100];
   
//--- initialize the custom symbol
   if(!CustomSymbolInitialize(custom_symbol,custom_group))
      return(false);
   ExtDigits=(int)SymbolInfoInteger(custom_symbol,SYMBOL_DIGITS);
   
//--- we make active all symbols of the instrument basket that participate in the index calculation
   for(int i=0; i<BASKET_SIZE; i++)
     {
      //--- select a symbol in the Market Watch window
      if(!SymbolSelect(ExtWeights[i].symbol,true))
        {
         PrintFormat("cannot select symbol %s",ExtWeights[i].symbol);
         return(false);
        }
      //--- request historical data of bars and ticks for the selected symbol
      CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,100,rates);
      CopyTicks(ExtWeights[i].symbol,ticks,COPY_TICKS_ALL,0,100);
     }
     
//--- build M1 bars for 1 month
   if(!PrepareRates(custom_symbol))
      return(false);
      
//--- get the last ticks after building M1 bars
   PrepareLastTicks(custom_symbol);
   
//--- service initialized
   Print(custom_symbol," datafeed started");
   return(true);
  }

Die Prüfung auf Existenz und die Erstellung eines nutzerdefinierten Symbols erfolgt mit der Funktion CustomSymbolInitialize():

//+------------------------------------------------------------------+
//| Initialize a custom symbol                                       |
//+------------------------------------------------------------------+
bool CustomSymbolInitialize(string symbol,string group)
  {
   bool is_custom=false;
//--- if a symbol is selected in the Market Watch window, we get a flag that this is a custom symbol
   bool res=SymbolSelect(symbol,true);
   if(res)
      is_custom=(bool)SymbolInfoInteger(symbol,SYMBOL_CUSTOM);

//--- if the selected symbol is not custom, create it
   if(!res)
     {
      if(!CustomSymbolCreate(symbol,group,"EURUSD"))
        {
         Print("cannot create custom symbol ",symbol);
         return(false);
        }
      //--- the symbol was successfully created - set the custom symbol flag
      is_custom=true;
      
      //--- place the created symbol in the Market Watch window
      if(!SymbolSelect(symbol,true))
        {
         Print("cannot select custom symbol ",symbol);
         return(false);
        }
     }
     
//--- open the chart of the created custom symbol
   if(is_custom)
     {
      //--- get the ID of the first window of open charts
      long chart_id=ChartFirst();
      bool found=false;
      //--- in the loop through the list of open charts, find the chart of the created custom symbol
      while(chart_id>=0)
        {
         //--- if the chart is open, report this to the journal, set the flag of the chart found and exit the search loop
         if(ChartSymbol(chart_id)==symbol)
           {
            found=true;
            Print(symbol," chart found");
            break;
           }
         //--- based on the currently selected chart, get the ID of the next one for the next iteration of the search in the loop
         chart_id=ChartNext(chart_id);
        }
      
      //--- if the symbol chart is not found among the open charts
      if(!found)
        {
         //--- report about opening of M1 chart of a custom symbol,
         //--- get the chart ID and move on to it
         Print("open chart ",symbol,",M1");
         chart_id=ChartOpen(symbol,PERIOD_M1);
         ChartSetInteger(chart_id,CHART_BRING_TO_TOP,true);
        }
     }
//--- user symbol initialized
   return(is_custom);
  }

Hier wird geprüft, ob es ein nutzerdefiniertes Symbol mit einem bestimmten Namen gibt. Wenn nicht, wird es erstellt. Als Nächstes suchen wir nach einem offenen Chart dieses Symbols, und wenn der Chart nicht unter den offenen Charts im Terminal zu finden ist, öffnen wir den Chart.

Nach der Erstellung eines nutzerdefinierten Symbols und dem Öffnen seines Charts müssen wir eine Historie der M1-Periode für einen Monat erstellen. Dies geschieht mit Hilfe der Funktion PrepareRates():

//+------------------------------------------------------------------+
//| Preparing historical data                                        |
//+------------------------------------------------------------------+
bool PrepareRates(const string custom_symbol)
  {
   str_rates symbols_rates[BASKET_SIZE];
   int       i,reserve=0;
   MqlRates  usdx_rates[];                                              // array timeseries of a synthetic instrument
   MqlRates  rate;                                                      // synthetic instrument single bar data
   datetime  stop=(TimeCurrent()/SECONDS_IN_MINUTE)*SECONDS_IN_MINUTE;  // M1 bar time of the end date
   datetime  start=stop-31*SECONDS_IN_DAY;                              // initial date M1 bar time
   datetime  start_date=0;
   
//--- copy M1 historical data for a month for all symbols of the instrument basket
   start/=SECONDS_IN_DAY;
   start*=SECONDS_IN_DAY;                                               // initial date D1 bar time
   for(i=0; i<BASKET_SIZE; i++)
     {
      if(CopyRates(ExtWeights[i].symbol,PERIOD_M1,start,stop,symbols_rates[i].rates)<=0)
        {
         PrintFormat("cannot copy rates for %s,M1 from %s to %s [%d]",ExtWeights[i].symbol,TimeToString(start),TimeToString(stop),GetLastError());
         return(false);
        }
      PrintFormat("%u %s,M1 rates from %s",ArraySize(symbols_rates[i].rates),ExtWeights[i].symbol,TimeToString(symbols_rates[i].rates[0].time));
      symbols_rates[i].index=0;
      //--- find and set the minimum non-zero start date from the symbol basket
      if(start_date<symbols_rates[i].rates[0].time)
         start_date=symbols_rates[i].rates[0].time;
     }
   Print("start date set to ",start_date);
   
//--- reserve of historical data array to avoid memory reallocation when changing array size
   reserve=int(stop-start)/60;
   
//--- set the start of all historical data of the symbol basket to a single date (start_date)
   for(i=0; i<BASKET_SIZE; i++)
     {
      int j=0;
      //--- as long as j is less than the amount of data in the 'rates' array and
      //--- time at j index in the array is less than start_date time - increase the index
      while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<start_date)
         j++;
      //--- if the index was increased and it is within the 'rates' array, decrease it by 1 to compensate for the last increment
      if(j>0 && j<ArraySize(symbols_rates[i].rates))
         j--;
      //--- write the received index into the structure
      symbols_rates[i].index=j;
     }
      
//--- USD index timeseries
   int    array_size=0;
   
//--- first bar of M1 time series
   rate.time=start_date;
   rate.real_volume=0;
   rate.spread=0;

//--- as long as the bar time is less than the end date time of the M1 timeseries
   while(!IsStopped() && rate.time<stop)
     {
      //--- if the historical data of the instrument bar is calculated
      if(CalculateRate(rate,symbols_rates))
        {
         //--- increase the timeseries array by 1 and add the calculated data to it
         ArrayResize(usdx_rates,array_size+1,reserve);
         usdx_rates[array_size]=rate;
         array_size++;
         //--- reset the size of the array size backup value since it is only applied during the first resize
         reserve=0;
        }
      
      //--- next bar of the M1 timeseries
      rate.time+=PeriodSeconds(PERIOD_M1);
      start_date=rate.time;
      
      //--- in the loop through the list of basket instruments
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- get the current data index
         int j=symbols_rates[i].index;
         //--- while j is within the timeseries data and if the time of the bar at index j is less than the time set for this bar in rate.time, increase j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<rate.time)
            j++;
         //--- if j is within the timeseries data and the time in start_date is less than the time of the timeseries data by j index
         //--- and the time in the timeseries at index j is less than or equal to the time in rate.time - write the time from the timeseries at index j to start_date
         if(j<ArraySize(symbols_rates[i].rates) && start_date<symbols_rates[i].rates[j].time && symbols_rates[i].rates[j].time<=rate.time)
            start_date=symbols_rates[i].rates[j].time;
        }
      
      //--- in the loop through the list of basket instruments
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- get the current data index
         int j=symbols_rates[i].index;
         //--- while j is within the timeseries data and if the time of the bar at index j is less than the time set for this bar in start_date, increase j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<=start_date)
            symbols_rates[i].index=j++;
        }
      //--- in rate.time, set the time from start_date for the next bar
      rate.time=start_date;
     }
     
//--- add the created timeseries to the database
   if(array_size>0)
     {
      if(!IsStopped())
        {
         int cnt=CustomRatesReplace(custom_symbol,usdx_rates[0].time,usdx_rates[ArraySize(usdx_rates)-1].time+1,usdx_rates);
         Print(cnt," ",custom_symbol,",M1 rates from ",usdx_rates[0].time," to ",usdx_rates[ArraySize(usdx_rates)-1].time," added");
        }
     }
//--- successful
   return(true);
  }

Die Funktion kopiert zunächst Daten für jedes der Symbole im Währungskorb und stellt die Gesamtstartzeit für das Kopieren von Daten ein. Für jedes der Instrumente wird dann ein Datenindex festgelegt, dessen Zeitpunkt mit dem Zeitpunkt der anderen Korbsymbole übereinstimmt, sodass das Anfangsdatum für alle Symbole im Korb gleich ist. Gibt es für ein Symbol bis zum Startdatum keinen Balken, wird der Index des nächstgelegenen vorherigen Balkens, auf dem die Daten vorhanden sind, für es gesetzt.

Anschließend wird im Zyklus jeder Balken der Zeitreihe des synthetischen Instruments Balken für Balken berechnet, wobei der Index jedes nachfolgenden Balkens so angepasst wird, dass die Daten entweder aus dem aktuellen Balken stammen, wenn dieser Daten enthält, oder aus dem vorherigen Balken, wenn der aktuelle Balken keine Daten enthält. Die berechneten Balken werden dem Zeitreihen-Array des synthetischen Instruments hinzugefügt. Nachdem alle Balken der Zeitreihe des Instruments berechnet wurden, wird die berechnete Zeitreihe zum Preisverlauf des nutzerdefinierten Symbols hinzugefügt.

Die historischen Daten eines Balkens eines synthetischen Instruments werden mit der Funktion CalculateRate() berechnet:

//+------------------------------------------------------------------+
//| Calculation of prices and volumes of synthetic instruments       |
//+------------------------------------------------------------------+
bool CalculateRate(MqlRates& rate,str_rates& symbols_rates[])
  {
   double values[BASKET_SIZE]={0};
   long   tick_volume=0;
   int    i;
//--- get Open prices of all symbols of the instrument basket into the values[] array
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_OPEN);

//--- if the tick volume is zero, then there is no data for this minute - return 'false'
   if(tick_volume==0)
      return(false);
      
//--- write down the total volume of all timeseries
   rate.tick_volume=tick_volume;
   
//--- calculate the Open price based on the prices and weights of all instruments in the basket
   rate.open=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.open*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the High price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_HIGH);
   rate.high=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.high*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the Low price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_LOW);
   rate.low=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.low*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the Close price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_CLOSE);
   rate.close=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.close*=MathPow(values[i],ExtWeights[i].weight);
      
//--- return the result of checking prices for validity
   return(CheckRate(rate));
  }

Für jeden der Preise des Balkens (Open, High, Low und Close) des synthetischen Instruments werden die Preise anhand der Gleichung des synthetischen Instruments berechnet:

Open USDX = 50.14348112 * Open EURUSD^(-0.576) * Open USDJPY^(0.136) * Open GBPUSD^(-0.119) * Open USDCAD^(0.091) * Open USDSEK^(0.042) * Open USDCHF^(0.036);
High USDX = 50.14348112 * High EURUSD^(-0.576) * High USDJPY^(0.136) * High GBPUSD^(-0.119) * High USDCAD^(0.091) * High USDSEK^(0.042) * High USDCHF^(0.036);
Low  USDX = 50.14348112 * Low  EURUSD^(-0.576) * Low  USDJPY^(0.136) * Low  GBPUSD^(-0.119) * Low  USDCAD^(0.091) * Low  USDSEK^(0.042) * Low  USDCHF^(0.036);
CloseUSDX = 50.14348112 * CloseEURUSD^(-0.576) * CloseUSDJPY^(0.136) * CloseGBPUSD^(-0.119) * CloseUSDCAD^(0.091) * CloseUSDSEK^(0.042) * CloseUSDCHF^(0.036);

Die Preise der einzelnen Symbole des Korbes werden mit der Funktion GetRateValue() ermittelt:

//+------------------------------------------------------------------+
//| Return the specified bar price                                   |
//+------------------------------------------------------------------+
double GetRateValue(long &tick_volume,str_rates &symbol_rates,datetime time,ENUM_RATES_VALUES num_value)
  {
   double value=0;                  // obtained value
   int    index=symbol_rates.index; // data index
   
//--- if the index is within the timeseries
   if(index<ArraySize(symbol_rates.rates))
     {
      //--- depending on the type of requested data, set the corresponding value to the 'value' variable
      switch(num_value)
        {
         //--- Open price
         case VALUE_OPEN:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                 {
                  value=symbol_rates.rates[index].open;
                  //--- when requesting the Open price, add the tick volume to the tick_volume variable passed by reference,
                  //--- to get the total volume of all symbols in the instrument basket
                  tick_volume+=symbol_rates.rates[index].tick_volume;
                 }
              }
            break;
         //--- High price
         case VALUE_HIGH:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                  value=symbol_rates.rates[index].high;
              }
            break;
         //--- Low price
         case VALUE_LOW:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                  value=symbol_rates.rates[index].low;
              }
            break;
         //--- Close price
         case VALUE_CLOSE:
            if(symbol_rates.rates[index].time<=time)
               value=symbol_rates.rates[index].close;
            break;
        }
     }
     
//--- return the received value
   return(value);
  }

Die Funktion CalculateRate() gibt das Ergebnis der Überprüfung der berechneten Preise des synthetischen Instruments zurück:

//--- return the result of checking prices for validity
   return(CheckRate(rate));

Da alle Preise des Balken des synthetischen Instruments berechnet werden, ist es notwendig, die Gültigkeit ihrer Berechnung zu überprüfen und etwaige Fehler zu korrigieren.

All dies wird in der Funktion CheckRate() durchgeführt, deren Ergebnis zurückgegeben wird:

//+------------------------------------------------------------------+
//| Check prices for validity and return the check result            |
//+------------------------------------------------------------------+
bool CheckRate(MqlRates &rate)
  {
//--- if prices are not valid real numbers, or are less than or equal to zero - return 'false'
   if(!MathIsValidNumber(rate.open) || !MathIsValidNumber(rate.high) || !MathIsValidNumber(rate.low) || !MathIsValidNumber(rate.close))
      return(false);
   if(rate.open<=0.0 || rate.high<=0.0 || rate.low<=0.0 || rate.close<=0.0)
      return(false);
      
//--- normalize prices to the required number of digits
   rate.open=NormalizeDouble(rate.open,ExtDigits);
   rate.high=NormalizeDouble(rate.high,ExtDigits);
   rate.low=NormalizeDouble(rate.low,ExtDigits);
   rate.close=NormalizeDouble(rate.close,ExtDigits);
   
//--- adjust prices if necessary
   if(rate.high<rate.open)
      rate.high=rate.open;
   if(rate.low>rate.open)
      rate.low=rate.open;
   if(rate.high<rate.close)
      rate.high=rate.close;
   if(rate.low>rate.close)
      rate.low=rate.close;
      
//--- all is fine
   return(true);
  }

Wenn einer der berechneten Preise plus oder minus unendlich ist, oder "keine Zahl" (NaN), oder kleiner oder gleich Null, gibt die Funktion false zurück.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/15684

Beigefügte Dateien |
CurrencyIndex.mqh (51.79 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.66 KB)
Entwicklung eines Expertenberaters für mehrere Währungen (Teil 17): Weitere Vorbereitung auf den realen Handel Entwicklung eines Expertenberaters für mehrere Währungen (Teil 17): Weitere Vorbereitung auf den realen Handel
Derzeit verwendet unser EA die Datenbank, um Initialisierungs-Strings für einzelne Instanzen von Handelsstrategien zu erhalten. Die Datenbank ist jedoch recht groß und enthält viele Informationen, die für den eigentlichen EA-Betrieb nicht benötigt werden. Versuchen wir, die Funktionalität des EA ohne eine obligatorische Verbindung zur Datenbank zu gewährleisten.
Von der Grundstufe bis zur Mittelstufe: Die Anweisungen BREAK und CONTINUE Von der Grundstufe bis zur Mittelstufe: Die Anweisungen BREAK und CONTINUE
In diesem Artikel sehen wir uns an, wie man die Anweisungen RETURN, BREAK und CONTINUE in einer Schleife verwendet. Für die Arbeit mit komplexeren Anwendungen ist es sehr wichtig zu verstehen, was die einzelnen Anweisungen im Ablauf der Schleifenausführung bewirken. Der hier dargestellte Inhalt ist ausschließlich für Bildungszwecke bestimmt. Die Anwendung sollte unter keinen Umständen zu einem anderen Zweck als zum Erlernen und Beherrschen der vorgestellten Konzepte verwendet werden.
Neuronale Netze im Handel: Punktwolkenanalyse (PointNet) Neuronale Netze im Handel: Punktwolkenanalyse (PointNet)
Die direkte Analyse von Punktwolken vermeidet unnötiges Datenwachstum und verbessert die Leistung von Modellen bei Klassifizierungs- und Segmentierungsaufgaben. Solche Ansätze zeigen eine hohe Leistungsfähigkeit und Robustheit gegenüber Störungen in den Originaldaten.
MQL5 beherrschen, vom Anfänger zum Profi (Teil V): Grundlegende Operatoren für die Ablaufkontrolle MQL5 beherrschen, vom Anfänger zum Profi (Teil V): Grundlegende Operatoren für die Ablaufkontrolle
Dieser Artikel befasst sich mit den wichtigsten Operatoren, die zur Änderung des Programmablaufs verwendet werden: bedingte Anweisungen, Schleifen und Switch-Anweisungen. Die Verwendung dieser Operatoren ermöglicht es den von uns erstellten Funktionen, sich „intelligenter“ zu verhalten.