English Русский Español Deutsch Português
preview
USDとEURの指数チャート—MetaTrader 5サービスの例

USDとEURの指数チャート—MetaTrader 5サービスの例

MetaTrader 5 | 2 5月 2025, 10:07
58 0
Artyom Trishkin
Artyom Trishkin

内容


はじめに

米ドル指数(USDX)は、為替市場でもっとも広く利用されている指標の一つであり、為替レートの動向を予測するうえで有効な手がかりとなります。これは、相対的な米ドルの価値を示す重要な指標です。USDXは1973年3月に導入され、基準値は100ポイントに設定されました。つまり、現在の指数が90ポイントであれば、1973年と比べて米ドルが10%下落したことを意味し、110ポイントであれば10%上昇したことを意味します。

ジャマイカ国際通貨会議の決定により、指数に含まれる通貨は変動相場制へと移行しました。以降、USD指数は、世界の大手500行が提供する通貨取引データに基づいて、継続的に算出されています。1999年にユーロ(EUR)が導入され、いくつかの欧州通貨がこれに置き換えられたことで、米ドル指数の計算方法も変更されました。

USDXは、世界主要通貨で構成された通貨バスケットの加重幾何平均として計算されます。このバスケットには、米国の主要な貿易相手国6カ国の通貨が含まれており、各国の経済規模が異なるため、指数内の各通貨には個別の影響力(重み)が設定されています。

通貨
重み
ユーロ(EUR)
0.576 (57.6%)
日本円(JPY)
0.136 (13.6%)
英国ポンド(GBP)
0.119 (11.9%)
カナダドル(CAD)
0.091 (9.1%)
スウェーデンクローナ(SEK)
0.042 (4.2%)
スイスフラン(CHF)
0.036 (3.6%)

USDXの計算式

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

使用されるバスケット内の通貨に割り当てられた重みに応じて、為替レートはべき乗されます。係数50.14348112により、1973年3月時点の為替レートをこの式に代入すると、ドル指数の値は100.0になります。したがって、現在のUSDXは、1973年の相場と比較した通貨バスケットに対する米ドルの価値の変化を示しています。指数の値が100ポイント未満の場合、米ドルの価値が下落していることを示し、100ポイントを超える場合は、1973年と比べて米ドルの価値が上昇していることを意味します。

ユーロ通貨指数(EURX)は、世界の主要5通貨(米ドル、英ポンド、日本円、スイスフラン、スウェーデンクローナ)に対するユーロの為替レートの平均変動率を表します。

EURXは、2006年1月13日 にニューヨーク商品取引所(NYBOT)にて取引手段として導入され、ティッカーシンボルはECX、EURX、またはEです。

このユーロ指数は、国際金融市場において単一通貨ユーロの現在価値を示す標準的な指標として広く利用されており、取引活動をおこなう上でも重要なツールとなっています。

EURXは、ユーロ圏諸国の主要な対外貿易相手国の通貨を基にした5通貨バスケットを使用して計算されており、そのデータは、欧州中央銀行(ECB)が貿易加重ユーロ指数を算出する際にも使用されています。ユーロ圏諸国の国際貿易の内訳は、米国(31.55%)が最大で、次いで英国(30.56%)、日本(18.91%)、スイス(11.13%)、スウェーデン(7.85%)となっています。

ユーロ指数の現在値の計算は、USDXの計算と同様の基本原則に基づいており、加重幾何平均法によって求められます。

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

ここでのべき乗(指数)は、それぞれの通貨がバスケット内で占める重みを表しています。


タスクの形成

上記の数式に基づいて価格が計算される合成銘柄を作成する必要があります。この銘柄には、完全なチャートが必要であり、バスケットに含まれる各通貨ペアに新しいティックが到着するたびにチャートが更新され、さらにそのチャート上では任意のインジケーター、スクリプト、エキスパートアドバイザー(EA)を使用できる必要があります。

要するに、標準銘柄のチャートとほぼ同等の機能を備えた合成銘柄のチャートを作成する必要があります。そして、それを管理するサービスプログラムは、他のチャートやその上で動作しているプログラムに干渉することなく、独立したスレッドで処理を実行する必要があります。

サービス起動時には、対象となる合成銘柄がすでに存在するかを確認し、存在しない場合は新たに作成して気配値表示ウィンドウに追加します。続いて、合成銘柄の1分足およびティック履歴を作成し、最後にその銘柄のチャートを表示します。これらの処理が完了すると、サービスはバスケットに含まれる各シンボルの新しいティックを受信し、それらに基づいて合成銘柄の価格をリアルタイムで計算し、履歴にティックデータを追加していきます。また、端末を再起動した際、終了前にこのサービスが起動していた場合には、端末起動時に自動的に再起動されます。つまり、一度サービスを起動しておけば、以降端末起動時に常に自動実行されるようになります。

このようなカスタム銘柄を2つ(米ドル指数とユーロ指数)作成します。各銘柄に対して個別のサービスプログラムを用意し、それらは共通のインクルードファイルに基づいて構築されます。このインクルードファイルには、必要なツールを作成するためのすべての関数をまとめて実装します。サービスプログラム側では、バスケットの構成(銘柄リストと重み)、ベース比率、バスケットサイズなど、指数計算に必要な最小限の情報のみを定義します。その他の処理はすべてインクルードファイル内の関数呼び出しによって自動的に実行されるようにします。これにより、異なる通貨構成や重みを指定するだけで、共通の関数を用いて柔軟にカスタム指数を作成することが可能になります。


通貨指数計算関数ファイルの実装

\MQL5\Services\端末ディレクトリに、Indexes\という新しいフォルダを作成し、その中に新しいインクルードファイルCurrencyIndex.mqhを作成します。このファイルには、プロジェクトの動作に必要なすべての関数を格納します。す。

ファイルの冒頭には、マクロ置換の機能を正常に動作させるために必要な構造体や列挙型を定義します。

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

バスケットに含まれる各銘柄の構造体には、銘柄名とバスケット内での重みの2つのフィールドが含まれます。指数の計算用に銘柄バスケットを作成する際は、この構造体の配列を使用すると非常に便利です。初期化時に、銘柄の名前と重みをこの配列に記述し、その後、指数価格を計算するループ内でこの配列から銘柄とその重みを取得します。この方法を用いることで、任意の銘柄とその重みを配列に記述することが可能になり、指数の計算に使うあらゆる構成の銘柄バスケットを柔軟に作成できます。

履歴およびティックデータの構造体では、MqlRatesMqlTickのそれぞれに対応した構造体配列を使用します。これらの配列は、指数に使われる銘柄バスケット内の各銘柄に関するデータを格納します。また、それぞれの構造体にはデータ指数も含まれます。この指数は、指数計算に使用するデータを取得する対象バーの番号を示すために必要です。たとえば、あるバーにおいて指数を計算するには、指数計算に参加するすべての銘柄がそのバーに対応する価格データを持っている必要があります。ただし、各銘柄においてバーが常に存在するとは限りません。ある銘柄では、その分足の間にティックがなかった場合、バーが形成されずに空白となることがあります。こういった場合には、計算対象のデータを取得するために、どのバーからデータを取るかを示す指数を指定する必要があります。該当するバーにデータが存在しない場合、その指数を1つ進めて、1つ前のバーからデータを取得するようにします。また、指数計算に用いる銘柄バスケットに含まれる銘柄の数はあらかじめ不明なため、プログラム内であらかじめ必要な指数数を宣言することができません。そのため、このような構造体の配列として保持して使用する方が便利です。

価格タイプの列挙型は、バーの価格を計算するために使用する価格の種類(終値や始値など)を示す定数を定義するためのものです。

サービスを開始するときは、まずカスタム銘柄を作成し、過去1か月分のM1(1分足)バーの履歴データを生成して、ティック履歴を作成する必要があります。これはサービスの初期化になります。次の関数を実装してみましょう。

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

カスタム銘柄の存在の確認と作成は、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);
  }

ここでは、指定された名前のカスタム銘柄が存在するかどうかを確認します。存在しない場合は、それを新規作成します。次に、その銘柄のチャートが端末内で開かれているかを確認し、見つからなければチャートを開きます。

カスタム銘柄の作成とチャートの表示が完了した後、1か月分のM1履歴を作成する必要があります。これは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);
  }

この関数では、まず指数計算に使用されるバスケット内の各銘柄について、データをコピーし、そのコピー処理の開始時刻を統一します。すべての銘柄で開始日時が一致するように、他のバスケット銘柄と同じ時刻に対応するデータ指数を各銘柄に対して設定します。開始日時に該当するバーが存在しない銘柄があった場合は、データが存在する最も近い過去のバーの指数が設定されます。

その後、ループ内で合成銘柄の時系列バーを1本ずつ計算していきます。このとき、各バーについて、データが存在する場合はそのバーから、存在しない場合は直前のバーからデータを取得するように指数が調整されます。計算された各バーは合成銘柄の時系列配列に追加されます。すべてのバーの計算が完了すると、その時系列データはカスタム銘柄の価格履歴に書き込まれます。

合成銘柄の1本のバーに対応する履歴データは、CalculateRate関数によって計算されます。

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

合成銘柄の各バー価格(始値、高値、安値、終値)は、それぞれ合成銘柄の計算式に基づいて算出されます。

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

バスケットの各銘柄の価格はGetRateValue関数内で取得されます。

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

CalculateRate関数は、合成銘柄の計算された価格をチェックした結果を返します。

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

合成銘柄バーのすべての価格が計算されるため、それらの計算結果が有効かどうかを確認し、必要に応じてエラーを修正する必要があります。

これらの処理はすべて、CheckRate関数内で実行され、その結果が返されます。

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

計算された価格のいずれかがプラスまたはマイナスの無限大、または「非数」(NaN)、あるいはゼロ以下である場合、関数はfalseを返します。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/15684

添付されたファイル |
CurrencyIndex.mqh (51.79 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.66 KB)
初級から中級へ:BREAK文とCONTINUE文 初級から中級へ:BREAK文とCONTINUE文
この記事では、ループ内でのRETURN、BREAK、CONTINUE文の使い方について解説します。ループの実行フローにおいて、これらの各文がどのような役割を果たすかを理解することは、より複雑なアプリケーションを扱う上で非常に重要です。ここで提示されるコンテンツは、教育目的のみを目的としています。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを閲覧することは避けてください。
タブーサーチ(TS) タブーサーチ(TS)
この記事では、最初期かつ最も広く知られているメタヒューリスティック手法の一つであるタブーサーチアルゴリズムについて解説します。初期解の選択や近傍解の探索から始め、特にタブーリストの活用に焦点を当てながら、アルゴリズムの動作を詳しく見ていきます。本記事では、タブーサーチの主要な特徴と要素について取り上げます。
取引におけるニューラルネットワーク:階層型ベクトルTransformer(最終回) 取引におけるニューラルネットワーク:階層型ベクトルTransformer(最終回)
階層的ベクトルTransformer法の研究を引き続き進めていきます。本記事では、モデルの構築を完了し、実際の履歴データを用いて訓練およびテストをおこないます。
初心者からプロまでMQL5をマスターする(第5回):基本的な制御フロー演算子 初心者からプロまでMQL5をマスターする(第5回):基本的な制御フロー演算子
この記事では、プログラムの実行フローを変更するために使用される主要な演算子(条件文、ループ、switch文)について説明します。これらの演算子を利用することで、作成する関数がより「インテリジェント」に動作できるようになります。