//+------------------------------------------------------------------+ //| 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 MQLARTICLES_RM_ACCOUNTSTATUS_MQH #define MQLARTICLES_RM_ACCOUNTSTATUS_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "RM_Functions.mqh" #include //+------------------------------------------------------------------+ //| Defines | //+------------------------------------------------------------------+ //--- Events #define ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION (8) #define ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY (16) #define ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD (32) #define ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE (64) #define ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE (128) #define ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT (256) #define ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT (512) #define ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS (1024) #define ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT (2048) //--- All events #define ACCOUNT_STATUS_REG_FLAG_ALL (ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION | \ ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY | \ ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD | \ ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE | \ ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE | \ ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT | \ ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT | \ ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS | \ ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT) //--- #define ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_TP 1 #define ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_SL 2 #define ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_TP_SL (ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_TP|ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_SL) //--- #define ACCOUNT_STATUS_FLAG_FREE (1) #define ACCOUNT_STATUS_FLAG_INIT (2) //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ 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 //+------------------------------------------------------------------+ //| Class for account management | //+------------------------------------------------------------------+ class CAccountStatus : public CAllClassEventsBasic { private: //--- uint8_t m_flags; //--- Event Subscribers Arrays CAccountGestor* m_on_open_close_position[]; int m_on_open_close_position_size; CAccountGestor* m_on_position_modify[]; int m_on_position_modify_size; CAccountGestor* m_on_order_add[]; int m_on_order_add_size; CAccountGestor* m_on_order_update[]; int m_on_order_update_size; CAccountGestor* m_on_order_delete[]; int m_on_order_delete_size; CAccountGestor* m_on_new_profit[]; int m_on_new_profit_size; CAccountGestor* m_on_withdrawal_deposit[]; int m_on_withdrawal_deposit_size; CAccountGestor* m_on_init_new_pos[]; int m_on_init_new_pos_size; CAccountGestor* m_on_loss_profit[]; int m_on_loss_profit_size; //--- CTicketsInfo* open_positions; // CTicketsInfo type object for efficient ticket management //--- 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 //--- ROrder last_order_struct; //--- Most recent structure of the opened or closed position ROnOpenClosePosition last_struct; // Variables that store a property of the last deal (when adding a deal, TRADE_TRANSACTION_DEAL_ADD) ulong last_deal_ticket; // Ticket ENUM_DEAL_TYPE last_deal_type; // Deal type ulong last_deal_magic; // Magic // Variable that stores the last symbol for specific events (TRADE_TRANSACTION_DEAL_ADD, TRADE_TRANSACTION_ORDER_DELETE) string last_trans_symbol; // Symbol //--- Function to update profits void UpdateProfitInit(); void UpdateProfit(const double new_profit); //--- void RemoveFromArrayFast(CAccountGestor* &array[], int &size, CAccountGestor* &ptr); public: CAccountStatus(); ~CAccountStatus(); //--- Tipos (publcios para facil acceso.. recuerda NO modificar) 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 //--- Event Registration Method void RegisterEvents(CAccountGestor* ptr, const int flags); void UnregisterEvents(CAccountGestor* ptr, const int flags); void UnregisterEvent(CAccountGestor* ptr, const int event); void RegisterEvent(CAccountGestor* ptr, const int event); //--- Getter //- Tickets __forceinline Position GetPosition(ulong ticket) { return open_positions.GetByTicket(ticket); } // Get Position by ticket __forceinline int16_t GetPositionsTotal() const { return open_positions.Total(); } // Get total number of open positions __forceinline bool TheTicketExists(const ulong ticket) const { return open_positions.Exists(ticket); } // Check if ticket exists in positions array //- Profits 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; } //- Esta iniciado ? __forceinline bool IsInitialized() const { return (m_flags & ACCOUNT_STATUS_FLAG_INIT) != 0; } //- Aun esta activo?? (util para deinciacion en destrucotr de herederos de CAccountGestor) __forceinline bool IsActive() const { return (m_flags & ACCOUNT_STATUS_FLAG_FREE) == 0; } //- Ultima transaccion inline string LastTransctionSymbol() const { return last_trans_symbol; } __forceinline ROnOpenClosePosition LastStructPosition() const { return last_struct; } __forceinline Position LastPosition() const { return last_struct.position; } //- Deals (solo en DEAL_TRANSACTION_ADD) __forceinline ulong LastDealMagic() const { return last_deal_magic; } __forceinline ulong LastDealTicket() const { return last_deal_ticket; } //--- Events void OnTradeTransactionEvent(const MqlTradeTransaction& trans); // Should be executed in OnTradeTransaction void OnInitEvent(); // Should be executed in OnInit, at the end void OnNewDay(const datetime curr_time) override final; // The function should be called every new day void OnNewWeek(const datetime curr_time) override final; // The function should be called every new week void OnNewMonth(const datetime curr_time) override final; // The function should be called every new month //--- Info functions void PrintLastOrder(); void PrintLastPosition(); //--- void Deinit(const int reason); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CAccountStatus::CAccountStatus(void) : account_daily_profit(0.00), account_weekly_profit(0.00), account_gross_profit(0.00), account_monthly_profit(0.00), m_on_open_close_position_size(0), m_on_position_modify_size(0), m_on_order_add_size(0), m_on_order_update_size(0), m_on_order_delete_size(0), m_on_new_profit_size(0), m_on_withdrawal_deposit_size(0), m_on_init_new_pos_size(0), m_on_loss_profit_size(0), m_flags(0) { //--- account_status_curr_profit = 0.00; account_status_positions_open = false; //--- open_positions = new CTicketsInfo(); AddLogger(open_positions); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CAccountStatus::~CAccountStatus() { //Print("Entrando"); CleanItems("AccountStatus"); // CBasicEvents::Unregister(CAllClassEventsBasic *ptr,const uchar flags) no tiene sentido. dado que es global... // Entonces es eliminar cuando se elimina... asi que dejemos que mql5 haga loq eu tenga que hacer } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CAccountStatus_Deinit(const int reason) { // WRapper para poder pasarlo account_status.Deinit(reason); } //+------------------------------------------------------------------+ void CAccountStatus::Deinit(const int reason) { //--- Limpiamos todos los arrays // SOlo ejecutamos en caso la clase solo tenga la benadera de incilzaicon // En caso tenga otra bandera como free entonces no se ejeucta if((m_flags & ACCOUNT_STATUS_FLAG_INIT) == ACCOUNT_STATUS_FLAG_INIT) { //--- m_on_open_close_position_size = ArrayResize(m_on_open_close_position, 0); m_on_position_modify_size = ArrayResize(m_on_position_modify, 0); m_on_order_add_size = ArrayResize(m_on_order_add, 0); m_on_order_update_size = ArrayResize(m_on_order_update, 0); m_on_order_delete_size = ArrayResize(m_on_order_delete, 0); m_on_new_profit_size = ArrayResize(m_on_new_profit, 0); m_on_withdrawal_deposit_size = ArrayResize(m_on_withdrawal_deposit, 0); m_on_init_new_pos_size = ArrayResize(m_on_init_new_pos, 0); m_on_loss_profit_size = ArrayResize(m_on_loss_profit, 0); //--- m_flags |= ACCOUNT_STATUS_FLAG_FREE; } } //+------------------------------------------------------------------+ //| Registro y De-Registro | //+------------------------------------------------------------------+ void CAccountStatus::RegisterEvents(CAccountGestor* ptr, const int flags) { //--- if(!CheckPointer(ptr)) { LogFatalError("Puntero invalido registrado", FUNCION_ACTUAL); Remover(); return; } //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION) != 0) m_on_open_close_position[ArrayResize(m_on_open_close_position, (++m_on_open_close_position_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY) != 0) m_on_position_modify[ArrayResize(m_on_position_modify, (++m_on_position_modify_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD) != 0) m_on_order_add[ArrayResize(m_on_order_add, (++m_on_order_add_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE) != 0) m_on_order_update[ArrayResize(m_on_order_update, (++m_on_order_update_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE) != 0) m_on_order_delete[ArrayResize(m_on_order_delete, (++m_on_order_delete_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT) != 0) m_on_new_profit[ArrayResize(m_on_new_profit, (++m_on_new_profit_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT) != 0) m_on_withdrawal_deposit[ArrayResize(m_on_withdrawal_deposit, (++m_on_withdrawal_deposit_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS) != 0) m_on_init_new_pos[ArrayResize(m_on_init_new_pos, (++m_on_init_new_pos_size)) - 1] = ptr; //--- if((flags & ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT) != 0) m_on_loss_profit[ArrayResize(m_on_loss_profit, (++m_on_loss_profit_size)) - 1] = ptr; } //+------------------------------------------------------------------+ void CAccountStatus::RegisterEvent(CAccountGestor* ptr, const int event) { //--- if(!CheckPointer(ptr)) { LogFatalError("Puntero invalido registrado", FUNCION_ACTUAL); Remover(); return; } //--- if(event == ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION) m_on_open_close_position[ArrayResize(m_on_open_close_position, (++m_on_open_close_position_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY) m_on_position_modify[ArrayResize(m_on_position_modify, (++m_on_position_modify_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD) m_on_order_add[ArrayResize(m_on_order_add, (++m_on_order_add_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE) m_on_order_update[ArrayResize(m_on_order_update, (++m_on_order_update_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE) m_on_order_delete[ArrayResize(m_on_order_delete, (++m_on_order_delete_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT) m_on_new_profit[ArrayResize(m_on_new_profit, (++m_on_new_profit_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT) m_on_withdrawal_deposit[ArrayResize(m_on_withdrawal_deposit, (++m_on_withdrawal_deposit_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS) m_on_init_new_pos[ArrayResize(m_on_init_new_pos, (++m_on_init_new_pos_size)) - 1] = ptr; //--- else if(event == ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT) m_on_loss_profit[ArrayResize(m_on_loss_profit, (++m_on_loss_profit_size)) - 1] = ptr; } //+------------------------------------------------------------------+ //| De-Suscribirse | //+------------------------------------------------------------------+ void CAccountStatus::UnregisterEvents(CAccountGestor* ptr, const int flags) { if(!CheckPointer(ptr)) { LogError("Invalid pointer for unregister", FUNCION_ACTUAL); return; } //--- OnOpenClosePosition if((flags & ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION) != 0) RemoveFromArrayFast(m_on_open_close_position, m_on_open_close_position_size, ptr); //--- OnPositionModify if((flags & ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY) != 0) RemoveFromArrayFast(m_on_position_modify, m_on_position_modify_size, ptr); //--- OnOrderAdd if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD) != 0) RemoveFromArrayFast(m_on_order_add, m_on_order_add_size, ptr); //--- OnOrderUpdate if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE) != 0) RemoveFromArrayFast(m_on_order_update, m_on_order_update_size, ptr); //--- OnOrderDelete if((flags & ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE) != 0) RemoveFromArrayFast(m_on_order_delete, m_on_order_delete_size, ptr); //--- OnNewProfit if((flags & ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT) != 0) RemoveFromArrayFast(m_on_new_profit, m_on_new_profit_size, ptr); //--- OnWithdrawalDeposit if((flags & ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT) != 0) RemoveFromArrayFast(m_on_withdrawal_deposit, m_on_withdrawal_deposit_size, ptr); //--- OnInitNewPos if((flags & ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS) != 0) RemoveFromArrayFast(m_on_init_new_pos, m_on_init_new_pos_size, ptr); //--- OnLossProfit if((flags & ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT) != 0) RemoveFromArrayFast(m_on_loss_profit, m_on_loss_profit_size, ptr); } //+------------------------------------------------------------------+ void CAccountStatus::UnregisterEvent(CAccountGestor* ptr, const int event) { if(!CheckPointer(ptr)) { LogError("Invalid pointer for unregister", FUNCION_ACTUAL); return; } //--- OnOpenClosePosition if(event == ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION) RemoveFromArrayFast(m_on_open_close_position, m_on_open_close_position_size, ptr); //--- OnPositionModify else if(event == ACCOUNT_STATUS_REG_FLAG_ON_POSITION_MODIFY) RemoveFromArrayFast(m_on_position_modify, m_on_position_modify_size, ptr); //--- OnOrderAdd else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_ADD) RemoveFromArrayFast(m_on_order_add, m_on_order_add_size, ptr); //--- OnOrderUpdate else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_UPDATE) RemoveFromArrayFast(m_on_order_update, m_on_order_update_size, ptr); //--- OnOrderDelete else if(event == ACCOUNT_STATUS_REG_FLAG_ON_ORDER_DELETE) RemoveFromArrayFast(m_on_order_delete, m_on_order_delete_size, ptr); //--- OnNewProfit else if(event == ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT) RemoveFromArrayFast(m_on_new_profit, m_on_new_profit_size, ptr); //--- OnWithdrawalDeposit else if(event == ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT) RemoveFromArrayFast(m_on_withdrawal_deposit, m_on_withdrawal_deposit_size, ptr); //--- OnInitNewPos else if(event == ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS) RemoveFromArrayFast(m_on_init_new_pos, m_on_init_new_pos_size, ptr); //--- OnLossProfit else if(event == ACCOUNT_STATUS_REG_FLAG_ON_LOSS_PROFIT) RemoveFromArrayFast(m_on_loss_profit, m_on_loss_profit_size, ptr); } //+------------------------------------------------------------------+ void CAccountStatus::RemoveFromArrayFast(CAccountGestor* &array[], int &size, CAccountGestor* &ptr) { for(int i = 0; i < size; i++) { if(array[i] == ptr) { array[i] = array[size - 1]; ArrayResize(array, (--size)); //LogInfo(StringFormat("Event subscriber removed (size now: %d)", size), FUNCION_ACTUAL); return; } } //LogWarning("Pointer not found in event array", FUNCION_ACTUAL); } //+------------------------------------------------------------------+ //| Function that executes every new day | //+------------------------------------------------------------------+ void CAccountStatus::OnNewDay(const datetime curr_time) { this.last_day_time = curr_time - (curr_time % 86400); this.account_daily_profit = 0.0; } //+------------------------------------------------------------------+ //| Function that executes every new month | //+------------------------------------------------------------------+ void CAccountStatus::OnNewMonth(const datetime curr_time) { if((m_flags & ACCOUNT_STATUS_FLAG_INIT) == 0) { LogError("Account status no INICIADO", FUNCION_ACTUAL); Remover(); return; } this.last_monthly_time = CTimeUtils::StartOfMonth(curr_time); this.account_monthly_profit = 0.0; } //+------------------------------------------------------------------+ //| Function that executes weekly | //+------------------------------------------------------------------+ void CAccountStatus::OnNewWeek(const datetime curr_time) { this.last_weekly_time = CTimeUtils::StartOfWeek(curr_time, true); this.account_weekly_profit = 0.0; } //+------------------------------------------------------------------+ //| Function that executes in OnInit | //+------------------------------------------------------------------+ void CAccountStatus::OnInitEvent() { //--- if((m_flags & ACCOUNT_STATUS_FLAG_INIT) != 0) { LogWarning("Clase ya iniciada", FUNCION_ACTUAL); return; } //--- const datetime curr_time = TimeCurrent(); //--- Initialize the profit collection variables this.last_day_time = curr_time - (curr_time % 86400); this.last_weekly_time = CTimeUtils::StartOfWeek(curr_time, true); this.last_monthly_time = CTimeUtils::StartOfMonth(curr_time); this.m_flags |= ACCOUNT_STATUS_FLAG_INIT; //--- Registramos para que reciba el evento CBasicEvents::Register(&this, BASICEVENT_REG_ALL_FLAG); //--- Initialize the managers with the new profit UpdateProfitInit(); //--- 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; //--- Notify OnNewProfit subscribers for(int i = 0; i < m_on_new_profit_size; i++) m_on_new_profit[i].OnNewProfit(profit, curr_time); //--- Check if there are open trades const int pos_total = PositionsTotal(); //--- If there are open positions we add them if(pos_total > 0) { for(int i = pos_total - 1; i >= 0; i--) { const ulong position_ticket = PositionGetTicket(i); if(!PositionSelectByTicket(position_ticket)) continue; //--- last_trans_symbol = PositionGetString(POSITION_SYMBOL); //--- static 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.tp = PositionGetDouble(POSITION_TP); new_pos.sl = PositionGetDouble(POSITION_SL); new_pos.open_time = (datetime)PositionGetInteger(POSITION_TIME); new_pos.open_price = PositionGetDouble(POSITION_PRICE_OPEN); new_pos.volume = PositionGetDouble(POSITION_VOLUME); open_positions.Add(new_pos); //--- profit.position = new_pos; //--- Notify OnInitNewPos subscribers for(int k = 0; k < m_on_init_new_pos_size; k++) m_on_init_new_pos[k].OnInitNewPos(profit); } } } //+------------------------------------------------------------------+ //| Function to update profits | //+------------------------------------------------------------------+ void CAccountStatus::UpdateProfit(const double new_profit) { //--- 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(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); } //--- Notify OnLossProfit subscribers for(int i = 0; i < m_on_loss_profit_size; i++) m_on_loss_profit[i].OnLossProfit(new_profit); } //+------------------------------------------------------------------+ void CAccountStatus::UpdateProfitInit() { //--- 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 account_status_curr_profit = AccountInfoDouble(ACCOUNT_EQUITY) - AccountInfoDouble(ACCOUNT_BALANCE); //+------------------------------------------------------------------+ //| OnTradeTransaction event | //+------------------------------------------------------------------+ //--- Activa el evento de OnOrderAdd //#define CACCOUNT_STATUS_ACTIVE_ON_ORDER_ADD //--- Activa el evento de OnOrderUpdate //#define CACCOUNT_STATUS_ACTIVE_ON_ORDER_UPDATE //--- Activa el evento de OnOrderDelete //#define CACCOUNT_STATUS_ACTIVE_ON_ORDER_DELETE //--- Activa el evento de OnPositionModifed //#define CACCOUNT_STATUS_ACTIVE_ON_POSITION_MODIFIED //--- En los evenots de ordenes, su definicion activa que tamiben cuenten las ordenes de compra y venta //#define CACCOUNT_STATUS_COUNT_ORDERS_BUY_SELL //--- void CAccountStatus::OnTradeTransactionEvent(const MqlTradeTransaction &trans) { //--- switch(trans.type) { //--- case TRADE_TRANSACTION_ORDER_ADD: // 0 { #ifdef CACCOUNT_STATUS_ACTIVE_ON_ORDER_ADD #ifndef CACCOUNT_STATUS_COUNT_ORDERS_BUY_SELL if(!EsUnaOrderPendiente[trans.order_type]) return; #endif // CACCOUNT_STATUS_COUNT_ORDERS_BUY_SELL //--- if(OrderSelect(trans.order)) { //--- last_trans_symbol = trans.symbol; //--- last_order_struct.order_open_price = trans.price; last_order_struct.order_stop_loss = trans.price_sl; last_order_struct.order_take_profit = trans.price_tp; last_order_struct.order_magic = OrderGetInteger(ORDER_MAGIC); last_order_struct.order_ticket = trans.order; last_order_struct.order_time_expiration = trans.time_expiration; last_order_struct.order_reason = (ENUM_ORDER_REASON)OrderGetInteger(ORDER_REASON); last_order_struct.order_type = trans.order_type; last_order_struct.order_state = trans.order_state; last_order_struct.order_type_time = trans.time_type; //--- Notify OnOrderAdd subscribers for(int i = 0; i < m_on_order_add_size; i++) m_on_order_add[i].OnOrderAdd(last_order_struct); } else { LogError(StringFormat("Error al seleccionar la orden %I64u", trans.order), FUNCION_ACTUAL); } #endif // CACCOUNT_STATUS_ACTIVE_ON_ORDER_ADD //--- break; } //--- case TRADE_TRANSACTION_ORDER_UPDATE: // 1 { #ifdef CACCOUNT_STATUS_ACTIVE_ON_ORDER_UPDATE #ifndef CACCOUNT_STATUS_COUNT_ORDERS_BUY_SELL if(!EsUnaOrderPendiente[trans.order_type]) return; #endif // CACCOUNT_STATUS_COUNT_ORDERS_BUY_SELL //--- if(OrderSelect(trans.order)) { //--- last_trans_symbol = trans.symbol; //--- last_order_struct.order_open_price = trans.price; last_order_struct.order_stop_loss = trans.price_sl; last_order_struct.order_take_profit = trans.price_tp; last_order_struct.order_magic = OrderGetInteger(ORDER_MAGIC); last_order_struct.order_ticket = trans.order; last_order_struct.order_time_expiration = trans.time_expiration; last_order_struct.order_reason = (ENUM_ORDER_REASON)OrderGetInteger(ORDER_REASON); last_order_struct.order_type = trans.order_type; last_order_struct.order_state = trans.order_state; last_order_struct.order_type_time = trans.time_type; //--- Notify OnOrderUpdate subscribers for(int i = 0; i < m_on_order_update_size; i++) m_on_order_update[i].OnOrderUpdate(last_order_struct); } else { LogError(StringFormat("Error al seleccionar la orden %I64u", trans.order), FUNCION_ACTUAL); } #endif // CACCOUNT_STATUS_ACTIVE_ON_ORDER_UPDATE //--- break; } //--- case TRADE_TRANSACTION_ORDER_DELETE: // 2 { //--- break; } //--- case TRADE_TRANSACTION_HISTORY_ADD: // 3 { #ifdef CACCOUNT_STATUS_ACTIVE_ON_ORDER_DELETE //--- if(EsUnaOrderPendiente[trans.order_type]) { if(HistoryOrderSelect(trans.order)) { // Se llama antes que historyadd //--- last_trans_symbol = trans.symbol; //--- last_order_struct.order_open_price = trans.price; last_order_struct.order_stop_loss = trans.price_sl; last_order_struct.order_take_profit = trans.price_tp; last_order_struct.order_magic = HistoryOrderGetInteger(trans.order, ORDER_MAGIC); last_order_struct.order_ticket = trans.order; last_order_struct.order_time_expiration = trans.time_expiration; last_order_struct.order_reason = (ENUM_ORDER_REASON)HistoryOrderGetInteger(trans.order, ORDER_REASON); last_order_struct.order_type = trans.order_type; last_order_struct.order_state = trans.order_state; last_order_struct.order_type_time = trans.time_type; //--- Notify OnOrderDelete subscribers for(int i = 0; i < m_on_order_delete_size; i++) m_on_order_delete[i].OnOrderDelete(last_order_struct); } else { LogError(StringFormat("Error al seleccionar la orden %I64u", trans.order), FUNCION_ACTUAL); } } #endif // CACCOUNT_STATUS_ACTIVE_ON_ORDER_DELETE break; } //--- case TRADE_TRANSACTION_HISTORY_UPDATE: // 4 { break; } //--- case TRADE_TRANSACTION_HISTORY_DELETE: // 5 { break; } //--- case TRADE_TRANSACTION_DEAL_ADD: // 6 { //--- last_deal_type = trans.deal_type; last_deal_ticket = trans.deal; last_trans_symbol = trans.symbol; //--- if(last_deal_type == DEAL_TYPE_BUY || last_deal_type == DEAL_TYPE_SELL) { HistoryDealSelect(trans.deal); const ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(last_deal_ticket, DEAL_ENTRY); last_deal_magic = (ulong)HistoryDealGetInteger(last_deal_ticket, DEAL_MAGIC); const ulong position_ticket = trans.position; //--- const double deal_profit = HistoryDealGetDouble(last_deal_ticket, DEAL_PROFIT); // Get deal profit const double deal_commission = HistoryDealGetDouble(last_deal_ticket, DEAL_COMMISSION); // Get commission //--- if(PositionSelectByTicket(trans.position) && entry == DEAL_ENTRY_IN) { //--- LogInfo(StringFormat("Position %I64u opened with magic %I64u", position_ticket, last_deal_magic), FUNCION_ACTUAL); //--- Activate open positions flag account_status_positions_open = true; //--- Calculate the profit const double deal_net_profit = deal_profit + deal_commission; // Calculate net profit //--- Create a new position to add to the tickets static Position new_pos; new_pos.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); new_pos.ticket = position_ticket; new_pos.magic = last_deal_magic; new_pos.profit = deal_net_profit; new_pos.open_time = (datetime)PositionGetInteger(POSITION_TIME); new_pos.open_price = trans.price; new_pos.sl = trans.price_sl; new_pos.tp = trans.price_tp; new_pos.volume = trans.volume; //--- UpdateProfit(deal_net_profit); //Update the profit //--- Add to positions array open_positions.Add(new_pos); //--- Set the new structure last_struct.position = new_pos; // Account 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 = last_deal_ticket; last_struct.deal_profit = deal_net_profit; last_struct.deal_reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(last_deal_ticket, DEAL_REASON); // Extra last_struct.magic_number_closed = 0; //--- Notify OnOpenClosePosition subscribers for(int i = 0; i < m_on_open_close_position_size; i++) m_on_open_close_position[i].OnOpenClosePosition(last_struct); //--- return; } if(entry != DEAL_ENTRY_OUT) break; //--- #define ACCOUNT_STATUS_MIN_VOL 0.00000001 //--- int16_t pos_idx; if(open_positions.GetByTicket(position_ticket, last_struct.position, pos_idx)) { // Print("Vol: ", DoubleToString(trans.volume)); //--- if((last_struct.position.volume - trans.volume) < ACCOUNT_STATUS_MIN_VOL) // Cierre completo { //--- open_positions.DeleteByIndex(pos_idx); //--- LogCaution(StringFormat("Position with ticket %I64u has been closed", position_ticket), FUNCION_ACTUAL); //--- Get the most recent profit const double deal_swap = HistoryDealGetDouble(last_deal_ticket, 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; //--- Update its profit last_struct.position.profit += deal_net_profit; //--- Update the general profit UpdateProfit(deal_net_profit); //--- Set the new structure // Ya se hace // Account 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; //market entry type (ENTRY_OUT) last_struct.deal_ticket = last_deal_ticket; //Closing deal ticket last_struct.deal_profit = deal_net_profit; //Net profit (excluding commissions) last_struct.deal_reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(last_deal_ticket, DEAL_REASON); // Extra last_struct.magic_number_closed = last_deal_magic; //Ticket with which the position was closed //--- Notify OnOpenClosePosition subscribers for(int i = 0; i < m_on_open_close_position_size; i++) m_on_open_close_position[i].OnOpenClosePosition(last_struct); //--- break; } else { //--- LogCaution(StringFormat("Possible partial closure of the position: %I64u", position_ticket), FUNCION_ACTUAL); //--- const double deal_swap = HistoryDealGetDouble(last_deal_ticket, DEAL_SWAP); // Get swap fees const double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit //--- open_positions.UpdatePositionPartial(deal_net_profit, trans.volume, pos_idx); //Update the profit of the position that partially closed //--- UpdateProfit(deal_net_profit); //--- break; } //--- break; } } else if(last_deal_type == DEAL_TYPE_BALANCE || last_deal_type == DEAL_TYPE_CHARGE) // Withdrawal or deposit { HistoryDealSelect(last_deal_ticket); const double deal_profit = HistoryDealGetDouble(last_deal_ticket, DEAL_PROFIT); // Get deal profit //--- if(fabs(deal_profit) < 0.0000000001) break; //--- Notify OnWithdrawalDeposit subscribers for(int i = 0; i < m_on_withdrawal_deposit_size; i++) m_on_withdrawal_deposit[i].OnWithdrawalDeposit(deal_profit); //--- break; } //--- break; } //--- case TRADE_TRANSACTION_DEAL_UPDATE: // 7 { break; } //--- case TRADE_TRANSACTION_DEAL_DELETE: // 8 { break; } //--- case TRADE_TRANSACTION_POSITION: // 9 { #ifdef CACCOUNT_STATUS_ACTIVE_ON_POSITION_MODIFIED const ulong t = trans.position; if(open_positions.GetByTicket(t, last_struct.position)) { //--- last_trans_symbol = trans.symbol; //--- uint8_t f = 0; //--- Cambio tp const double new_tp = trans.price_tp; if(last_struct.position.tp != new_tp) { open_positions.UpdateTp(new_tp, t); last_struct.position.tp = new_tp; f |= ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_TP; } //--- Cambio sl const double new_sl = trans.price_sl; if(last_struct.position.sl != new_sl) { open_positions.UpdateSl(new_sl, t); last_struct.position.sl = new_sl; f |= ACCOUNTSTATUS_ON_POS_MODIFY_FLAG_CHANGE_SL; } //--- Notify OnPositionModify subscribers if(f) for(int i = 0; i < m_on_position_modify_size; i++) m_on_position_modify[i].OnPositionModify(last_struct.position, f); } #endif // CACCOUNT_STATUS_ACTIVE_ON_POSITION_MODIFIED break; } //--- case TRADE_TRANSACTION_REQUEST: // 10 { break; } } } //+------------------------------------------------------------------+ //| Print last order values | //+------------------------------------------------------------------+ void CAccountStatus::PrintLastOrder(void) { Print("--- Last Order ---"); Print("Ticket: ", last_order_struct.order_ticket); Print("Type: ", EnumToString(last_order_struct.order_type)); Print("State: ", EnumToString(last_order_struct.order_state)); Print("Magic: ", last_order_struct.order_magic); Print("Open Price: ", last_order_struct.order_open_price); Print("Stop Loss: ", last_order_struct.order_stop_loss); Print("Take Profit: ", last_order_struct.order_take_profit); Print("Expiration: ", last_order_struct.order_time_expiration); Print("Time Type: ", EnumToString(last_order_struct.order_type_time)); Print("Reason: ", EnumToString(last_order_struct.order_reason)); } //+------------------------------------------------------------------+ //| Print last position values | //+------------------------------------------------------------------+ void CAccountStatus::PrintLastPosition(void) { Print("--- Last Position ---"); Print("Ticket: ", last_struct.position.ticket); Print("Type: ", EnumToString(last_struct.position.type)); Print("Magic: ", last_struct.position.magic); Print("Open Price: ", last_struct.position.open_price); Print("Open Time: ", last_struct.position.open_time); Print("Stop Loss: ", last_struct.position.sl); Print("Take Profit: ", last_struct.position.tp); Print("Profit: ", last_struct.position.profit); } // Nota importante: // En versiones antiguas los obejtos agregados (regisrados) SE ELIMINABAN de la clase.. ahora YA NO // La clase ya no gestiona los LOGS NI elimina los obejtos agregados a ella, solo da los eventos CAccountStatus account_status; #endif // MQLARTICLES_RM_ACCOUNTSTATUS_MQH //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //+------------------------------------------------------------------+