Разработка пользовательского индикатора матрицы эффективности торгового счёта
Введение
Многие трейдеры часто сталкиваются с проблемой чрезмерной торговли, недостаточной дисциплиной и принятием решений на эмоциональном уровне, особенно при торговле в соответствии со строгими правилами проп-трейдинговой фирмы или при управлении собственным капиталом. Искушение отыграть убытки, увеличить размер лотов после серии проигрышей или игнорировать дневные лимиты часто приводит к нарушению правил просадки и преждевременному обнулению счетов. Даже опытные трейдеры могут попасть в ловушку несоблюдения ежедневных пороговых значений риска, что приводит к нестабильной доходности и неспособности достичь долгосрочной прибыльности или пройти отбор для проп-трейдинга.
Матрица показателей эффективности счета представляет собой практическое решение, выступая в качестве встроенного менеджера рисков и наставника по вопросам дисциплины. Предоставляя трейдерам возможность устанавливать собственный ежедневный процент риска, всегда в пределах правил максимальной просадки, система в режиме реального времени автоматически отслеживает состояние счета, ежедневную прибыль/убыток и просадки. При достижении дневного порога риска индикатор закрывает все позиции и блокирует торговлю до начала следующей торговой сессии, исключая возможность торговли «из мести» или чрезмерной торговли «на эмоциях». Эта структура помогает трейдерам добиваться последовательности, защищать свой капитал и повышать шансы на успешное прохождение проверок со стороны проп-фирм или на стабильный рост личных счетов.
Планирование и логика
1. Настройка системы сохранения:
- Баланс счета = 10 000 долларов США
- Максимальная общая просадка (6%) = 600 долларов США
- Максимальная суточная просадка (8%) = 800 долларов США
- Ежедневный риск (1%) = 100 долларов США
Торговая операция может приносить убытки в размере не более 100 долларов в день. Это дает им 6 убыточных дней до достижения максимальной просадки, что находится в пределах допустимых значений, установленных инвестиционной компанией.
2. Умеренная настройка:
- Баланс счета = 10 000 долларов США
- Максимальная общая просадка (6%) = 600 долларов США
- Максимальная дневная просадка (8%) = 800 долларов США
- Ежедневный риск (2%) = 200 долларов США
Трейдер рискует 200 долларами в день. Теперь у них осталось около 3 дней до достижения максимальной просадки. Это позволяет торговать более агрессивно, но ограничивает свободу действий.
3. Агрессивная настройка (не рекомендуется):
- Баланс счета = 10 000 долларов США
- Максимальная общая просадка (6%) = 600 долларов США
- Максимальная дневная просадка (8%) = 800 долларов США
- Ежедневный риск (2%) = 200 долларов США
Один неудачный день может лишить вас 500 долларов, оставив очень мало места ниже максимального лимита просадки в 600 долларов. Такая схема практически гарантирует провал, если не контролировать риски.
Характеристики индикатора:
- На панели управления отображаются показатели эффективности работы аккаунта.
- Визуальный статус риска торговли
- Прибыль/убыток (P/L) отображается на графике для открытых позиций.

Логика управления рисками

Начало работы
//+------------------------------------------------------------------+ //| Acc_Matrix.mq5 | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/johnhlomohang/" #property version "1.00" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #property indicator_type2 DRAW_LINE #property indicator_color2 clrCrimson #property indicator_style2 STYLE_SOLID #property indicator_width2 1 #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Input variables | //+------------------------------------------------------------------+ input bool ShowDashboard = true; // Show performance dashboard input bool ShowProfit_Loss = true; // Show Profit and Loss on the chart input color DashboardBGColor = clrWhiteSmoke; // Dashboard background color input color TextColor = clrBlack; // Text color input int DashboardX = 20; // Dashboard X position input int DashboardY = 20; // Dashboard Y position input int DashboardWidth = 280; // Dashboard width input int FontSize = 10; // Font size // Performance tracking mode input bool TrackFromIndicatorStart = true; // Track from indicator start (true) or overall (false) // Risk management parameters input double DailyRiskPercent = 1.0; // Daily risk percentage input double MaxDailyDrawdownPercent = 10.0; // Max daily drawdown percentage input double MaxOverallDrawdownPercent = 8.0; // Max overall drawdown percentage input bool EnableRiskManagement = true; // Enable risk management input ulong MagicNumber = 123456; // Magic number for position identification
Начнем с раздела входных переменных, который позволяет трейдерам настраивать внешний вид и поведение индикатора. Они могут переключаться между отображением панели мониторинга, показом прибыли или убытка непосредственно на графике, а также настройкой визуальных свойств, таких как цвет фона, цвет текста, его положение и размер шрифта. Эти опции предоставляют трейдерам возможность гибко адаптировать визуальный интерфейс к своим личным предпочтениям, сохраняя при этом доступ к основным данным счета в режиме реального времени.
Самое важное, что параметры управления рисками превращают индикатор в нечто большее, чем просто инструмент визуализации. Трейдеры могут задавать свой ежедневный процент риска, максимальный ежедневный процент просадки и максимальный общий процент просадки, гарантируя тем самым, что система будет поддерживать дисциплину и предотвращать безрассудную торговлю. С помощью включения функции управления рисками индикатор может отслеживать сделки (с помощью идентификации «магическим числом») и отправлять оповещения при превышении лимитов. Эта возможность помогает трейдерам поддерживать стабильность, избегать чрезмерной торговли и соблюдать правила проверки сделок, установленным проп-фирмами или следовать стратегиям роста личного счета.
//+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double BidBuffer[]; double AskBuffer[]; //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double currentProfit; double balance; double equity; double margin; double freeMargin; double marginLevel; int totalOrders; double totalProfit; double dailyProfit; datetime lastTradeTime; int winCount; int lossCount; double maxDrawdown; double maxDrawdownPercent; // Risk management variables double initialBalance; double dailyHighEquity; double dailyLowEquity; double dailyStartEquity; bool tradingEnabled = true; string riskStatus = "Trading Enabled"; color riskStatusColor = clrGreen; // Performance tracking variables int totalClosedTrades; int closedWinCount; int closedLossCount; double totalGains; double totalLosses; double largestWin; double largestLoss; double averageWin; double averageLoss; double profitFactor; // Track the last time history was updated datetime lastHistoryUpdate; bool firstUpdate = true;
В этом разделе кода определены глобальные переменные, которые индикатор использует для отслеживания эффективности, управления рисками и истории торгов по всем торговым сессиям. Первая группа переменных касается показателей эффективности работы счета, таких как текущая прибыль, остаток на счете, собственный капитал, маржа, свободная маржа и уровень маржи. Она также отслеживает количество открытых ордеров, общую прибыль, дневную прибыль, время последней сделки, а также количество выигрышных и проигрышных сделок. Кроме того, с их помощью отслеживают максимальную просадку стоимости активов как в абсолютном выражении, так и в процентах, что имеет решающее значение для оценки степени риска.
Вторая группа переменных выделена для управления рисками. Здесь код хранит исходный баланс на момент начала отслеживания, а также ежедневные максимальные, минимальные и начальные уровни собственного капитала. Эти показатели помогают измерять ежедневные колебания производительности и обеспечивать дисциплину, выявляя нарушения дневных или общих лимитов. Флаг tradingEnabled действует как главный переключатель: при превышении пороговых значений риска он отправляет оповещения. Визуальный элемент, реализованный с помощью строкового параметра riskStatus и параметра riskStatusColor, обеспечивает трейдеру четкую обратную связь на графике, показывая, например, сообщение «Торговля разрешена» зеленым цветом или предупреждения красным.
В заключительную группу входят переменные отслеживания эффективности для анализа закрытых сделок. Это включает в себя общее количество закрытых сделок, количество выигрышных и проигрышных сделок, совокупные прибыли и совокупные убытки, а также крупнейший зафиксированный выигрыш и крупнейший зафиксированный убыток. Кроме того, в ней рассчитываются средние значения выигрышей и проигрышей, а также профит-фактор (коэффициент прибыли), который является ключевым показателем эффективности, сравнивающим прибыли и убытки. Для обеспечения точности обновлений система отслеживает, когда история обновлялась в последний раз и является ли это первым обновлением с момента инициализации. Все вместе эти переменные дают трейдерам всестороннее представление об эффективности их торговли, что повышает согласованность результатов.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { IndicatorSetString(INDICATOR_SHORTNAME, "ACCOUNT PERFORMANCE MATRIX"); IndicatorSetInteger(INDICATOR_DIGITS, _Digits); // Initialize variables currentProfit = 0.0; balance = AccountInfoDouble(ACCOUNT_BALANCE); equity = AccountInfoDouble(ACCOUNT_EQUITY); // Set initial balance based on tracking mode if(TrackFromIndicatorStart) { initialBalance = balance; } else { // For overall tracking, use the actual account balance initialBalance = AccountInfoDouble(ACCOUNT_BALANCE); } dailyStartEquity = equity; dailyHighEquity = equity; dailyLowEquity = equity; lastHistoryUpdate = TimeCurrent(); // Load historical trade data based on tracking mode LoadHistory(); // Create dashboard objects if enabled if(ShowDashboard) { CreateDashboard(); } // Set timer to update every second EventSetTimer(1); return(INIT_SUCCEEDED); }
Функция OnInit() инициализирует пользовательский индикатор, устанавливая его отображаемое имя, точность и подготавливая к использованию все переменные для отслеживания производительности. Она сбрасывает текущую прибыль, восстанавливает баланс и капитал счета, а также определяет начальный баланс в зависимости от того, хочет пользователь отслеживать результаты с момента установки индикатора или на протяжении всей истории счета. Кроме того, она устанавливает начальный уровень капитала на день, одновременно регистрируя максимальные и минимальные значения капитала для оценки внутридневной доходности. Исторические данные о сделках загружаются с помощью функции LoadHistory() для обеспечения контекста для расчетов, а если включена опция панели мониторинга, визуальная панель производительности создается с помощью функции CreateDashboard(). Наконец, эта функция активирует событие таймера с обновлением каждую одну секунду, чтобы обеспечить сохранение синхронизации всех показателей счета и визуальных инструментов панели управления с торговой активностью в реальном времени.
//+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Delete all objects created by the indicator ObjectsDeleteAll(0, 0, -1); EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { // Update account information UpdateAccountInfo(); // Update historical data periodically to capture new closed trades if(TimeCurrent() - lastHistoryUpdate >= 5 || firstUpdate) // Update every 5 seconds or on first run { LoadHistory(); lastHistoryUpdate = TimeCurrent(); firstUpdate = false; } // Update dashboard if enabled if(ShowDashboard) { UpdateDashboard(); } // Check risk management rules if(EnableRiskManagement) { CheckRiskManagement(); } } //+------------------------------------------------------------------+ //| 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[]) { // Update bid and ask buffers if(ShowProfit_Loss) { UpdatePriceBuffers(rates_total, BidBuffer, AskBuffer, time); } return(rates_total); } //+------------------------------------------------------------------+ //| Update account information function | //+------------------------------------------------------------------+ void UpdateAccountInfo() { balance = AccountInfoDouble(ACCOUNT_BALANCE); equity = AccountInfoDouble(ACCOUNT_EQUITY); margin = AccountInfoDouble(ACCOUNT_MARGIN); freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); marginLevel = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); // Update daily high/low equity if(equity > dailyHighEquity) dailyHighEquity = equity; if(equity < dailyLowEquity) dailyLowEquity = equity; CalculateProfitMetrics(); CalculateTradeMetrics(); }
Функция OnDeinit() обеспечивает корректное завершение работы индикатора путем удаления всех созданных им объектов на графике и отключения события таймера для остановки фоновых обновлений. Это предотвращает загромождение графика после удаления индикатора и гарантирует, что никакие ненужные процессы не останутся активными. Функция OnTimer(), которая запускается каждую секунду, является центральной для функциональности индикатора: она непрерывно обновляет информацию об учетной записи, обновляет исторические данные о сделках каждые пять секунд (или при первом запуске) и управляет панелью мониторинга производительности, если она включена. Кроме того, она проверяет и обеспечивает соблюдение правил управления рисками, гарантируя, что при достижении пороговых значений риска трейдер получает оповещения в режиме реального времени.
Между тем, функция OnCalculate() интегрирует индикатор с обновлениями графика, гарантируя, что если отображение прибыли/убытка включено, то линии Bid и Ask обновляются через ценовые буферы. Функция UpdateAccountInfo() получает актуальные данные по счету, такие как баланс, капитал, маржа и свободная маржа, а также записывает ежедневные максимальные и минимальные значения капитала для точного отслеживания эффективности. Затем она вызывает вспомогательные функции, такие как CalculateProfitMetrics() и CalculateTradeMetrics(), для дальнейшего анализа производительности. Вместе эти функции создают самообновляющуюся, учитывающую риски панель мониторинга, которая предоставляет трейдерам полную информацию о состоянии их счета и активно способствует соблюдению дисциплины.
//+------------------------------------------------------------------+ //| Update account information function | //+------------------------------------------------------------------+ void UpdateAccountInfo() { balance = AccountInfoDouble(ACCOUNT_BALANCE); equity = AccountInfoDouble(ACCOUNT_EQUITY); margin = AccountInfoDouble(ACCOUNT_MARGIN); freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); marginLevel = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL); // Update daily high/low equity if(equity > dailyHighEquity) dailyHighEquity = equity; if(equity < dailyLowEquity) dailyLowEquity = equity; CalculateProfitMetrics(); CalculateTradeMetrics(); } //+------------------------------------------------------------------+ //| Update price buffers with Bid and Ask values | //+------------------------------------------------------------------+ void UpdatePriceBuffers(int rates_total, double &bidBuffer[], double &askBuffer[], const datetime &time[]) { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Calculate current profit for open positions currentProfit = 0.0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { currentProfit += PositionGetDouble(POSITION_PROFIT); } } // Fill buffers with current bid and ask values if(ShowProfit_Loss && rates_total > 0) { for(int i = 0; i < rates_total; i++) { bidBuffer[i] = bid; askBuffer[i] = ask; } // Calculate position for labels (2 bars to the right of current candle) int currentBar = rates_total - 1; datetime labelTime = (currentBar + 2 < rates_total) ? time[currentBar + 2] : time[currentBar] + PeriodSeconds() * 2; // Add profit/loss label with color coding string plLabelText; color plColor; if(currentProfit >= 0) { plLabelText = "Profit: " + DoubleToString(currentProfit, 2); plColor = clrBlue; } else { plLabelText = "Loss: " + DoubleToString(currentProfit, 2); plColor = clrRed; } CreateOrUpdateLabel("PLLabel", plLabelText, labelTime, (bid + ask) / 2, plColor, ANCHOR_RIGHT); } }
Функция UpdateAccountInfo() отвечает за получение актуальных данных об учетной записи и отслеживание важных показателей производительности. Эта функция извлекает из торгового счета текущий баланс, капитал, маржу, свободную маржу и уровень маржи. Более того, она отслеживает внутридневные колебания цен на акции, обновляя дневные максимумы и минимумы цен акций всякий раз, когда достигается новый крайний уровень. После обновления исходных данных счета эта функция вызывает функции CalculateProfitMetrics() и CalculateTradeMetrics(), которые выполняют более глубокий анализ, такой как расчет прибыли, отслеживание выигрышных/проигрышных сделок и оценка эффективности. Это обеспечивает постоянное отображение на панели управления самых актуальных данных об учетной записи.
Функция UpdatePriceBuffers() ориентирована на визуализацию цен и прибылей/убытков. Сначала она получает текущие цены Bid и Ask, затем перебирает открытые позиции, чтобы рассчитать текущую прибыль для активного символа. Если включена визуализация прибылей/убытков, она заполняет буферы индикатора последними значениями Bid и Ask по всему графику и создает метку, расположенную немного правее текущего бара. Эта метка отображает либо прибыль (синий цвет), либо убыток (красный цвет) в зависимости от текущего результата сделки. Сочетая отслеживание эффективности счета с визуализацией цен и прибыли в реальном времени, эта функция предоставляет трейдерам как числовую, так и графическую обратную связь, что улучшает осведомленность о ситуации и облегчает принятие решений.
//+------------------------------------------------------------------+ //| Create or update text label | //+------------------------------------------------------------------+ void CreateOrUpdateLabel(string name, string text, datetime time, double price, color clr, ENUM_ANCHOR_POINT anchor) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_TEXT, 0, time, price); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize); } else { ObjectMove(0, name, 0, time, price); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); } } //+------------------------------------------------------------------+ //| Calculate profit metrics | //+------------------------------------------------------------------+ void CalculateProfitMetrics() { totalProfit = equity - initialBalance; // Calculate max drawdown - FIXED: Prevent division by zero double drawdown = dailyHighEquity - equity; if(drawdown > maxDrawdown) { maxDrawdown = drawdown; // Avoid division by zero - use a small epsilon if dailyHighEquity is zero double denominator = (dailyHighEquity == 0) ? 0.000001 : dailyHighEquity; maxDrawdownPercent = (drawdown / denominator) * 100; } }
Функция UpdateAccountInfo() отвечает за получение актуальных данных об учетной записи и отслеживание важных показателей производительности. Эта функция извлекает из торгового счета текущий баланс, капитал, маржу, свободную маржу и уровень маржи. Более того, она отслеживает внутридневные колебания цен на акции, обновляя дневные максимумы и минимумы цен акций всякий раз, когда достигается новый крайний уровень. После обновления исходных данных счета функция вызывает функции CalculateProfitMetrics() и CalculateTradeMetrics(), которые выполняют более углубленный анализ, такой как расчет прибыли, отслеживание выигрышных/проигрышных сделок и оценка эффективности. Это обеспечивает постоянное отображение на панели управления самых актуальных данных об учетной записи.<br0/>
Функция UpdatePriceBuffers() ориентирована на визуализацию цен и прибылей/убытков. Сначала она получает текущие цены Bid и Ask, затем перебирает открытые позиции, чтобы рассчитать текущую прибыль для активного символа. Если включена визуализация прибылей/убытков, она заполняет буферы индикатора последними значениями Bid и Ask по всему графику и создает метку, расположенную немного правее текущего бара. Эта метка отображает либо прибыль (синий цвет), либо убыток (красный цвет) в зависимости от текущего результата сделки. Сочетая отслеживание эффективности счета с визуализацией цен и прибыли в реальном времени, эта функция предоставляет трейдерам как числовую, так и графическую обратную связь, что улучшает осведомленность о ситуации и облегчает принятие решений.
//+------------------------------------------------------------------+ //| Calculate trade metrics | //+------------------------------------------------------------------+ void CalculateTradeMetrics() { totalOrders = PositionsTotal(); winCount = 0; lossCount = 0; // Count winning and losing positions for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { double profit = PositionGetDouble(POSITION_PROFIT); if(profit > 0) winCount++; else if(profit < 0) lossCount++; } } } //+------------------------------------------------------------------+ //| Load historical trade data | //+------------------------------------------------------------------+ void LoadHistory() { datetime startDate = 0; if(TrackFromIndicatorStart) { // Only load history from when the indicator was started startDate = TimeCurrent() - 86400; // Load from 24 hours ago to ensure we capture all recent trades } HistorySelect(startDate, TimeCurrent()); int totalHistory = HistoryDealsTotal(); // Reset counters int newClosedTrades = 0; int newClosedWinCount = 0; int newClosedLossCount = 0; double newTotalGains = 0; double newTotalLosses = 0; double newLargestWin = 0; double newLargestLoss = 0; for(int i = 0; i < totalHistory; i++) { ulong ticket = HistoryDealGetTicket(i); if(ticket > 0) { // Check if this is a closing deal (not a deposit/withdrawal or opening trade) long dealType = HistoryDealGetInteger(ticket, DEAL_TYPE); if(dealType == DEAL_TYPE_BUY || dealType == DEAL_TYPE_SELL) { double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT); if(profit != 0) { newClosedTrades++; if(profit > 0) { newClosedWinCount++; newTotalGains += profit; if(profit > newLargestWin) newLargestWin = profit; } else { newClosedLossCount++; newTotalLosses += MathAbs(profit); if(profit < newLargestLoss) newLargestLoss = profit; } } } } } // Update the global variables totalClosedTrades = newClosedTrades; closedWinCount = newClosedWinCount; closedLossCount = newClosedLossCount; totalGains = newTotalGains; totalLosses = newTotalLosses; largestWin = newLargestWin; largestLoss = newLargestLoss; // Calculate averages and profit factor averageWin = (closedWinCount > 0) ? totalGains / closedWinCount : 0; averageLoss = (closedLossCount > 0) ? totalLosses / closedLossCount : 0; profitFactor = (totalLosses > 0) ? totalGains / totalLosses : (totalGains > 0) ? 1000 : 0; }
Функция CalcculateTradeMetrics() предоставляет актуальную информацию об открытых торговых позициях, подсчитывая, сколько из них в данный момент находятся в плюсе, а сколько — в минусе. Процесс начинается с получения общего количества открытых позиций, затем проходит цикл по каждой из них, проверяя значение прибыли или убытка. Если позиция прибыльная, она увеличивает счетчик выигрышей, а если убыточная, увеличивается счетчик проигрышей. Эта функция предоставляет трейдерам мгновенную обратную связь относительно качества открытых ими сделок, помогая оценить в режиме реального времени, соответствуют ли их позиции торговому плану.
Функция LoadHistory() позволяет глубже изучить историческую динамику, анализируя закрытые сделки в пределах указанного периода времени. Если отслеживание настроено с момента запуска индикатора, она загружает историю за последние 24 часа, чтобы гарантировать включение в нее недавних сделок. Затем система сканирует все исторические сделки, отфильтровывая только закрываемые сделки купли-продажи, игнорируя депозиты, снятия средств или записи. Для каждой закрытой сделки она регистрирует прибыль или убыток, обновляет данные о выигрышах и проигрышах, суммирует общие прибыли и убытки, а также отслеживает крупнейший отдельный выигрыш или проигрыш. Наконец, программа рассчитывает средние значения и профит-фактор — ключевой показатель, позволяющий сравнивать прибыли и убытки. Этот всесторонний анализ эффективности позволяет трейдерам выявлять закономерности в своем торговом поведении, оценивать прибыльность и в течение длительного времени поддерживать дисциплинированное управление рисками.
//+------------------------------------------------------------------+ //| Check risk management rules | //+------------------------------------------------------------------+ void CheckRiskManagement() { // Check if it's a new day (reset daily equity) MqlDateTime today, lastCheck; TimeToStruct(TimeCurrent(), today); TimeToStruct(TimeCurrent(), lastCheck); static int lastDay = -1; if(today.day != lastDay) { dailyStartEquity = equity; dailyHighEquity = equity; dailyLowEquity = equity; lastDay = today.day; // Re-enable trading at the start of a new day if(!tradingEnabled) { tradingEnabled = true; riskStatus = "Trading Enabled"; riskStatusColor = clrGreen; } } // Calculate daily drawdown percentage - FIXED: Prevent division by zero double dailyDrawdownPercent = 0; if(dailyHighEquity > 0) { dailyDrawdownPercent = (dailyHighEquity - equity) / dailyHighEquity * 100; } // Calculate overall drawdown percentage - FIXED: Prevent division by zero double overallDrawdownPercent = 0; if(initialBalance > 0) { overallDrawdownPercent = (initialBalance - equity) / initialBalance * 100; } double dailyRiskEquity = dailyStartEquity * (1 - DailyRiskPercent / 100); // Check if we've hit risk limits if(tradingEnabled) { if(equity <= dailyRiskEquity) { riskStatus = "Daily Risk Limit Reached"; riskStatusColor = clrRed; tradingEnabled = false; Alert("Daily Risk Limit Reached, Consider Closing Open Positions!!!"); } else if(dailyDrawdownPercent >= MaxDailyDrawdownPercent) { riskStatus = "Max Daily Drawdown Reached"; riskStatusColor = clrRed; tradingEnabled = false; Alert("Max Daily Drawdown Reached!!!"); } else if(overallDrawdownPercent >= MaxOverallDrawdownPercent) { riskStatus = "Max Overall Drawdown Reached"; riskStatusColor = clrRed; tradingEnabled = false; Alert("Max Overall Drawdown Reached!!!"); } } } //+------------------------------------------------------------------+ //| Create dashboard function | //+------------------------------------------------------------------+ void CreateDashboard() { // Create background rectangle ObjectCreate(0, "DashboardBG", OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, "DashboardBG", OBJPROP_XDISTANCE, DashboardX); ObjectSetInteger(0, "DashboardBG", OBJPROP_YDISTANCE, DashboardY); ObjectSetInteger(0, "DashboardBG", OBJPROP_XSIZE, DashboardWidth); ObjectSetInteger(0, "DashboardBG", OBJPROP_YSIZE, 320); ObjectSetInteger(0, "DashboardBG", OBJPROP_BGCOLOR, DashboardBGColor); ObjectSetInteger(0, "DashboardBG", OBJPROP_BORDER_TYPE, BORDER_FLAT); ObjectSetInteger(0, "DashboardBG", OBJPROP_BORDER_COLOR, clrGray); ObjectSetInteger(0, "DashboardBG", OBJPROP_BACK, true); ObjectSetInteger(0, "DashboardBG", OBJPROP_SELECTABLE, false); ObjectSetInteger(0, "DashboardBG", OBJPROP_SELECTED, false); ObjectSetInteger(0, "DashboardBG", OBJPROP_HIDDEN, true); ObjectSetInteger(0, "DashboardBG", OBJPROP_ZORDER, 0); // Create title ObjectCreate(0, "DashboardTitle", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "DashboardTitle", OBJPROP_XDISTANCE, DashboardX + 10); ObjectSetInteger(0, "DashboardTitle", OBJPROP_YDISTANCE, DashboardY + 10); ObjectSetString(0, "DashboardTitle", OBJPROP_TEXT, "ACCOUNT PERFORMANCE MATRIX"); ObjectSetInteger(0, "DashboardTitle", OBJPROP_COLOR, TextColor); ObjectSetInteger(0, "DashboardTitle", OBJPROP_FONTSIZE, FontSize + 2); // Create tracking mode indicator string trackingMode = TrackFromIndicatorStart ? "From Indicator Start" : "Overall Account"; ObjectCreate(0, "TrackingModeLabel", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "TrackingModeLabel", OBJPROP_XDISTANCE, DashboardX + 10); ObjectSetInteger(0, "TrackingModeLabel", OBJPROP_YDISTANCE, DashboardY + 30); ObjectSetString(0, "TrackingModeLabel", OBJPROP_TEXT, "Tracking: " + trackingMode); ObjectSetInteger(0, "TrackingModeLabel", OBJPROP_COLOR, clrDarkBlue); ObjectSetInteger(0, "TrackingModeLabel", OBJPROP_FONTSIZE, FontSize); // Create information labels CreateDashboardLabel("RiskStatusLabel", "Trading Status: ", 50, DashboardX, DashboardY); CreateDashboardLabel("BalanceLabel", "Balance: ", 70, DashboardX, DashboardY); CreateDashboardLabel("EquityLabel", "Equity: ", 90, DashboardX, DashboardY); CreateDashboardLabel("DailyProfitLabel", "Daily P/L: ", 110, DashboardX, DashboardY); CreateDashboardLabel("TotalProfitLabel", "Total P/L: ", 130, DashboardX, DashboardY); CreateDashboardLabel("PositionsLabel", "Open Positions: ", 150, DashboardX, DashboardY); CreateDashboardLabel("WinRateLabel", "Win Rate: ", 170, DashboardX, DashboardY); CreateDashboardLabel("DailyRiskLabel", "Daily Risk: ", 190, DashboardX, DashboardY); CreateDashboardLabel("DailyDrawdownLabel", "Daily Drawdown: ", 210, DashboardX, DashboardY); CreateDashboardLabel("TotalDrawdownLabel", "Total Drawdown: ", 230, DashboardX, DashboardY); CreateDashboardLabel("ProfitFactorLabel", "Profit Factor: ", 250, DashboardX, DashboardY); CreateDashboardLabel("TradesLabel", "Total Trades: ", 270, DashboardX, DashboardY); CreateDashboardLabel("AvgWinLossLabel", "Avg Win/Loss: ", 290, DashboardX, DashboardY); }
Функция CheckRiskManagement() — это механизм контроля, который обеспечивает соблюдение трейдерами своего плана управления рисками и не позволяют превышать лимиты счета. Затем программа рассчитывает ежедневный и общий проценты просадки, тщательно проверяя наличие деления на ноль во избежание ошибок. Функция сравнивает текущий остаток на счете с предварительно заданным трейдером дневным лимитом риска, максимальной дневной просадкой и максимальной общей просадкой. При превышении какого-либо из этих пороговых значений сообщение о состоянии станет красным, и сработают оповещения, предупреждающие трейдера. Такой подход не только помогает предотвратить катастрофические потери, но и прививает дисциплину, устраняя соблазн продолжать торговлю после достижения лимитных значений.
Функция CreateDashboard() создает экранный интерфейс, отображающий все отслеживаемые показатели на организованной и визуально понятной панели. Процесс начинается с создания фонового прямоугольника, оформленного заданными пользователем цветами, размерами и границами, который будет служить контейнером для всех данных об эффективности. Затем добавляются заголовок и метка режима отслеживания, чтобы указать, отслеживает панель мониторинга результаты с момента запуска индикатора или за всю историю учетной записи. Ниже, в структурированных строках, расположен ряд полей с соответствующими метками, таких как баланс, капитал, ежедневная и общая прибыль/убыток, процент выигрышей, просадки, профит-фактор и средний выигрыш/убыток. Эта визуальная панель мониторинга позволяет трейдерам мгновенно оценивать как состояние своего счета, так и уровень риска, не углубляясь в многочисленные отчеты, что делает ее мощным инструментом для поддержания ясности и дисциплины в торговле.
//+------------------------------------------------------------------+ //| Create dashboard label helper function | //+------------------------------------------------------------------+ void CreateDashboardLabel(string name, string text, int yOffset, int x, int y) { ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x + 10); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y + yOffset); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_COLOR, TextColor); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize); } //+------------------------------------------------------------------+ //| Update dashboard function | //+------------------------------------------------------------------+ void UpdateDashboard() { // Update risk status with color coding ObjectSetString(0, "RiskStatusLabel", OBJPROP_TEXT, "Trading Status: " + riskStatus); ObjectSetInteger(0, "RiskStatusLabel", OBJPROP_COLOR, riskStatusColor); // Update values for all dashboard labels UpdateDashboardLabel("BalanceLabel", "Balance: " + DoubleToString(balance, 2)); UpdateDashboardLabel("EquityLabel", "Equity: " + DoubleToString(equity, 2)); // Color code profit values double dailyPL = equity - dailyStartEquity; string dailyPLText = "Daily P/L: "; if(dailyPL >= 0) dailyPLText += "+" + DoubleToString(dailyPL, 2); else dailyPLText += DoubleToString(dailyPL, 2); UpdateDashboardLabel("DailyProfitLabel", dailyPLText); string totalPLText = "Total P/L: "; if(totalProfit >= 0) totalPLText += "+" + DoubleToString(totalProfit, 2); else totalPLText += DoubleToString(totalProfit, 2); UpdateDashboardLabel("TotalProfitLabel", totalPLText); UpdateDashboardLabel("PositionsLabel", "Open Positions: " + IntegerToString(totalOrders)); // Calculate and display win rate from closed trades int totalTrades = closedWinCount + closedLossCount; string winRateText = "Win Rate: "; if(totalTrades > 0) { double winRate = (double)closedWinCount / totalTrades * 100; winRateText += DoubleToString(winRate, 1) + "% (" + IntegerToString(closedWinCount) + "/" + IntegerToString(totalTrades) + ")"; } else winRateText += "N/A"; UpdateDashboardLabel("WinRateLabel", winRateText); // Risk metrics double dailyRiskEquity = dailyStartEquity * (1 - DailyRiskPercent / 100); // Calculate drawdown percentages with zero division protection double dailyDrawdownPercent = 0; if(dailyHighEquity > 0) { dailyDrawdownPercent = (dailyHighEquity - equity) / dailyHighEquity * 100; } double overallDrawdownPercent = 0; if(initialBalance > 0) { overallDrawdownPercent = (initialBalance - equity) / initialBalance * 100; } UpdateDashboardLabel("DailyRiskLabel", "Daily Risk: " + DoubleToString(DailyRiskPercent, 1) + "% (" + DoubleToString(dailyRiskEquity, 2) + ")"); string dailyDrawdownText = "Daily Drawdown: " + DoubleToString(dailyDrawdownPercent, 1) + "%"; UpdateDashboardLabel("DailyDrawdownLabel", dailyDrawdownText); string totalDrawdownText = "Total Drawdown: " + DoubleToString(overallDrawdownPercent, 1) + "%"; UpdateDashboardLabel("TotalDrawdownLabel", totalDrawdownText); // Performance metrics UpdateDashboardLabel("ProfitFactorLabel", "Profit Factor: " + DoubleToString(profitFactor, 2)); UpdateDashboardLabel("TradesLabel", "Total Trades: " + IntegerToString(totalClosedTrades)); UpdateDashboardLabel("AvgWinLossLabel", "Avg Win/Loss: " + DoubleToString(averageWin, 2) + "/" + DoubleToString(MathAbs(averageLoss), 2)); } //+------------------------------------------------------------------+ //| Update dashboard label helper function | //+------------------------------------------------------------------+ void UpdateDashboardLabel(string name, string text) { ObjectSetString(0, name, OBJPROP_TEXT, text); }
Функция CreateDashboardLabel() служит вспомогательным инструментом для быстрого создания полей с метками на панели мониторинга. Каждая метка создается с заданными именем, текстом и смещением позиции, и к ней применяется единообразный стиль, например размер и цвет шрифта. Это гарантирует, что все показатели на панели управления будут четко видны и отформатированы в едином стиле, предоставляя трейдерам упорядоченный обзор эффективности счета и состояния риска.
Функция UpdateDashboard() непрерывно обновляет панель мониторинга, отображая актуальные данные об учетных записях и показателях эффективности. Она обновляет все ключевые показатели, включая баланс, капитал, ежедневную и общую прибыль/убыток, открытые позиции, процент выигрышных сделок, просадки, ежедневный риск, профит-фактор, общее количество сделок и средний процент выигрышных/проигрышных сделок. Кроме того, эта функция использует цветовую кодировку для важной информации, такой как прибыль/убыток и статус сделки, обеспечивая мгновенную визуальную обратную связь. Постоянно отражая состояние счета в режиме реального времени, система помогает трейдерам принимать обоснованные решения, укрепляет дисциплину и предотвращает чрезмерную торговлю, четко отображая моменты достижения лимитов по рискам.
Демонстрационный индикатор:

Заключение
Резюмируя все сказанное выше: мы разработали собственный индикатор матрицы эффективности счета, который отслеживает как показатели торговой активности в реальном времени, так и исторические данные. Этот индикатор отслеживает ключевые показатели счета, такие как баланс, собственный капитал, маржа, открытые позиции, ежедневная и общая прибыль/убыток, процент выигрышных сделок, просадки, коэффициент прибыльности (профит-фактор) и среднее соотношение выигрышных и проигрышных сделок. Он включает в себя динамическую панель управления с визуальными метками и цветовыми оповещениями, предоставляющую трейдерам четкое и систематизированное представление о результатах работы их счета. В систему интегрированы правила управления рисками, обеспечивающие соблюдение ежедневных лимитов по рискам и максимальных просадок; при достижении этих лимитов система оповещает трейдера о текущей динамике счета.
В заключение скажем, что этот индикатор помогает трейдерам поддерживать дисциплину, избегать чрезмерной торговли и принимать более обоснованные решения, предоставляя прозрачный обзор состояния счета и степени подверженности риску. Четко указывая на приближение к пороговым значениям риска или их превышении, он стимулирует последовательное торговое поведение и способствует устойчивому росту. Независимо от того, готовится трейдер к сдаче экзамена в проп-трейдерской фирме или управляет личным торговым счетом, этот инструмент дает структурированные рекомендации по управлению рисками, оптимизации торговых решений и, в конечном итоге, повышению долгосрочной прибыльности.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19508
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Знакомство с языком MQL5 (Часть 32): Освоение API и функции WebRequest в языке MQL5 (VI)
От новичка до эксперта: Разработка стратегии торговли по зонам ликвидности
Знакомство с языком MQL5 (Часть 30): Освоение API и функции WebRequest в языке MQL5 (IV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования