MT5: Кастомный индикатор Equity Curve для мультивалютного портфеля

MT5: Кастомный индикатор Equity Curve для мультивалютного портфеля MQL4 / MQL5 (MetaTrader)
Создайте собственный индикатор Equity Curve в MT5 для анализа производительности мультивалютного портфеля. Подробная инструкция и MQL5 код.
Суть: Создание кастомного индикатора Equity Curve в MetaTrader 5 для визуализации производительности мультивалютного портфеля. Индикатор рассчитывает кумулятивную прибыль/убыток на основе заданных символов и лотов, отображая результат в подокне графика.

Исходный код

Представляем MQL5-скрипт для создания кастомного индикатора Equity Curve (кривой доходности) вашего мультивалютного портфеля. Этот индикатор позволяет визуализировать, как изменялась бы стоимость вашего портфеля во времени, если бы вы держали заданные активы с определенными лотами, начиная с первой доступной свечи на графике, к которому прикреплен индикатор. Он рассчитывает кумулятивную прибыль или убыток для каждого символа и суммирует их, добавляя к начальному депозиту.

Для более продвинутых сценариев работы с данными, например, для экспорта тиков в реальном времени или интеграции с внешними базами данных, рекомендуем ознакомиться с нашими статьями: Экспорт тиков в SQL из MQL5: Скрипт реального времени и Интеграция MetaTrader 5 с PostgreSQL через libpq.dll в MQL5.


//+------------------------------------------------------------------+
//|                                     MultiCurrencyEquityCurve.mq5 |
//|                                                  Finfluct.com    |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Finfluct.com"
#property link      "https://finfluct.com"
#property version   "1.00"
#property description "Custom Equity Curve for a Multi-Currency Portfolio"

#property indicator_separate_window
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrBlue
#property indicator_width1 2

//--- indicator buffers
double ExtEquityBuffer[];

//--- input parameters
input double   InitialDeposit = 10000.0; // Начальный депозит портфеля
input string   SymbolsList    = "EURUSD,GBPUSD,USDJPY"; // Список символов через запятую
input string   LotsList       = "0.1,0.05,0.2"; // Список лотов для каждого символа через запятую

// Global arrays to store initial prices for each symbol and parsed inputs
double initial_prices[];
string portfolio_symbols[];
double portfolio_lots_parsed[]; // Use a double array for parsed lots
int    num_portfolio_symbols = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- indicator buffers mapping
   SetIndexBuffer(0, ExtEquityBuffer, INDICATOR_DATA);
   PlotIndexSetString(0, PLOT_LABEL, "Equity Curve");
   PlotIndexSetInteger(0, PLOT_AMP_MODE, AMP_MODE_NONE); // Disable auto-scaling for better visualization

   //--- set empty value for plotting
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); // Use 0.0 as empty value, will be replaced by InitialDeposit

   //--- check and parse input parameters
   string temp_symbols_array[];
   string temp_lots_array[];
   
   num_portfolio_symbols = StringSplit(SymbolsList, ',', temp_symbols_array);
   int num_lots_str = StringSplit(LotsList, ',', temp_lots_array);

   if (num_portfolio_symbols == 0 || num_lots_str == 0)
   {
      Print("Ошибка: Списки символов или лотов пусты.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if (num_portfolio_symbols != num_lots_str)
   {
      Print("Ошибка: Количество символов не соответствует количеству лотов. Символов: ", num_portfolio_symbols, ", Лотов: ", num_lots_str);
      return INIT_PARAMETERS_INCORRECT;
   }
   
   // Resize global arrays and copy parsed values
   ArrayResize(portfolio_symbols, num_portfolio_symbols);
   ArrayCopy(portfolio_symbols, temp_symbols_array);
   
   ArrayResize(portfolio_lots_parsed, num_portfolio_symbols);
   for(int i = 0; i < num_portfolio_symbols; i++)
   {
       portfolio_lots_parsed[i] = StringToDouble(StringTrim(temp_lots_array[i]));
       if (portfolio_lots_parsed[i] <= 0)
       {
           Print("Ошибка: Неверный размер лота для символа ", portfolio_symbols[i], ". Лоты должны быть положительными.");
           return INIT_PARAMETERS_INCORRECT;
       }
   }

   ArrayResize(initial_prices, num_portfolio_symbols);

   //---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   //--- check for minimum bars
   if (rates_total < 2) return(0);

   int limit = rates_total - prev_calculated;
   if (prev_calculated > 0) limit++; // Recalculate the last bar if it's updated

   // If this is the first calculation, or if parameters changed (full refresh),
   // we need to establish initial prices for all symbols.
   if (prev_calculated == 0 || limit == rates_total) 
   {
       for (int s = 0; s < num_portfolio_symbols; s++)
       {
           string symbol_name = StringTrim(portfolio_symbols[s]);
           MqlRates first_rates_symbol[];
           // Get the close price of the symbol at the time of the *first bar* of the current chart
           int first_copied = CopyRates(symbol_name, Period(), time[0], 1, first_rates_symbol);

           if (first_copied == 1)
           {
               initial_prices[s] = first_rates_symbol[0].close;
           }
           else
           {
               // If we can't get the initial price, set it to 0.0 to flag it for skipping
               initial_prices[s] = 0.0;
               Print("Предупреждение: Не удалось получить начальную цену для символа ", symbol_name, " на время ", TimeToString(time[0]), ". Этот символ будет пропущен.");
           }
       }
   }

   //--- loop through bars
   for (int i = 0; i < limit; i++)
   {
      double current_portfolio_pnl = 0.0;

      //--- loop through each symbol in the portfolio
      for (int s = 0; s < num_portfolio_symbols; s++)
      {
         string symbol_name = portfolio_symbols[s];
         double lot_size = portfolio_lots_parsed[s];

         if (lot_size <= 0 || initial_prices[s] == 0.0) // Skip if lot is invalid or initial price not found
         {
            continue;
         }

         double point = SymbolInfoDouble(symbol_name, SYMBOL_POINT);
         double tick_value = SymbolInfoDouble(symbol_name, SYMBOL_TRADE_TICK_VALUE);
         double tick_size = SymbolInfoDouble(symbol_name, SYMBOL_TRADE_TICK_SIZE);

         if (point == 0 || tick_value == 0 || tick_size == 0)
         {
             // Print("Ошибка: Не удалось получить торговые параметры для символа ", symbol_name, ". Пропускаем.");
             continue;
         }

         //--- Request historical data for the current symbol at the current bar's time
         MqlRates rates_symbol[];
         int copied = CopyRates(symbol_name, Period(), time[i], 1, rates_symbol);

         if (copied != 1)
         {
            // If data for this exact bar is not available, we skip this symbol for this bar.
            // A more advanced solution might use the previous bar's PnL for this symbol or interpolate.
            continue;
         }

         double symbol_close_price = rates_symbol[0].close;

         //--- Calculate PnL for this symbol relative to its initial price
         double pips_change = (symbol_close_price - initial_prices[s]) / point;
         double symbol_pnl = pips_change * lot_size * tick_value; // PnL in deposit currency

         current_portfolio_pnl += symbol_pnl;
      }

      //--- Store the cumulative equity for the current bar
      ExtEquityBuffer[i] = InitialDeposit + current_portfolio_pnl;
   }

   //--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+

Разбор параметров

  • InitialDeposit: Начальный размер вашего портфеля или стартовый капитал, от которого будет рассчитываться кривая доходности. Это базовая линия для отображения прибыли/убытка.
  • SymbolsList: Список торговых символов (инструментов), входящих в ваш портфель. Символы должны быть перечислены через запятую без пробелов (например, EURUSD,GBPUSD,USDJPY). Убедитесь, что MT5 имеет доступ к историческим данным по этим символам.
  • LotsList: Список размеров лотов, соответствующих каждому символу из SymbolsList. Порядок лотов должен строго соответствовать порядку символов. Например, если SymbolsList это EURUSD,GBPUSD, а LotsList это 0.1,0.05, это означает 0.1 лота EURUSD и 0.05 лота GBPUSD.

Как запустить

Чтобы использовать этот индикатор в вашем MetaTrader 5 терминале, выполните следующие шаги:

  1. Откройте MetaEditor: Нажмите F4 в вашем терминале MT5, чтобы открыть MetaEditor.
  2. Создайте новый индикатор: В MetaEditor выберите Файл -> Создать -> Пользовательский индикатор. Назовите его, например, MultiCurrencyEquityCurve.mq5, и нажмите Готово.
  3. Вставьте код: Удалите весь сгенерированный код и вставьте предоставленный выше MQL5-код в новый файл MultiCurrencyEquityCurve.mq5.
  4. Скомпилируйте: Нажмите кнопку Компилировать (или F7). Убедитесь, что в окне Ошибки нет критических ошибок.
  5. Прикрепите индикатор к графику: Вернитесь в терминал MT5. В окне Навигатор найдите ваш новый индикатор в разделе Индикаторы -> Пользовательские. Перетащите MultiCurrencyEquityCurve на любой график (например, EURUSD H1).
  6. Настройте параметры: При прикреплении индикатора появится окно настроек. Перейдите на вкладку Входные параметры и укажите значения для InitialDeposit, SymbolsList и LotsList в соответствии с вашим портфелем. Нажмите ОК.
  7. Анализируйте кривую: Индикатор появится в отдельном подокне под основным графиком, отображая кривую доходности вашего мультивалютного портфеля.

Этот подход позволяет быстро оценить историческую производительность гипотетического портфеля. Если вы заинтересованы в анализе более сложных данных, таких как отчеты COT, вы можете найти полезную информацию в статье Парсинг отчетов COT с CFTC и индикатор нетто-позиций для MT4, хотя она и ориентирована на MT4, принципы работы с данными остаются актуальными.

Оцените статью
FinFluct