MQLArticles/RM/AccountStatus.mqh

402 lines
33 KiB
MQL5
Raw Permalink Normal View History

2025-09-22 09:09:20 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| AccountStatus.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com"
#property strict
#ifndef ACCOUNT_STATUS_BASES_MQH
#define ACCOUNT_STATUS_BASES_MQH
#include "RM_Functions.mqh"
#include <Math/Stat/Stat.mqh>
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Global Variables |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
double account_status_curr_profit; // Profit en flotante - profit in float
bool account_status_positions_open; // Flag indicating if there are open positions in the account
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Class for account management |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
class CAccountStatus : public CManagerBase<CAccountGestor>
{
private:
//---
2025-09-24 14:00:58 -05:00
CTicketsInfo open_positions; // CTicketsInfo type object for efficient ticket management
bool init; // Flag indicating if it has been initialized correctly
2025-09-22 09:09:20 -05:00
//---
2025-09-24 14:00:58 -05:00
double account_daily_profit; // Account profit (adding EAs and user etc, total) daily
double account_weekly_profit; // Account profit (adding EAs and user etc, total) weekly
double account_monthly_profit; // Account profit (adding EAs and user etc, total) monthly
double account_gross_profit; // Account profit (adding EAs and user etc, total) total
2025-09-22 09:09:20 -05:00
//---
2025-09-24 14:00:58 -05:00
datetime last_day_time; // Opening time of the current daily candle
datetime last_weekly_time; // Opening time of the current weekly candle
datetime last_monthly_time; // Opening time of the current monthly candle
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Most recent structure of the opened or closed position
2025-09-22 09:09:20 -05:00
ROnOpenClosePosition last_struct;
2025-09-24 14:00:58 -05:00
//--- Function to update profits
2025-09-22 09:09:20 -05:00
void UpdateProfit(double new_profit, bool flag_init);
public:
CAccountStatus();
//--- Getter
2025-09-24 14:00:58 -05:00
inline Position GetPosition(ulong ticket) { return open_positions.GetByTicket(ticket); } // Get Position by ticket
2025-09-22 09:09:20 -05:00
inline int16_t GetPositionsTotal() const { return (int16_t)this.open_positions.Size(); } // Get total number of open positions
inline bool TheTicketExists(const ulong ticket) const { return open_positions.Exists(ticket); } // Check if ticket exists in positions array
inline double AccountGrossProfit() const { return account_gross_profit; }
inline double AccountDailyProfit() const { return account_daily_profit; }
inline double AccountWeeklyProfit() const { return account_weekly_profit; }
inline double AccountMonthlyProfit() const { return account_monthly_profit; }
inline bool IsInitialized() const { return init; }
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Events
void OnTradeTransactionEvent(const MqlTradeTransaction& trans); // Should be executed in OnTradeTransaction
void OnInitEvent(); // Should be executed in OnInit, at the end
inline void OnNewDay(); // The function should be called every new day
inline void OnNewWeek(); // The function should be called every new week
inline void OnNewMonth(); // The function should be called every new month
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Ticket logs
2025-09-22 09:09:20 -05:00
inline void AddLogFlagTicket(uint8_t flags) { open_positions.AddLogFlags(flags); }
inline void RemoveLogFlagTicket(uint8_t flags) { open_positions.RemoveLogFlags(flags); }
};
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Constructor |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
CAccountStatus::CAccountStatus(void)
: CManagerBase<CAccountGestor>(true), account_daily_profit(0.00), account_weekly_profit(0.00), account_gross_profit(0.00), account_monthly_profit(0.00), init(false)
{
account_status_curr_profit = 0.00;
account_status_positions_open = false;
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function that executes every new day |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
inline void CAccountStatus::OnNewDay(void)
{
this.last_day_time = iTime(_Symbol, PERIOD_D1, 0);
this.account_daily_profit = 0.0;
for(int i = 0; i < total; i++)
items[i].OnNewDay(this.last_day_time);
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function that executes every new month |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
inline void CAccountStatus::OnNewMonth(void)
{
this.last_monthly_time = iTime(_Symbol, PERIOD_MN1, 0);
this.account_monthly_profit = 0.0;
for(int i = 0; i < total; i++)
items[i].OnNewMonth(this.last_monthly_time);
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function that executes weekly |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
inline void CAccountStatus::OnNewWeek(void)
{
this.last_weekly_time = iTime(_Symbol, PERIOD_W1, 0);
this.account_weekly_profit = 0.0;
for(int i = 0; i < total; i++)
items[i].OnNewWeek(this.last_weekly_time);
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function that executes in OnInit |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
void CAccountStatus::OnInitEvent(void)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
//--- Initialize the profit collection variables
2025-09-22 09:09:20 -05:00
this.last_day_time = iTime(_Symbol, PERIOD_D1, 0);
this.last_weekly_time = iTime(_Symbol, PERIOD_W1, 0);
this.last_monthly_time = iTime(_Symbol, PERIOD_MN1, 0);
this.init = true;
2025-09-24 14:00:58 -05:00
//--- Initialize the managers with the new profit
2025-09-22 09:09:20 -05:00
UpdateProfit(0, true);
ROnOpenClosePosition profit;
profit.account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
profit.account_profit_diario = this.account_daily_profit;
profit.account_profit_mensual = this.account_monthly_profit;
profit.account_profit_semanal = this.account_weekly_profit;
profit.account_profit_total = this.account_gross_profit;
for(int i = 0; i < total; i++)
items[i].OnNewProfit(profit);
2025-09-24 14:00:58 -05:00
//--- Check if there are open trades
2025-09-22 09:09:20 -05:00
const int pos_total = PositionsTotal();
2025-09-24 14:00:58 -05:00
//--- If there are open positions we add them
2025-09-22 09:09:20 -05:00
if(pos_total > 0)
{
for(int i = pos_total - 1; i >= 0; i--)
{
ulong position_ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(position_ticket))
continue;
Position new_pos;
new_pos.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
new_pos.ticket = position_ticket;
new_pos.profit = GetTotalPositionProfitNoCurrent(position_ticket);
new_pos.magic = (ulong)PositionGetInteger(POSITION_MAGIC);
new_pos.first_tp = PositionGetDouble(POSITION_TP);
new_pos.firt_sl = PositionGetDouble(POSITION_SL);
new_pos.open_time = (datetime)PositionGetInteger(POSITION_TIME);
new_pos.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
open_positions.Add(new_pos);
//---
profit.position = new_pos;
for(int k = 0; k < total; k++)
items[k].OnInitNewPos(profit);
}
}
}
//+------------------------------------------------------------------+
//| Function to update profits |
//+------------------------------------------------------------------+
void CAccountStatus::UpdateProfit(double new_profit, bool flag_init)
{
if(!flag_init)
{
this.account_gross_profit += new_profit;
this.account_daily_profit += new_profit;
this.account_weekly_profit += new_profit;
this.account_monthly_profit += new_profit;
if(!init)
{
2025-09-24 14:00:58 -05:00
LogFatalError("CAccountStatus.OnInit must be called before executing any CAccountStatus function", FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
Remover();
}
}
else
{
this.account_gross_profit = GetNetProfitSince(true, 0, D'1972.01.01 00:00');
this.account_daily_profit = GetNetProfitSince(true, 0, this.last_day_time);
this.account_weekly_profit = GetNetProfitSince(true, 0, this.last_weekly_time);
this.account_monthly_profit = GetNetProfitSince(true, 0, this.last_monthly_time);
}
if(IsInfoLogEnabled())
{
FastLog(FUNCION_ACTUAL, INFO_TEXT, "New profits:");
Print("Daily profit: ", this.account_daily_profit);
Print("Weekly profit: ", this.account_weekly_profit);
Print("Monthly profit: ", this.account_monthly_profit);
Print("Gross profit: ", this.account_gross_profit);
}
}
//+------------------------------------------------------------------+
//| Function to execute in OnTick |
//+------------------------------------------------------------------+
#define CAccountStatus_OnTickEvent \
if(account_status_positions_open) \
{ \
account_status_curr_profit = AccountInfoDouble(ACCOUNT_EQUITY) - AccountInfoDouble(ACCOUNT_BALANCE); \
}
//+------------------------------------------------------------------+
//| OnTradeTransaction event |
//+------------------------------------------------------------------+
//#define CACCOUNT_STATUS_ACTIVE_ORDER_EVENT
//---
void CAccountStatus::OnTradeTransactionEvent(const MqlTradeTransaction &trans)
{
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
const ENUM_DEAL_TYPE deal_type = trans.deal_type;
if(deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL)
{
HistoryDealSelect(trans.deal);
const ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
const ulong deal_magic = (ulong)HistoryDealGetInteger(trans.deal, DEAL_MAGIC);
const bool is_select = PositionSelectByTicket(trans.position);
//---
const double deal_profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT); // Get deal profit
const double deal_commission = HistoryDealGetDouble(trans.deal, DEAL_COMMISSION); // Get commission
//---
if(is_select && entry == DEAL_ENTRY_IN)
{
//---
LogInfo(StringFormat("Position %I64u opened with magic %I64u", trans.position, deal_magic), FUNCION_ACTUAL);
2025-09-24 14:00:58 -05:00
//--- Activate open positions flag
2025-09-22 09:09:20 -05:00
account_status_positions_open = true;
2025-09-24 14:00:58 -05:00
//--- Calculate the profit
2025-09-22 09:09:20 -05:00
const double deal_net_profit = deal_profit + deal_commission; // Calculate net profit
2025-09-24 14:00:58 -05:00
//--- Create a new position to add to the tickets
2025-09-22 09:09:20 -05:00
Position new_pos;
new_pos.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
new_pos.ticket = trans.position;
new_pos.magic = deal_magic;
new_pos.profit = deal_net_profit;
new_pos.open_time = (datetime)PositionGetInteger(POSITION_TIME);
new_pos.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
new_pos.firt_sl = PositionGetDouble(POSITION_SL);
new_pos.first_tp = PositionGetDouble(POSITION_TP);
//---
2025-09-24 14:00:58 -05:00
UpdateProfit(deal_net_profit, false); //Update the profit
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Add to positions array
2025-09-22 09:09:20 -05:00
open_positions.Add(new_pos);
2025-09-24 14:00:58 -05:00
//--- Set the new structure
2025-09-22 09:09:20 -05:00
last_struct.position = new_pos;
2025-09-24 14:00:58 -05:00
// Account
2025-09-22 09:09:20 -05:00
last_struct.account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
last_struct.account_profit_diario = account_daily_profit;
last_struct.account_profit_total = account_gross_profit;
last_struct.account_profit_mensual = account_monthly_profit;
last_struct.account_profit_semanal = account_weekly_profit;
// Deal
last_struct.deal_entry_type = entry;
last_struct.deal_ticket = trans.deal;
last_struct.deal_profit = deal_net_profit;
last_struct.deal_reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON);
// Extra
last_struct.magic_number_closed = 0;
//---
for(int i = 0; i < total; i++)
items[i].OnOpenClosePosition(last_struct);
//---
return;
}
if(entry != DEAL_ENTRY_OUT)
return;
if(!is_select)
{
2025-09-24 14:00:58 -05:00
Position pos_delete; //Position that was deleted
2025-09-22 09:09:20 -05:00
if(open_positions.DeleteByTicket(trans.position, pos_delete))
{
LogCaution(StringFormat("Position with ticket %I64u has been closed", trans.position), FUNCION_ACTUAL);
2025-09-24 14:00:58 -05:00
//--- Get the most recent profit
2025-09-22 09:09:20 -05:00
const double deal_swap = HistoryDealGetDouble(trans.deal, DEAL_SWAP); // Get swap fees
const double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit
//---
if(GetPositionsTotal() == 0)
account_status_positions_open = false;
2025-09-24 14:00:58 -05:00
//--- Update its profit
2025-09-22 09:09:20 -05:00
pos_delete.profit += deal_net_profit;
2025-09-24 14:00:58 -05:00
//--- Update the general profit
2025-09-22 09:09:20 -05:00
UpdateProfit(deal_net_profit, false);
2025-09-24 14:00:58 -05:00
//--- Set the new structure
2025-09-22 09:09:20 -05:00
last_struct.position = pos_delete;
2025-09-24 14:00:58 -05:00
// Account
2025-09-22 09:09:20 -05:00
last_struct.account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
last_struct.account_profit_diario = account_daily_profit;
last_struct.account_profit_total = account_gross_profit;
last_struct.account_profit_mensual = account_monthly_profit;
last_struct.account_profit_semanal = account_weekly_profit;
// Deal
2025-09-24 14:00:58 -05:00
last_struct.deal_entry_type = entry; //market entry type (ENTRY_OUT)
last_struct.deal_ticket = trans.deal; //Closing deal ticket
last_struct.deal_profit = deal_net_profit; //Net profit (excluding commissions)
2025-09-22 09:09:20 -05:00
last_struct.deal_reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON);
// Extra
2025-09-24 14:00:58 -05:00
last_struct.magic_number_closed = deal_magic; //Ticket with which the position was closed
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Execute in the inheritors
2025-09-22 09:09:20 -05:00
for(int i = 0; i < total; i++)
items[i].OnOpenClosePosition(last_struct);
}
}
else
{
LogCaution(StringFormat("Possible partial closure of the position: %I64u", trans.position), FUNCION_ACTUAL);
//---
const double deal_swap = HistoryDealGetDouble(trans.deal, DEAL_SWAP); // Get swap fees
const double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit
//---
2025-09-24 14:00:58 -05:00
open_positions.UpdateProfit(deal_net_profit, trans.position); //Update the profit of the position that partially closed
2025-09-22 09:09:20 -05:00
UpdateProfit(deal_net_profit, false);
}
}
else
2025-09-24 14:00:58 -05:00
if(deal_type == DEAL_TYPE_BALANCE || deal_type == DEAL_TYPE_CHARGE) // Withdrawal or deposit
2025-09-22 09:09:20 -05:00
{
HistoryDealSelect(trans.deal);
const double deal_profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT); // Get deal profit
if(fabs(deal_profit) < 0.0000000001)
return;
for(int i = 0; i < total; i++)
items[i].OnWithdrawalDeposit(deal_profit);
}
return;
}
#ifdef CACCOUNT_STATUS_ACTIVE_ORDER_EVENT
else
if(trans.type == TRADE_TRANSACTION_ORDER_DELETE)
{
HistoryOrderSelect(trans.order);
ROnOrderDelete new_order;
new_order.order_ticket = trans.order;
new_order.order_type = trans.order_type;
new_order.order_state = trans.order_state;
new_order.order_magic = HistoryOrderGetInteger(trans.order, ORDER_MAGIC);
for(int i = 0; i < total; i++)
items[i].OnOrderDelete(new_order);
return;
}
#endif
}
/*
ADVERTENCIA: Si se a<EFBFBD>aden items a esta clase, esta las eliminara por defecto, NO propagara sus logs.. solko funcioan como container
2025-09-22 12:18:57 -05:00
WARNING: If items are added to this class, it will delete them by default, it will NOT propagate their logs. They only function as containers.
2025-09-22 09:09:20 -05:00
*/
CAccountStatus account_status;
#endif
//+------------------------------------------------------------------+