Исходный код
Представляем 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 терминале, выполните следующие шаги:
- Откройте MetaEditor: Нажмите
F4в вашем терминале MT5, чтобы открыть MetaEditor. - Создайте новый индикатор: В MetaEditor выберите
Файл -> Создать -> Пользовательский индикатор. Назовите его, например,MultiCurrencyEquityCurve.mq5, и нажмитеГотово. - Вставьте код: Удалите весь сгенерированный код и вставьте предоставленный выше MQL5-код в новый файл
MultiCurrencyEquityCurve.mq5. - Скомпилируйте: Нажмите кнопку
Компилировать(илиF7). Убедитесь, что в окнеОшибкинет критических ошибок. - Прикрепите индикатор к графику: Вернитесь в терминал MT5. В окне
Навигаторнайдите ваш новый индикатор в разделеИндикаторы -> Пользовательские. ПеретащитеMultiCurrencyEquityCurveна любой график (например, EURUSD H1). - Настройте параметры: При прикреплении индикатора появится окно настроек. Перейдите на вкладку
Входные параметрыи укажите значения дляInitialDeposit,SymbolsListиLotsListв соответствии с вашим портфелем. НажмитеОК. - Анализируйте кривую: Индикатор появится в отдельном подокне под основным графиком, отображая кривую доходности вашего мультивалютного портфеля.
Этот подход позволяет быстро оценить историческую производительность гипотетического портфеля. Если вы заинтересованы в анализе более сложных данных, таких как отчеты COT, вы можете найти полезную информацию в статье Парсинг отчетов COT с CFTC и индикатор нетто-позиций для MT4, хотя она и ориентирована на MT4, принципы работы с данными остаются актуальными.




