English Deutsch
preview
カスタム口座パフォーマンス行列インジケーターの開発

カスタム口座パフォーマンス行列インジケーターの開発

MetaTrader 5 |
78 0
Hlomohang John Borotho
Hlomohang John Borotho

はじめに

多くのトレーダーは、特に厳格なプロップファームのルール下で取引する場合や、自身の資金を運用する場合に、過剰取引や規律の乱れ、感情的な判断に陥りやすくなります。損失を追いかけて取引量を増やしたり、連敗後にロットを引き上げたり、日次制限を無視してしまう行為は、結果的にドローダウンルール違反や口座の早期破綻を招きます。経験豊富なトレーダーであっても、日々のリスク限度を尊重しきれず、パフォーマンスの一貫性を損ない、長期的な収益性の確保やプロップファームチャレンジの達成を妨げることがあります。

Account Performance Matrixインジケーターは、実用的な解決策として機能します。最大ドローダウン規定の範囲内で日次リスク%を設定できるようにし、インジケーターは口座エクイティ、日次損益、ドローダウンをリアルタイムで自動監視します。設定した日次リスク閾値に達した場合、インジケーターはすべてのポジションを決済し、次の取引セッションが始まるまで新規取引をロックします。これにより、リベンジ取引や感情による過剰取引を排除し、トレーダーが規律を維持しやすい環境を構築します。こうした仕組みは、資金保全とパフォーマンスの安定化を促し、プロップファームチャレンジの合格率向上や個人口座の着実な成長につながります。



計画と論理

1. 保守的な設定

  • 口座残高 = $10,000
  • 最大総ドローダウン(6%) = $600
  • 最大日次ドローダウン(8%) = $800
  • 日次リスク(1%) = $100

この設定では、1日に許容される最大損失は$100になります。最大総ドローダウンに到達するまでに6回の連続損失が可能であり、プロップファームで求められる安全基準の範囲内で安定した運用ができます。

2. 中程度の設定

  • 口座残高 = $10,000
  • 最大総ドローダウン(6%) = $600
  • 最大日次ドローダウン(8%) = $800
  • 日次リスク(2%) = $200

この設定では、トレーダーは1日に$200のリスクを取ります。最大総ドローダウンに達するまで約3日の余裕しかありません。より積極的な取引が可能になる一方、許容される損失幅は狭くなります。

3. 積極的な設定(非推奨)

  • 口座残高 = $10,000
  • 最大総ドローダウン(6%) = $600
  • 最大日次ドローダウン(8%) = $800
  • 日次リスク(5%) = $500

この設定では、1日の悪い取引だけで$500を失う可能性があり、$600の最大総ドローダウンまでほとんど余裕が残りません。リスク管理を徹底しない限り、失敗を招く可能性が非常に高い設定です。

インジケーターの機能

  • ダッシュボードとして口座パフォーマンス行列を表示する
  • 現在のリスク状態を視覚的に示す
  • 保有中ポジションの損益(P/L)をチャートに表示する


リスク管理ロジック


導入手順

//+------------------------------------------------------------------+
//|                                                   Acc_Matrix.mq5 |
//|                        GIT under Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/ja/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "GIT under Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/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;

このコードセクションでは、インジケーターがセッションをまたいでパフォーマンス、リスク管理、取引履歴を追跡するために使用するグローバル変数が定義されています。最初のグループは、口座パフォーマンス指標を扱う変数で、現在の損益、残高、エクイティ、証拠金、余剰証拠金、証拠金維持率などが含まれます。さらに、保有中注文数、累計損益、日次損益、最終取引時刻、勝ち負けの件数といった情報も保持します。また、絶対値および割合ベースの最大ドローダウンを監視しており、これはリスクエクスポージャーを評価するうえで極めて重要です。

2番目のグループは、リスク管理専用の変数で構成されています。ここでは、追跡開始時の初期残高に加え、日次のエクイティ高値、安値、開始値が記録されます。これらの値は日次のパフォーマンス変動を測定し、日次あるいは総合リスク上限の違反を検出することで規律を強制する役割を果たします。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()を呼び出して視覚的なパフォーマンスパネルを生成します。最後に、1秒ごとの更新をおこなうタイマーイベントを有効化し、口座指標およびダッシュボードの表示内容がリアルタイムの取引状況と常に同期するようにします。

//+------------------------------------------------------------------+
//| 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()関数は、インジケーターの終了時処理を担当し、インジケーターが作成したすべてのチャートオブジェクトを削除し、バックグラウンド更新をおこなっているタイマーイベントを停止することで、クリーンな終了を実現します。これにより、インジケーター削除後にチャートが不要なオブジェクトで散らかることを防ぎ、余計なプロセスが動き続けることもありません。一方、1秒ごとに実行されるOnTimer()関数は、このインジケーターの中核となる機能を担っています。口座情報を継続的に更新し、取引履歴データを初回および5秒ごとに更新し、ダッシュボードが有効な場合はその表示内容も更新します。また、リスク管理ルールの監視と適用をおこない、設定したリスク閾値に達した際にはリアルタイムでアラートを発することで、トレーダーが即座に状況を把握できるようにします。

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()を呼び出します。これらは、利益計算、取引の勝ち負けの追跡、パフォーマンス評価などのより詳細な分析を処理します。この仕組みによって、ダッシュボードは常に最新の口座状況を反映するよう維持されます。

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

CalculateTradeMetrics()関数は、現在保有している取引ポジションの状況をリアルタイムで把握するためのスナップショットを提供します。まず、開いているポジションの総数を取得し、各ポジションを順に走査して、その損益値を確認します。利益が出ている場合は勝ちカウンタを、損失が出ている場合は負けカウンタをそれぞれ増加させます。この処理により、トレーダーは現在の保有ポジションがどれほど健全かを即座に把握でき、自身の取引計画に沿った状態を保てているかどうかをリアルタイムで判断できるようになります。

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()関数は、口座およびパフォーマンスデータをリアルタイムで反映するよう、ダッシュボードを継続的に更新します。残高、エクイティ、日次および累計の損益、保有ポジション数、勝率、ドローダウン、日次リスク、プロフィットファクター、総取引数、平均利益/平均損失といった主要指標を順次更新します。また、損益値や取引ステータスなど重要な情報には色分けを施し、即座に把握できる視覚的フィードバックを提供します。リアルタイムの口座状況が常に明確に表示されることで、トレーダーは状況に基づいた判断をおこないやすくなり、規律を強化し、リスク限度到達時には過剰な取引を防ぐ助けにもなります。

インジケーターのデモ



結論

本記事では、リアルタイムおよび過去の取引パフォーマンスを一元的に追跡する、カスタムのAccount Performance Matrixインジケーターを開発しました。本インジケーターは、残高、エクイティ、証拠金、保有ポジション数、日次および累計の損益、勝率、各種ドローダウン、プロフィットファクター、平均利益/平均損失といった主要な口座指標を監視します。さらに、視覚的ラベルや色分けされたアラートを備えた動的ダッシュボードを統合し、口座パフォーマンスを明確かつ整理された形で提示します。加えて、日次リスク上限や最大ドローダウンを強制するリスク管理ルールを組み込み、限度に到達した際はトレーダーへ警告を出します。

総じて、このインジケーターはトレーダーが規律を維持し、過剰取引を避け、より根拠に基づいた判断をおこなうための透明性の高い口座状況とリスク露出の可視化を提供します。また、リスク閾値に近づいた段階、あるいは超過した瞬間を明確に示すことで、一貫した取引行動を促し、持続的な成長をサポートします。プロップファームチャレンジの突破を目指すトレーダーであれ、自身の口座を運用する個人トレーダーであれ、本ツールはリスク管理を構造化し、取引判断の最適化を支援し、長期的な収益性の向上に寄与する実践的なガイドとなります。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19508

添付されたファイル |
Acc_Matrix.mq5 (25.52 KB)
Parafrac V2オシレーター:パラボリックSARとATRの統合 Parafrac V2オシレーター:パラボリックSARとATRの統合
Parafrac V2オシレーターは、パラボリックSARとATR(Average True Range、平均真の範囲)を統合した高度なテクニカル分析ツールです。前バージョンのParafracオシレーターではフラクタルを使用していたため、過去や現在のシグナルを覆い隠すようなスパイクが発生しやすいという課題がありました。Parafrac V2ではATRによるボラティリティ測定を活用することで、トレンドや反転、ダイバージェンスの検出をより滑らかで信頼性の高い方法で行えるようになり、チャートの混雑や分析の過負荷を軽減できます。
MQL5におけるパイプライン MQL5におけるパイプライン
本記事では、機械学習におけるデータ準備工程の中で、重要性が急速に高まっているデータ前処理パイプラインを取り上げます。前処理パイプラインとは、生データをモデルに入力する前に通す一連の変換ステップを整理し、効率化したものです。一見地味な作業ですが、前処理(特にスケーリング)は学習時間や実行コストを削減するだけでなく、モデルの汎化性能を大きく左右します。本記事ではscikit-learnの前処理関数を中心に扱います。MQL5ウィザードはここでは使用しませんが、後続の記事で取り上げる予定です。
MQL5入門(第21回):ハーモニックパターン検出の自動化 MQL5入門(第21回):ハーモニックパターン検出の自動化
MetaTrader 5でMQL5を使ってガートリーハーモニックパターンを検出して表示する方法を学びます。この記事では、スイングポイントの特定からフィボナッチ比率の適用、チャート上へのパターン描画までの手順を順を追って解説し、視覚的に確認できる形で表示する方法を紹介します。
初心者からエキスパートへ:NFP発表後の市場取引におけるフィボナッチ戦略の実装 初心者からエキスパートへ:NFP発表後の市場取引におけるフィボナッチ戦略の実装
金融市場において、リトレースメントの法則は最も否定しがたい力の一つです。価格は必ずリトレースするというのが経験則であり、大きな値動きにおいても、最小のティックパターンにおいても、ジグザグの形で現れることが多くあります。しかし、リトレースメントのパターン自体は固定されておらず、不確実で予測が難しいのが現状です。この不確実性があるため、トレーダーは複数のフィボナッチレベルを参照し、それぞれの影響力を確率的に考慮します。本記事では、主要経済指標発表後の短期売買における課題に対処するため、フィボナッチ手法を応用した精緻な戦略を紹介します。リトレースメントの原則とイベントドリブンの市場動向を組み合わせることで、より信頼性の高いエントリーおよびエグジットの機会を見出すことを目指します。ディスカッションに参加し、フィボナッチをイベント後取引にどのように適応できるかをご覧ください。