StopFirst-Core/Include/StopFirst/Account.mqh
RUSLAN SLOBODIANIUK 6ba79702fd Make the header comments self-contained
They referred to project documents that are not part of this repository.
Comments only; PriceToPips is now documented as not rounding, which is what
it does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 21:48:01 +03:00

128 lines
6.2 KiB
MQL5

//+------------------------------------------------------------------+
//| Account.mqh|
//| Copyright 2026, bandersoft |
//| https://www.mql5.com/en/users/fats |
//+------------------------------------------------------------------+
//| |
//| Account metrics, compounding, summary string. |
//| |
//+------------------------------------------------------------------+
#ifndef STOPFIRST_ACCOUNT_MQH
#define STOPFIRST_ACCOUNT_MQH
#include <Trade\AccountInfo.mqh>
//--- GetTradeStats reports position counts and profit
#include "Positions.mqh"
#include "Defs.mqh"
//+------------------------------------------------------------------+
//| Calculate compound growth |
//| |
//| Returns: initial compounded at ratePercent over periods. |
//+------------------------------------------------------------------+
double CalcCompoundGrowth(double initial, double ratePercent, int periods)
{
return initial * MathPow(1 + ratePercent / 100.0, periods);
}
//+------------------------------------------------------------------+
//| Does this account keep one position per ticket? |
//| |
//| Hedging accounts hold a position per ticket and can be long and |
//| short the same symbol at once. Netting accounts hold one position |
//| per symbol, so an opposing order reduces or reverses it. Several |
//| operations, partial close among them, work differently on each. |
//| |
//| Returns: true for a retail hedging account, false otherwise. |
//+------------------------------------------------------------------+
bool IsHedgingAccount()
{
return (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE) ==
ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;
}
//+------------------------------------------------------------------+
//| Open loss as a percentage of balance |
//| |
//| Not drawdown: balance minus equity is the CURRENT floating loss, |
//| and drawdown is measured from peak equity - for that, see |
//| GetPeakDrawdownPercent(). |
//| |
//| Returns: (balance - equity) / balance * 100, 2 decimals. |
//| NEGATIVE when open trades are in profit - that is not an |
//| error, it is what the quantity means. |
//| 0 if balance is zero. |
//+------------------------------------------------------------------+
double GetFloatingLossPercent()
{
CAccountInfo accountInfo;
double balance = accountInfo.Balance();
double equity = accountInfo.Equity();
if(balance == 0) return 0;
return NormalizeDouble(((balance - equity) / balance) * 100.0, 2);
}
//--- highest equity seen since tracking began, for GetPeakDrawdownPercent
double g_peakEquity = 0;
//+------------------------------------------------------------------+
//| Restart peak-equity tracking from the current equity |
//| Returns: nothing. |
//+------------------------------------------------------------------+
void ResetPeakEquity()
{
CAccountInfo accountInfo;
g_peakEquity = accountInfo.Equity();
}
//+------------------------------------------------------------------+
//| Drawdown from the highest equity seen so far |
//| |
//| The peak is built up from the first call onwards, so it measures |
//| the run since the program started, not the account lifetime high |
//| - call it on a schedule (each tick or each bar) or the peak means |
//| little. |
//| ResetPeakEquity() starts the measurement over. A withdrawal |
//| lowers equity like a loss and reads as drawdown: call |
//| ResetPeakEquity() after moving money out. |
//| |
//| Returns: (peak - equity) / peak * 100, 2 decimals. 0 at a new |
//| high, and 0 if peak equity is not positive. |
//+------------------------------------------------------------------+
double GetPeakDrawdownPercent()
{
CAccountInfo accountInfo;
double equity = accountInfo.Equity();
if(equity > g_peakEquity) g_peakEquity = equity;
if(g_peakEquity <= 0) return 0;
return NormalizeDouble(((g_peakEquity - equity) / g_peakEquity) * 100.0, 2);
}
//+------------------------------------------------------------------+
//| Print trade statistics summary |
//| |
//| Returns: a multi-line summary of balance, equity, margin, |
//| drawdown and open exposure. Never empty. |
//+------------------------------------------------------------------+
string GetTradeStats(ulong magic = STOPFIRST_ANY_MAGIC)
{
CAccountInfo accountInfo;
string stats = "=== Trade Statistics ===\n";
stats += "Balance: " + DoubleToString(accountInfo.Balance(), 2) + "\n";
stats += "Equity: " + DoubleToString(accountInfo.Equity(), 2) + "\n";
stats += "Free Margin: " + DoubleToString(accountInfo.FreeMargin(), 2) + "\n";
stats += "Margin Level: " + DoubleToString(accountInfo.MarginLevel(), 2) + "%\n";
stats += "Floating Loss: " + DoubleToString(GetFloatingLossPercent(), 2) + "%\n";
stats += "Open Positions: " + IntegerToString(GetPositionCount("", magic)) + "\n";
stats += "Total Profit: " + DoubleToString(GetTotalProfit("", magic), 2) + "\n";
return stats;
}
#endif // STOPFIRST_ACCOUNT_MQH
//+------------------------------------------------------------------+