//+------------------------------------------------------------------+ //| TicketsTable.mqh | //| Copyright 2026, Niquel Mendoza | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza" #property link "https://www.mql5.com" #property strict #ifndef MQLARTICLES_RM_TICKETS_TABLE_MQH #define MQLARTICLES_RM_TICKETS_TABLE_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include #include #include "RM_Functions.mqh" //+------------------------------------------------------------------+ //| Config | //+------------------------------------------------------------------+ #ifndef MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS #define MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS 64 #endif // MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS //+------------------------------------------------------------------+ #ifndef MQLARTICLES_RM_ACCOUNT_FREELIST_SIZE #define MQLARTICLES_RM_ACCOUNT_FREELIST_SIZE 32 #endif // MQLARTICLES_RM_ACCOUNT_FREELIST_SIZE //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ Position EMPTY_POSITION; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // Heredamos del main class CTicketsTable : public CHashMapFastBase { public: int m_last_created_position_pos; private: Position m_positions[MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS]; int m_next_fresh; int m_free_list[MQLARTICLES_RM_ACCOUNT_FREELIST_SIZE]; int m_free_list_s; CHashMapFastIteratorP(ulong, int, _fibbo) m_it; public: CTicketsTable(void); ~CTicketsTable(void) {} //--- bool GetByTicket(const ulong ticket, Position & pos, int& out_idx); bool GetByTicket(const ulong ticket, Position & pos); inline Position GetByTicket(const ulong ticket); //--- __forceinline Position GetPositionByIndex(const int index) const { return m_positions[index]; } __forceinline ulong GetPositionMagicByIndex(const int index) const { return m_positions[index].magic; } //--- __forceinline void DeleteByIndex(const int pos_idx); bool DeleteByTicket(const ulong ticket); bool DeleteByTicket(const ulong ticket, Position & position); //--- bool Add(const Position & pos); //--- inline void UpdatePositionPartial(const double net, const double close_volume, const int pos_idx); inline void UpdateTp(const double new_tp, const ulong ticket); inline void UpdateSl(const double new_sl, const ulong ticket); //--- void CloseAllPositionMask(const int& index_arr[], const int size_arr, CTrade & trade, const uint flags) const; int GetPositionsMask(const int& index_arr[], const int size_arr, const uint flags) const; double GetProfitMask(const int &index_arr[], const int size_arr) const; void GetPositionsFromMagic(const ulong magig, int& arr[], int& size_arr); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTicketsTable::CTicketsTable(void) : CHashMapFastBase(512), m_last_created_position_pos(0), m_free_list_s(0), m_next_fresh(0), m_it(&this) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTicketsTable::Add(const Position &pos) { if(ReserveSlot(pos.ticket)) { m_last_created_position_pos = (m_free_list_s > 0) ? (m_free_list[--m_free_list_s]) : (m_next_fresh++); m_table[m_last_pos_tbl].key = pos.ticket; m_table[m_last_pos_tbl].value = m_last_created_position_pos; m_positions[m_last_created_position_pos] = pos; return true; } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTicketsTable::GetByTicket(const ulong ticket, Position &pos, int &out_idx) { out_idx = -1; // para hacerlo compatbile if(ContainsKey(ticket)) { out_idx = m_table[m_last_pos_tbl].value; // retona el indice en positions pos = m_positions[out_idx]; return true; } return false; } //+------------------------------------------------------------------+ bool CTicketsTable::GetByTicket(const ulong ticket, Position &pos) { if(ContainsKey(ticket)) { pos = m_positions[m_table[m_last_pos_tbl].value]; return true; } return false; } //+------------------------------------------------------------------+ inline Position CTicketsTable::GetByTicket(const ulong ticket) { return ContainsKey(ticket) ? m_positions[m_table[m_last_pos_tbl].value] : EMPTY_POSITION; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ __forceinline void CTicketsTable::DeleteByIndex(const int pos_idx) { if(Remove(m_positions[pos_idx].ticket)) m_free_list[m_free_list_s++] = pos_idx; } // a = i * 8 + k / 8 /// 8 + 2 = 10 = a // 1 //+------------------------------------------------------------------+ bool CTicketsTable::DeleteByTicket(const ulong ticket, Position &position) { //--- if(!ContainsKey(ticket)) { FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket)); return false; } //--- const int pos_idx = m_table[m_last_pos_tbl].value; position = m_positions[pos_idx]; m_free_list[m_free_list_s++] = pos_idx; RemoveLastFindCall(); //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline void CTicketsTable::UpdateSl(const double new_sl, const ulong ticket) { if(ContainsKey(ticket)) m_positions[m_table[m_last_pos_tbl].value].sl = new_sl; } //+------------------------------------------------------------------+ inline void CTicketsTable::UpdateTp(const double new_tp, const ulong ticket) { if(ContainsKey(ticket)) m_positions[m_table[m_last_pos_tbl].value].tp = new_tp; } //+------------------------------------------------------------------+ inline void CTicketsTable::UpdatePositionPartial(const double net, const double close_volume, const int pos_idx) { m_positions[pos_idx].volume -= close_volume; m_positions[pos_idx].profit += net; #ifdef MQLARTICLES_TICKETSINFO_DEBUG FastLog(FUNCION_ACTUAL, INFO_TEXT, 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)); #endif // MQLARTICLES_TICKETSINFO_DEBUG } //+------------------------------------------------------------------+ //| Mask functions | //+------------------------------------------------------------------+ void CTicketsTable::CloseAllPositionMask(const int &index_arr[], const int size_arr, CTrade& trade, const uint flags) const { for(int i = 0; i < size_arr; i++) { const int ri = index_arr[i]; if(!PositionSelectByTicket(m_positions[ri].ticket)) continue; //--- const double profit = PositionGetDouble(POSITION_PROFIT); const ENUM_POSITION_TYPE type = m_positions[ri].type; //--- if((type == POSITION_TYPE_BUY && (flags & FLAG_POSITION_TYPE_BUY) == 0) || (type == POSITION_TYPE_SELL && (flags & FLAG_POSITION_TYPE_SELL) == 0)) continue; if((flags & FLAG_CLOSE_ALL_PROFIT) != 0 && profit > 0) { trade.PositionClose(m_positions[ri].ticket); } else if((flags & FLAG_CLOSE_ALL_LOSS) != 0 && profit < 0) { trade.PositionClose(m_positions[ri].ticket); } } } //+------------------------------------------------------------------+ int CTicketsTable::GetPositionsMask(const int &index_arr[], const int size_arr, const uint flags) const { int count = 0; for(int i = 0; i < size_arr; i++) { const ENUM_POSITION_TYPE type = m_positions[index_arr[i]].type; if(type == POSITION_TYPE_BUY && (flags & FLAG_POSITION_TYPE_BUY) != 0) count++; if(type == POSITION_TYPE_SELL && (flags & FLAG_POSITION_TYPE_SELL) != 0) count++; } return count; } //+------------------------------------------------------------------+ double CTicketsTable::GetProfitMask(const int &index_arr[], const int size_arr) const { double v = 0.0; for(int i = 0; i < size_arr; i++) { if(!PositionSelectByTicket(m_positions[index_arr[i]].ticket)) continue; v += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); } return v; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // arr tendra que haber sido resizeado previa.. (no lo da en orden) void CTicketsTable::GetPositionsFromMagic(const ulong magic, int& arr[], int &size_arr) { m_it.Reset(); while(m_it.IsValid()) { const int pos_idx = m_it.Val(); if(m_positions[pos_idx].magic != magic && magic != NOT_MAGIC_NUMBER) { m_it.Next(); continue; } //--- arr[size_arr++] = pos_idx; //--- m_it.Next(); } } //--- } #endif // MQLARTICLES_RM_TICKETS_TABLE_MQH //+------------------------------------------------------------------+