//+------------------------------------------------------------------+ //| RMt_Functions.mqh | //| Niquel y Leo, Copyright 2025 | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Niquel y Leo, Copyright 2025" #property link "https://www.mql5.com" #property strict #ifndef MQLARTICLES_RM_RFUNCTIONS_MQH #define MQLARTICLES_RM_RFUNCTIONS_MQH #include "LoteSizeCalc.mqh" //+-------------------------------------------------------------------------------------------------+ //+----------------------------------- Functions -------------------------------------+ //+-------------------------------------------------------------------------------------------------+ //+------------------------------------------------------------------+ double GetPositionCommission(ulong ticket) { // Verificar si la posición existe y obtener su tiempo de apertura if(!PositionSelectByTicket(ticket)) { Print("Error: Posición con ticket ", ticket, " no encontrada"); return 0.0; } datetime open_time = (datetime)PositionGetInteger(POSITION_TIME); // Usar un rango de tiempo estrecho: desde el tiempo de apertura hasta 1 segundo después if(!HistorySelect(open_time, open_time + 1)) { Print("Error: No se pudo cargar el historial de deals para el ticket ", ticket); return 0.0; } // Obtener el primer deal del historial ulong deal_ticket = HistoryDealGetTicket(0); if(deal_ticket == 0) { Print("Error: No se encontró un deal para el ticket ", ticket); return 0.0; } // Verificar si el deal corresponde a la posición y es de entrada if(HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID) == ticket) { ENUM_DEAL_TYPE deal_type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(deal_ticket, DEAL_TYPE); if(deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL) { return HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); } } Print("Error: No se encontró un deal de apertura válido para el ticket ", ticket); return 0.0; } //+------------------------------------------------------------------+ inline CGetLote* CreateLotePtr(string symbol) { CGetLote* l = new CGetLote(symbol); return l; } //+------------------------------------------------------------------+ double GetTotalPositionProfitNoCurrent(ulong position_ticket) { double total_profit = 0.0; //--- if(HistorySelectByPosition(position_ticket)) { int deals_count = HistoryDealsTotal(); for(int i = 0; i < deals_count; i++) { ulong deal_ticket = HistoryDealGetTicket(i); if(deal_ticket <= 0) continue; ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); if(entry == DEAL_ENTRY_OUT || entry == DEAL_ENTRY_IN) { total_profit += HistoryDealGetDouble(deal_ticket, DEAL_PROFIT) + HistoryDealGetDouble(deal_ticket, DEAL_SWAP) + HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); } } } return total_profit; } //+------------------------------------------------------------------+ //| Retrieve the magic number associated with a ticket | //+------------------------------------------------------------------+ inline ulong GetMagic(const ulong ticket) { HistoryOrderSelect(ticket); return HistoryOrderGetInteger(ticket, ORDER_MAGIC); } //+----------------------------------------------------------------------------------------+ //| Calculates the net profit since a given date for a specific magic number or all trades | //+----------------------------------------------------------------------------------------+ const bool GetNetProfitOmitirDeal[18] = { false, //compra false, //venta true, //balance true, //credito true, //carga adicionales false, //correcion true, //bonos false, //comisiones adicional false, //comisions del dia false, //comision del mes false, //comosions agente dia false, //comosiion agenet mes, false, //interes false, //compra cancelada false, //ventan cancelada false, //dividendo false, //dividendo frankeado, con beneficions false //impuestos }; //--- double GetNetProfitSince(bool include_all_magic, ulong specific_magic, datetime start_date) { double total_net_profit = 0.0; // Initialize the total net profit ResetLastError(); // Reset any previous errors //--- if(start_date > 0 && start_date != D'1971.01.01 00:00') { if(!HistorySelect(start_date, TimeCurrent())) { Print("Error when selecting orders: ", _LastError); return 0.00; // Exit if unable to select the history } const int total_deals = HistoryDealsTotal(); // Count total deals in the history for(int i = 0; i < total_deals; i++) { const ulong deal_ticket = HistoryDealGetTicket(i); // Get the deal ticket if(GetNetProfitOmitirDeal[HistoryDealGetInteger(deal_ticket, DEAL_TYPE)]) continue; //--- const ulong deal_magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC); if(!include_all_magic && deal_magic != specific_magic) continue; //--- const double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); const double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); const double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); total_net_profit += (deal_profit + deal_commission + deal_swap); } } //--- return total_net_profit; } //+------------------------------------------------------------------+ //| Function to close orders | //+------------------------------------------------------------------+ // Converts an order type to its corresponding flag #define OrderTypeToFlag(type) OrdensToFlagArray[type] const int ALL_FLAGS_STOPS = FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP; const int ALL_FLAGS_LIMITS = FLAG_ORDER_TYPE_BUY_LIMIT | FLAG_ORDER_TYPE_SELL_LIMIT; const int ALL_FLAGS_ORDERS = (FLAG_ORDER_TYPE_BUY | FLAG_ORDER_TYPE_SELL | FLAG_ORDER_TYPE_BUY_LIMIT | FLAG_ORDER_TYPE_SELL_LIMIT | FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP | FLAG_ORDER_TYPE_BUY_STOP_LIMIT | FLAG_ORDER_TYPE_SELL_STOP_LIMIT | FLAG_ORDER_TYPE_CLOSE_BY); // Close all orders that match the flags in `flags` void CloseAllOrders(int flags, CTrade & obj_trade, ulong magic_number_ = NOT_MAGIC_NUMBER) { ResetLastError(); for(int i = OrdersTotal() - 1; i >= 0; i--) { ulong ticket = OrderGetTicket(i); if(OrderSelect(ticket)) { ENUM_ORDER_TYPE type_order = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); ulong magic = OrderGetInteger(ORDER_MAGIC); int bandera = OrderTypeToFlag(type_order); if((bandera & flags) != 0 && (magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER)) { if(type_order == ORDER_TYPE_BUY || type_order == ORDER_TYPE_SELL) obj_trade.PositionClose(ticket); else obj_trade.OrderDelete(ticket); } } else { PrintFormat("Error selecting order %d, last error %d", ticket, GetLastError()); } } } //+------------------------------------------------------------------+ //| Function to obtain the positions opened by the EA or user | //+------------------------------------------------------------------+ int PositionTypeToFlag(ENUM_POSITION_TYPE type) { if(type == POSITION_TYPE_BUY) return FLAG_POSITION_TYPE_BUY; else if(type == POSITION_TYPE_SELL) return FLAG_POSITION_TYPE_SELL; return FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL; } //--- int Get_Positions(int flags = FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL, ulong magic_number_ = NOT_MAGIC_NUMBER) { int counter = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong position_ticket = PositionGetTicket(i); if(!PositionSelectByTicket(position_ticket)) continue; // Si la selección falla, pasa a la siguiente posición ulong position_magic = PositionGetInteger(POSITION_MAGIC); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // Check if the position type matches the flags if((flags & PositionTypeToFlag(type)) != 0 && (position_magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER)) { counter++; } } return counter; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool RemoveIndexFromAnArrayOfPositions(T & array[], const ulong ticket, int reserve) { const int size = ArraySize(array); int index = -1; // Search index and move elements in a single loop for(int i = 0; i < size; i++) { if(array[i].ticket == ticket) { index = i; } if(index != -1 && i < size - 1) { array[i] = array[i + 1]; // Move the elements } } if(index == -1) return false; // Reducir el tamaño del array ArrayResize(array, size - 1, reserve); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void PrintArrayAsTable(double & array[], string fila_descripcion, string columna_prefijo = "Valor") { string header = fila_descripcion; int len = StringLen(header); string values = StringRepeat(" ", len + 2); int max_len = StringLen(header); for(int i = 0; i < ArraySize(array); i++) { string col_name = columna_prefijo + " " + IntegerToString(i + 1); max_len = MathMax(max_len, StringLen(col_name)); } header = StringFormat("%-" + (string)(max_len + 2) + "s", header); for(int i = 0; i < ArraySize(array); i++) { string col_name = columna_prefijo + " " + IntegerToString(i + 1); header += StringFormat("| %-10s ", col_name); values += StringFormat("| %-10.2f ", array[i]); } Print(header); Print(values); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SetDynamicUsingFixedParameters( double _balance_percentage_to_activate_the_risk_1, double _balance_percentage_to_activate_the_risk_2, double _balance_percentage_to_activate_the_risk_3, double _balance_percentage_to_activate_the_risk_4, double _percentage_to_be_modified_1, double _percentage_to_be_modified_2, double _percentage_to_be_modified_3, double _percentage_to_be_modified_4, string & percentages_to_activate, string & risks_to_be_applied) { percentages_to_activate = DoubleToString(_balance_percentage_to_activate_the_risk_1) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_2) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_3) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_4); risks_to_be_applied = DoubleToString(_percentage_to_be_modified_1) + "," + DoubleToString(_percentage_to_be_modified_2) + "," + DoubleToString(_percentage_to_be_modified_3) + "," + DoubleToString(_percentage_to_be_modified_4); } //+------------------------------------------------------------------+ //| Clase CTicketsInfo | //+------------------------------------------------------------------+ Position EMPTY_POSITION; //--- class CTicketsInfo : public CLoggerBase { private: //--- Data Position m_positions[]; //--- Hash uint16_t m_hash_table[]; bool m_hash_filled[]; uint16_t m_hash_size; uint16_t m_hash_count; //--- Static static const float LOAD_FACTOR_LIMIT; static const uint16_t INITIAL_HASH_SIZE; //--- Internal functions int16_t FindHash(const ulong ticket) const; bool InsertHash(ulong ticket, uint16_t pos_idx); bool RemoveHash(ulong ticket); void UpdateHashAfterSwap(ulong ticket, uint16_t new_pos_idx); void ResizeHashTable(); bool IsPrime(uint16_t n); public: CTicketsInfo(void); ~CTicketsInfo(void); //--- Data // Get bool GetByTicket(const ulong ticket, Position& pos, int16_t& out_idx) const; bool GetByTicket(const ulong ticket, Position& pos) const; inline Position GetByTicket(const ulong ticket) const; inline Position GetByIndex(const int16_t index) const { return m_positions[index]; } // Delete bool DeleteByIndex(const int16_t pos_idx); bool DeleteByTicket(const ulong ticket); bool DeleteByTicket(const ulong ticket, Position &position); // Add bool Add(Position &p); // Update inline void UpdatePositionPartial(double net, double close_volume, int16_t pos_idx); inline void UpdateTp(double new_tp, ulong ticket); inline void UpdateSl(double new_sl, ulong ticket); //--- General Getters // Index inline bool IsValidIdx(int16_t index) const; // Is Valid index __forceinline int GetIndex(const ulong ticket) const { return FindHash(ticket);} // Total __forceinline int16_t Total() const { return (int16_t)ArraySize(m_positions); } __forceinline uint16_t TotalU() const { return (uint16_t)m_positions.Size(); } //--- Exist inline bool Exists(const ulong ticket) const; //--- Free void Free(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ const float CTicketsInfo::LOAD_FACTOR_LIMIT = 0.7f; const uint16_t CTicketsInfo::INITIAL_HASH_SIZE = 9973; #define CTicketsInfo_HashTicket(ticket) uint16_t(ticket % m_hash_size) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTicketsInfo::CTicketsInfo(void) : m_hash_size(INITIAL_HASH_SIZE), m_hash_count(0) { ArrayResize(m_hash_table, m_hash_size); ArrayResize(m_hash_filled, m_hash_size); ArrayInitialize(m_hash_table, 65535); ArrayInitialize(m_hash_filled, false); } //+------------------------------------------------------------------+ CTicketsInfo::~CTicketsInfo(void) { ArrayFree(m_positions); ArrayFree(m_hash_table); ArrayFree(m_hash_filled); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline bool CTicketsInfo::IsValidIdx(int16_t index) const { if(index >= 0 && index < Total()) return true; LogError(StringFormat("El índice %d es inválido", index), FUNCION_ACTUAL); return false; } //+------------------------------------------------------------------+ int16_t CTicketsInfo::FindHash(const ulong ticket) const { //--- const uint16_t h = (uint16_t)(ticket % m_hash_size); const uint16_t data_size = TotalU(); //--- for(uint16_t i = 0; i < m_hash_size; i++) { //--- const uint16_t idx = (h + i) % m_hash_size; //--- if(!m_hash_filled[idx]) { LogCaution(StringFormat("El slot del ticket %I64u esta vacio", ticket), FUNCION_ACTUAL); return -1; } //--- const uint16_t pos_idx = m_hash_table[idx]; if(pos_idx != 65535 && pos_idx < data_size && m_positions[pos_idx].ticket == ticket) { LogInfo(StringFormat("Ticket %I64u encontrado en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL); return (int16_t)pos_idx; } //--- if(pos_idx >= data_size && pos_idx != 65535) { LogError(StringFormat("Indice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx), FUNCION_ACTUAL); return -1; } } //--- LogWarning(StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket), FUNCION_ACTUAL); return -1; } //+------------------------------------------------------------------+ bool CTicketsInfo::InsertHash(ulong ticket, uint16_t pos_idx) { //--- if((float)(m_hash_count + 1) / m_hash_size > LOAD_FACTOR_LIMIT) ResizeHashTable(); //--- const uint16_t h = CTicketsInfo_HashTicket(ticket); for(uint16_t i = 0; i < m_hash_size; i++) { const uint16_t idx = (h + i) % m_hash_size; if(!m_hash_filled[idx]) { m_hash_table[idx] = pos_idx; m_hash_filled[idx] = true; m_hash_count++; LogInfo(StringFormat("Ticket %I64u insertado en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL); return true; } } //--- LogError(StringFormat("Tabla hash llena para ticket %I64u", ticket), FUNCION_ACTUAL); return false; } //+------------------------------------------------------------------+ bool CTicketsInfo::RemoveHash(ulong ticket) { //--- const uint16_t h = CTicketsInfo_HashTicket(ticket); const uint16_t data_size = TotalU(); //--- for(uint16_t i = 0; i < m_hash_size; i++) { //--- const uint16_t idx = (h + i) % m_hash_size; if(!m_hash_filled[idx]) { LogCaution(StringFormat("El ticket %I64u no existe (casilla vacía en idx=%u)", ticket, idx), FUNCION_ACTUAL); return false; } //--- const uint16_t pos_idx = m_hash_table[idx]; if(pos_idx != 65535 && pos_idx < data_size && m_positions[pos_idx].ticket == ticket) { LogInfo(StringFormat("Se eliminó exitosamente el ticket %I64u en idx=%u, pos_idx=%u", ticket, idx, pos_idx), FUNCION_ACTUAL); m_hash_filled[idx] = false; m_hash_table[idx] = 65535; m_hash_count--; return true; } //--- if(pos_idx >= data_size && pos_idx != 65535) { LogError(StringFormat("Índice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx), FUNCION_ACTUAL); return false; } } //--- LogWarning(StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket), FUNCION_ACTUAL); return false; } //+------------------------------------------------------------------+ void CTicketsInfo::UpdateHashAfterSwap(ulong ticket, uint16_t new_pos_idx) { //--- const uint16_t h = CTicketsInfo_HashTicket(ticket); const uint16_t data_size = TotalU(); //--- for(uint16_t i = 0; i < m_hash_size; i++) { //--- const uint16_t idx = (h + i) % m_hash_size; if(!m_hash_filled[idx]) return; //--- const uint16_t pos_idx = m_hash_table[idx]; if(pos_idx < data_size && m_positions[new_pos_idx].ticket == ticket) { m_hash_table[idx] = new_pos_idx; return; } } } //+------------------------------------------------------------------+ void CTicketsInfo::ResizeHashTable() { //--- LogInfo("Se está aumentando el tamaño de la tabla hash al doble", FUNCION_ACTUAL); //--- uint16_t new_size = m_hash_size * 2 + 1; while(!IsPrime(new_size)) new_size += 2; //--- uint16_t old_hash_table[]; bool old_hash_filled[]; ArrayCopy(old_hash_table, m_hash_table); ArrayCopy(old_hash_filled, m_hash_filled); //--- m_hash_size = new_size; m_hash_count = 0; ArrayResize(m_hash_table, m_hash_size); ArrayResize(m_hash_filled, m_hash_size); ArrayInitialize(m_hash_table, 65535); ArrayInitialize(m_hash_filled, false); //--- for(uint16_t i = 0; i < ArraySize(old_hash_table); i++) { if(old_hash_filled[i] && old_hash_table[i] != 65535) { uint16_t pos_idx = old_hash_table[i]; if(pos_idx < ArraySize(m_positions)) InsertHash(m_positions[pos_idx].ticket, pos_idx); } } //--- ArrayFree(old_hash_table); ArrayFree(old_hash_filled); } //+------------------------------------------------------------------+ bool CTicketsInfo::IsPrime(uint16_t n) { if(n < 2) return false; for(uint16_t i = 2; i * i <= n; i++) if(n % i == 0) return false; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTicketsInfo::GetByTicket(const ulong ticket, Position &pos, int16_t &out_idx) const { out_idx = FindHash(ticket); if(out_idx >= 0) { pos = m_positions[out_idx]; return true; } else return false; } //+------------------------------------------------------------------+ bool CTicketsInfo::GetByTicket(const ulong ticket, Position &pos) const { const int16_t pos_idx = FindHash(ticket); if(pos_idx >= 0) { pos = m_positions[pos_idx]; return true; } else return false; } //+------------------------------------------------------------------+ inline Position CTicketsInfo::GetByTicket(const ulong ticket) const { const int16_t pos_idx = FindHash(ticket); return pos_idx >= 0 ? m_positions[pos_idx] : EMPTY_POSITION; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CTicketsInfo::UpdateSl(double new_sl, ulong ticket) { const int16_t pos_idx = FindHash(ticket); if(pos_idx >= 0) m_positions[pos_idx].sl = new_sl; } //+------------------------------------------------------------------+ inline void CTicketsInfo::UpdateTp(double new_tp, ulong ticket) { const int16_t pos_idx = FindHash(ticket); if(pos_idx >= 0) m_positions[pos_idx].tp = new_tp; } //+------------------------------------------------------------------+ inline void CTicketsInfo::UpdatePositionPartial(double net, double close_volume, int16_t pos_idx) { if(pos_idx >= 0) { m_positions[pos_idx].volume -= close_volume; m_positions[pos_idx].profit += net; LogInfo(StringFormat("Volumen cerrado = %.4f (new= %.4f) y profit obtenido = %.2f, al cierra parcial de la posicion = %I64u", close_volume, m_positions[pos_idx].volume, net, m_positions[pos_idx].ticket), FUNCION_ACTUAL); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTicketsInfo::Add(Position &p) { //--- if(FindHash(p.ticket) >= 0) { LogCaution(StringFormat("La posición %I64u ya existe", p.ticket), FUNCION_ACTUAL); return false; } //--- const uint16_t new_pos_idx = (uint16_t)ArraySize(m_positions); ArrayResize(m_positions, new_pos_idx + 1); m_positions[new_pos_idx] = p; //--- if(!InsertHash(p.ticket, new_pos_idx)) { ArrayResize(m_positions, new_pos_idx); LogError(StringFormat("No se pudo insertar el ticket %I64u en la tabla hash", p.ticket), FUNCION_ACTUAL); return false; } //--- // LogInfo(StringFormat("Añadiendo el ticket %I64u al array de posiciones >> tipo: %s >> profit: %+.2f", p.ticket, EnumToString(p.type), p.profit), FUNCION_ACTUAL); return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTicketsInfo::DeleteByIndex(const int16_t pos_idx) { //--- if(pos_idx < 0) { LogError(StringFormat("Indice invalido %d", pos_idx), FUNCION_ACTUAL); return false; } //--- const ulong ticket = m_positions[pos_idx].ticket; if(!RemoveHash(ticket)) { LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL); return false; } //--- const int16_t last_idx = Total() - 1; if(pos_idx != last_idx) { m_positions[pos_idx] = m_positions[last_idx]; UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx); } //--- ArrayResize(m_positions, last_idx); return true; } //+------------------------------------------------------------------+ bool CTicketsInfo::DeleteByTicket(const ulong ticket) { //--- const int16_t pos_idx = FindHash(ticket); if(pos_idx < 0) { LogError(StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket), FUNCION_ACTUAL); return false; } //--- if(!RemoveHash(ticket)) { LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL); return false; } //--- const int16_t last_idx = Total() - 1; if(pos_idx != last_idx) { m_positions[pos_idx] = m_positions[last_idx]; UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx); } //--- ArrayResize(m_positions, last_idx); return true; } //+------------------------------------------------------------------+ bool CTicketsInfo::DeleteByTicket(const ulong ticket, Position &position) { //--- const int16_t pos_idx = FindHash(ticket); if(pos_idx < 0) { LogError(StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket), FUNCION_ACTUAL); return false; } //--- position = m_positions[pos_idx]; //--- if(!RemoveHash(ticket)) { LogError(StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket), FUNCION_ACTUAL); return false; } //--- const int16_t last_idx = Total() - 1; if(pos_idx != last_idx) { m_positions[pos_idx] = m_positions[last_idx]; UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx); } //--- ArrayResize(m_positions, last_idx); return true; } //+------------------------------------------------------------------+ void CTicketsInfo::Free(void) { ArrayFree(m_positions); m_hash_count = 0; ArrayInitialize(m_hash_table, 65535); ArrayInitialize(m_hash_filled, false); } //+------------------------------------------------------------------+ inline bool CTicketsInfo::Exists(const ulong ticket) const { return FindHash(ticket) >= 0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CAccountGestor : public CSpecializedManager { public: //--- Positions virtual void OnOpenClosePosition(const ROnOpenClosePosition &pos) = 0; //Function that will be executed each time a position is opened or closed virtual void OnPositionModify(const Position& pos, const uint8_t change_flags) { } //--- Orders virtual void OnOrderAdd(const ROrder& order) { } virtual void OnOrderUpdate(const ROrder& order) { } virtual void OnOrderDelete(const ROrder& order) { } //-- Function that is executed only once, where only the account profit fields are filled, such as account_gross_profit //daily, weekly, etc. virtual void OnNewProfit(const ROnOpenClosePosition &profit) { } //--- Function that is executed each time TesterDeposit or TesterWithdrawal is called... or capital is added to the account virtual void OnWithdrawalDeposit(double value) { } //If the value is positive it means a deposit, otherwise a withdrawal //-- Function that is executed every new day virtual void OnNewDay(datetime init_time) { } //-- Function that will be executed every new week virtual void OnNewWeek(datetime init_time) { } //-- Function that is executed every new month virtual void OnNewMonth(datetime init_time) { } //--- Function that is executed only once, only if there are previously open trades, only the position structure virtual void OnInitNewPos(const ROnOpenClosePosition &position) { } //--- virtual void OnLossProfit(const double& p) {} }; //+------------------------------------------------------------------+ #endif // MQLARTICLES_RM_RFUNCTIONS_MQH