Русский Español Português
preview
USD and EUR index charts — example of a MetaTrader 5 service

USD and EUR index charts — example of a MetaTrader 5 service

MetaTrader 5Examples | 17 March 2025, 15:00
4 556 40
Artyom Trishkin
Artyom Trishkin

Contents


Introduction

U.S. Dollar Index is the most popular index of the currency market. It allows us to predict the movement of exchange rates. This is an important indicator of the relative USD value. The US Dollar Index (USDX) was introduced in March 1973. Its base value was set at 100 points. In other words, the index equal to 90 points today would mean a 10% fall of USD relative to the 1973 figure, while the index equal to 110 points would mean a 10% rise.

Following the results of the Jamaica International Conference, floating rates for the currencies included in the index came into effect. Since then, the USD index has been calculated continuously based on currency trading data provided by the world's 500 largest banks. The method for calculating the US dollar index changed in 1999 after the introduction of EUR, which replaced the national currencies of a number of European countries.

USDX is calculated as a weighted geometric mean of a basket of currencies that includes major global currencies. Each currency in this basket belongs to a group of six major trading partners of the United States, which are not equal in their economic strength, so each currency in the index is given a specific share of influence (weight):

Currency
Ratio
EUR
0.576 (57.6%)
Japanese Yen (JPY)
0.136 (13.6%)
British Pound (GBP)
0.119 (11.9%)
Canadian Dollar (CAD)
0.091 (9.1%)
Swedish Krona (SEK)
0.042 (4.2%)
Swiss Franc (CHF)
0.036 (3.6%)

Equation for calculating the US dollar index:

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

The powers the rates are raised to correspond to the weight of the currencies in the basket used. The ratio 50.14348112 brings the dollar index to a value of 100.0 if the exchange rates for March 1973 are substituted into the equation. Thus, the current USDX reflects the change in the value of the US dollar against a basket of currencies compared to the 1973 quotes. An index value of less than 100 points indicates a depreciation of USD, while a value of more than 100 points indicates an increase in the USD value compared to 1973.

Euro Currency Index is the average rate of change in the exchange rates of five world currencies (the US dollar, the British pound, the Japanese yen, the Swiss franc and the Swedish krona) against the euro.

As a trading instrument, the euro index (EURX) was introduced on January 13, 2006 on the New York Board of Trade (NYBOT), ticker symbols ECX, EURX or E.

The EUR index has become a standard for the current value of the single European currency for participants in international financial markets, as well as a tool for conducting trading operations.

The calculation of the EUR index based on a basket of five currencies matches with the data used by the European Central Bank when calculating the trade-weighted euro index based on the currencies of those countries that form the main foreign trade turnover of the Eurozone countries. The largest share of international trade of the countries in the Eurozone is with the United States (31.55%), followed by the United Kingdom - 30.56%, Japan - 18.91%, Switzerland - 11.13% and Sweden - 7.85%.

The basic principles for calculating the current value of the UER Index are similar to those used to calculate USDX. The EUR Index is calculated using the weighted geometric mean calculation method:

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

where the power is the weight of currencies in the basket used.


Formulating the task

We need to create a synthetic instrument, the price of which is calculated according to the equations provided above. We need a full-fledged chart of the instrument, which will be updated with the arrival of each new tick on the symbols used in the basket of instruments, and on this chart we can run any indicators, scripts and EAs.

Generally, we need to create a chart of a synthetic instrument that is practically no different from the charts of standard instruments. We need a service program, which will do everything in its own flow, regardless of other open charts and programs running on them.

When launching the service, we will check for the presence of the required synthetic instrument, create it if necessary, and place it in the Market Watch window. The minute and tick history of the synthetic instrument is to be created afterwards followed by the chart of the created instrument. After these manipulations, the service will receive new ticks for each of the symbols, on the basis of which the price of the instrument is calculated, and new ticks will be added to the history of the created custom symbol. After restarting the terminal, the service program is automatically launched if it had been launched when the terminal was closed. Thus, once the required service is launched, it always restarts automatically when the terminal is started.

We will create two such custom symbols: the US dollar index and the EUR index. A separate service program will be created for each of these symbols. They will be based on a single include file, in which we will place all the functions for creating the required tool. The service program will only indicate the basket size, base ratio, and symbol structure with their weights required for calculating the instrument. Everything else will happen inside the included file when calling the functions set in it. This will allow us to create our own indices based on the created functions with a different set of currencies and their weights just by specifying a list of symbols and the weight of each of them.


Implementing the currency indices calculation functions file

In the \MQL5\Services\ terminal directory, create a new Indexes\ folder containing a new include file CurrencyIndex.mqh. It will store all the functions necessary for the project operation.

At the very beginning of the file, we will enter the structures and enumerations necessary for the macro substitution to work:

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

The structure of the basket symbol contains two fields: the name of the symbol and its weight in the instrument basket. When compiling a basket of instruments for calculating the index, it will be convenient to use an array of such structures: immediately, upon initialization, we will write the names of the symbols and their weights into it, and then from this array we will obtain the symbol and its weight in a loop for calculating the index prices. In this case, any symbols with their weights can be written in the array, which provides flexibility in creating any baskets of instruments for calculating indices.

The historical and tick data structures use arrays of the corresponding structures: MqlRates and MqlTick — they will contain data on each of the symbols in the instrument basket. There is also a data index in each of these structures. The index is required to indicate the number of the existing bar, from which data is taken to calculate the index. For example, to calculate an index on a bar, it is necessary that each of the symbols of the instrument basket participating in the index calculation have data on that minute bar. They may not necessarily be located on each of the symbols - somewhere there are gaps in bars (if there were no ticks on any of the symbols during this minute). In this case, it is necessary to indicate the index of the bar the data for the calculation is taken from (where there is no data on the symbol, the index increases) in order to take the data from the previous bar. And since we do not know in advance the number of symbols in the basket of instruments for calculating the index, we cannot declare in advance the required number of indices for each instrument in the program. Therefore, it is convenient to store and use them in such a structure.

The price type enumeration simply specifies constants that indicate the price to be obtained to calculate the bar prices.

When starting the service, first of all it is necessary to create a custom symbol, form historical М1 bars in a month and create a tick history. This will be the initialization of the service. Let's implement the following function:

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

Checking for existence and creating a custom symbol occurs in the CustomSymbolInitialize() function:

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

Here we check if there is a custom symbol with a given name. If not, create it. Next, look for an open chart of this symbol and, if the chart is not found among those open in the terminal, open the chart.

After creating a custom symbol and opening its chart, we need to create a history of M1 period for a month. This is done with the help of the PrepareRates() function:

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

The function first copies data for each of the symbols in the instrument basket and sets the total start time for copying data. A data index whose time coincides with the time of the other basket symbols, so that the starting date is the same for all basket symbols, is then set for each of the instruments. If there is no bar for a symbol by the starting date, then the index of the closest previous bar, on which the data exists, is set for it.

Next, in the cycle, each bar of the timeseries of the synthetic instrument is calculated bar by bar, adjusting the index of each subsequent bar so that the data on it is either from the current bar, if there is data on it, or from the previous one, if there is no data on the current bar. The calculated bars are added to the timeseries array of the synthetic instrument. After calculating all bars of the instrument's timeseries, the calculated timeseries is added to the price history of the custom symbol.

Historical data of one bar of a synthetic instrument is calculated in the CalculateRate() function:

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

For each of the bar prices (Open, High, Low and Close) of the synthetic instrument, the prices are calculated using the equation of the synthetic instrument:

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

The prices of each symbol of the basket are obtained in the GetRateValue() function:

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

The CalculateRate() function returns the result of checking the calculated prices of the synthetic instrument:

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

Since all prices of the synthetic instrument bar are calculated, it is necessary to check the validity of their calculation and correct errors (if any).

All this is done in the CheckRate() function whose result is returned:

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

If any of the calculated prices is plus or minus infinity, or "not a number" (NaN), or less than or equal to zero, the function returns false.

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/15684

Attached files |
CurrencyIndex.mqh (51.79 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.66 KB)
Last comments | Go to discussion (40)
Roman Shiredchenko
Roman Shiredchenko | 14 Apr 2025 at 17:15
Artyom Trishkin #:

Hmmm... When a person guesses, he writes code to check, instead of waiting for days for an answer

No time - I'm busy at another job.
I'll write in the code one of these days.

I agree, I just had to think a little and read the help better.

Thanks again. For me, of course, it is clearer through the help.... ) and thinking - racking my brains to check if it's right or wrong - I don't need to..... )

I have something to check both in the logic and in the code on trading conditions. )

Just wanted to clarify - there was no time to implement in the code. Weekend is over.....
Ryan L Johnson
Ryan L Johnson | 14 Apr 2025 at 21:13
Artyom Trishkin #:

I wanted to lead a person step by step to this simple conclusion. They took it and ruined everything :)

Sometimes you should make people think instead of copying ready-made things.

Ah well, it got me thinking. 😅

Roman Shiredchenko
Roman Shiredchenko | 25 Apr 2025 at 02:48

Artem could you look - earlier services used to draw ok - now they stopped....

example in attachment

restart - restarted.

used to draw - you can see from above where the indicator is red-blue, now it has turned into a strip...)



and I can't do anything with it yet...


I've run it and deleted it and recompiled it and reset it.


It draws sticks on the triple and that's all...))

Artyom Trishkin
Artyom Trishkin | 25 Apr 2025 at 03:33
Roman Shiredchenko #:
services used to draw ok - now they stopped....

Does the service attached to the article draw normally? Yes, it does.

So, you need to look for errors in yourself (whether all data, for example, are received for calculation), I guess...

Roman Shiredchenko
Roman Shiredchenko | 25 Apr 2025 at 04:14
Artyom Trishkin #:

Does the service attached to the article draw normally? I do.

So, you need to look for errors in yourself (whether all data, for example, are received for calculation), I guess...

Ok. I'll check everything again. I've run it before and everything worked.....
From Novice to Expert: Support and Resistance Strength Indicator (SRSI) From Novice to Expert: Support and Resistance Strength Indicator (SRSI)
In this article, we will share insights on how to leverage MQL5 programming to pinpoint market levels—differentiating between weaker and strongest price levels. We will fully develop a working, Support and Resistance Strength Indicator (SRSI).
A New Approach to Custom Criteria in Optimizations (Part 1): Examples of Activation Functions A New Approach to Custom Criteria in Optimizations (Part 1): Examples of Activation Functions
The first of a series of articles looking at the mathematics of Custom Criteria with a specific focus on non-linear functions used in Neural Networks, MQL5 code for implementation and the use of targeted and correctional offsets.
Resampling techniques for prediction and classification assessment in MQL5 Resampling techniques for prediction and classification assessment in MQL5
In this article, we will explore and implement, methods for assessing model quality that utilize a single dataset as both training and validation sets.
Tabu Search (TS) Tabu Search (TS)
The article discusses the Tabu Search algorithm, one of the first and most well-known metaheuristic methods. We will go through the algorithm operation in detail, starting with choosing an initial solution and exploring neighboring options, with an emphasis on using a tabu list. The article covers the key aspects of the algorithm and its features.