
USD- und EUR-Index-Charts — Beispiel für einen MetaTrader 5-Dienst
Inhalt
- Einführung
- Formulierung der Aufgabe
- Implementierung der Datei mit den Funktionen zur Berechnung der Währungsindizes
- Testen des Dienstes
- Schlussfolgerung
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





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.