2026-05-25 09:26:40 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| 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
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-05-28 12:01:58 -05:00
|
|
|
//| Include |
|
2026-05-25 09:26:40 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "RM_Functions.mqh"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-05-28 12:01:58 -05:00
|
|
|
//| Defines |
|
2026-05-25 09:26:40 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
Position EMPTY_POSITION;
|
|
|
|
|
|
|
|
|
|
//--- Defines
|
|
|
|
|
// Numero maximo de posiciones abiertas al mismo tiempo
|
|
|
|
|
#ifndef MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS
|
2026-05-25 09:29:52 -05:00
|
|
|
#define MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS 512
|
2026-05-25 09:26:40 -05:00
|
|
|
#endif // MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS
|
2026-05-25 09:29:52 -05:00
|
|
|
|
|
|
|
|
|
2026-05-25 09:26:40 -05:00
|
|
|
// Numero de hash en la tabla
|
2026-05-25 09:29:52 -05:00
|
|
|
#ifndef MQLARTICLES_HASH_TABLE_SIZE
|
|
|
|
|
#define MQLARTICLES_HASH_TABLE_SIZE 1024
|
2026-05-25 09:26:40 -05:00
|
|
|
#endif // MQLARTICLES_HASH_TABLE_SIZE
|
|
|
|
|
|
2026-05-25 09:29:52 -05:00
|
|
|
#define MQLARTICLES_HASH_TABLE_INVALID_INDEX 65535
|
2026-05-25 09:26:40 -05:00
|
|
|
|
2026-05-28 12:01:58 -05:00
|
|
|
//--- Descomente para activar mas logs
|
|
|
|
|
//#define MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
//---
|
|
|
|
|
// Notas:
|
|
|
|
|
// 1. Hash table alamcena posiciones en m_positions
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
struct CTicketsInfo
|
2026-05-25 09:26:40 -05:00
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
//--- Data
|
|
|
|
|
Position m_positions[MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS];
|
|
|
|
|
|
|
|
|
|
//--- Hash (una sola tabla)
|
|
|
|
|
uint16_t m_hash_table[MQLARTICLES_HASH_TABLE_SIZE];
|
|
|
|
|
|
2026-05-25 09:29:52 -05:00
|
|
|
//---
|
|
|
|
|
uint16_t m_count;
|
|
|
|
|
|
2026-05-25 09:26:40 -05:00
|
|
|
//--- Internal functions
|
|
|
|
|
bool InsertHash(const ulong ticket, const uint16_t pos_idx);
|
|
|
|
|
bool RemoveHash(const ulong ticket);
|
|
|
|
|
void UpdateHashAfterSwap(const ulong ticket, const uint16_t new_pos_idx);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CTicketsInfo(void);
|
2026-05-28 12:01:58 -05:00
|
|
|
// ~CTicketsInfo(void);
|
|
|
|
|
//---
|
|
|
|
|
int16_t FindHash(const ulong ticket) const;
|
2026-05-25 09:26:40 -05:00
|
|
|
|
|
|
|
|
//--- 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;
|
2026-05-28 12:01:58 -05:00
|
|
|
__forceinline Position GetPositionByIndex(const int16_t index) const { return m_positions[index]; }
|
|
|
|
|
__forceinline ulong GetPositionMagicByIndex(const int16_t index) const { return m_positions[index].magic; }
|
|
|
|
|
|
2026-05-25 09:26:40 -05:00
|
|
|
|
|
|
|
|
// Delete
|
|
|
|
|
bool DeleteByIndex(const int16_t pos_idx);
|
|
|
|
|
bool DeleteByTicket(const ulong ticket);
|
|
|
|
|
bool DeleteByTicket(const ulong ticket, Position &position);
|
|
|
|
|
|
|
|
|
|
// Add
|
|
|
|
|
bool Add(const Position &p);
|
|
|
|
|
|
|
|
|
|
// Update
|
|
|
|
|
inline void UpdatePositionPartial(const double net, const double close_volume, const int16_t pos_idx);
|
|
|
|
|
inline void UpdateTp(const double new_tp, const ulong ticket);
|
|
|
|
|
inline void UpdateSl(const double new_sl, const ulong ticket);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--- General Getters
|
|
|
|
|
// Index
|
|
|
|
|
inline bool IsValidIdx(const int16_t index) const;
|
|
|
|
|
|
|
|
|
|
// Total
|
|
|
|
|
__forceinline int16_t Total() const { return (int16_t)m_count; }
|
|
|
|
|
__forceinline uint16_t TotalU() const { return m_count; }
|
|
|
|
|
|
|
|
|
|
// Capacity
|
|
|
|
|
inline bool IsFull() const { return m_count >= MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS; }
|
|
|
|
|
inline uint16_t Capacity() const { return MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS; }
|
|
|
|
|
|
|
|
|
|
//--- Exist
|
|
|
|
|
inline bool Exists(const ulong ticket) const;
|
|
|
|
|
|
|
|
|
|
//--- Free
|
|
|
|
|
void Free();
|
2026-05-28 12:01:58 -05:00
|
|
|
|
|
|
|
|
//--- Utilidades
|
|
|
|
|
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) const;
|
2026-05-25 09:26:40 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CTicketsInfo::CTicketsInfo(void) : m_count(0)
|
|
|
|
|
{
|
|
|
|
|
ArrayInitialize(m_hash_table, MQLARTICLES_HASH_TABLE_INVALID_INDEX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
inline bool CTicketsInfo::IsValidIdx(const int16_t index) const
|
|
|
|
|
{
|
|
|
|
|
if(index >= 0 && index < Total())
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int16_t CTicketsInfo::FindHash(const ulong ticket) const
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t h = (uint16_t)(ticket % MQLARTICLES_HASH_TABLE_SIZE);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
for(uint16_t i = 0; i < MQLARTICLES_HASH_TABLE_SIZE; i++)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t idx = (h + i) % MQLARTICLES_HASH_TABLE_SIZE;
|
|
|
|
|
const uint16_t pos_idx = m_hash_table[idx];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx == MQLARTICLES_HASH_TABLE_INVALID_INDEX)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, CAUTION_TEXT, StringFormat("El slot del ticket %I64u esta vacio", ticket));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx < m_count && m_positions[pos_idx].ticket == ticket)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, INFO_TEXT, StringFormat("Ticket %I64u encontrado en idx=%u, pos_idx=%u", ticket, idx, pos_idx));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return (int16_t)pos_idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx >= m_count)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Indice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx));
|
2026-05-25 09:26:40 -05:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, WARNING_TEXT, StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::InsertHash(const ulong ticket, const uint16_t pos_idx)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t h = (uint16_t)(ticket % MQLARTICLES_HASH_TABLE_SIZE);
|
|
|
|
|
for(uint16_t i = 0; i < MQLARTICLES_HASH_TABLE_SIZE; i++)
|
|
|
|
|
{
|
|
|
|
|
const uint16_t idx = (h + i) % MQLARTICLES_HASH_TABLE_SIZE;
|
|
|
|
|
if(m_hash_table[idx] == MQLARTICLES_HASH_TABLE_INVALID_INDEX)
|
|
|
|
|
{
|
|
|
|
|
m_hash_table[idx] = pos_idx;
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, INFO_TEXT, StringFormat("Ticket %I64u insertado en idx=%u, pos_idx=%u", ticket, idx, pos_idx));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Tabla hash llena para ticket %I64u", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::RemoveHash(const ulong ticket)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t h = (uint16_t)(ticket % MQLARTICLES_HASH_TABLE_SIZE);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
for(uint16_t i = 0; i < MQLARTICLES_HASH_TABLE_SIZE; i++)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t idx = (h + i) % MQLARTICLES_HASH_TABLE_SIZE;
|
|
|
|
|
const uint16_t pos_idx = m_hash_table[idx];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx == MQLARTICLES_HASH_TABLE_INVALID_INDEX)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, CAUTION_TEXT, StringFormat("El ticket %I64u no existe (casilla vacía en idx=%u)", ticket, idx));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx < m_count && m_positions[pos_idx].ticket == ticket)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, INFO_TEXT, StringFormat("Se eliminó exitosamente el ticket %I64u en idx=%u, pos_idx=%u", ticket, idx, pos_idx));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
m_hash_table[idx] = MQLARTICLES_HASH_TABLE_INVALID_INDEX;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx >= m_count)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Índice inválido pos_idx=%u para ticket %I64u en idx=%u", pos_idx, ticket, idx));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, WARNING_TEXT, StringFormat("El ticket %I64u no se ha encontrado tras recorrer toda la tabla", ticket));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTicketsInfo::UpdateHashAfterSwap(const ulong ticket, const uint16_t new_pos_idx)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t h = (uint16_t)(ticket % MQLARTICLES_HASH_TABLE_SIZE);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
for(uint16_t i = 0; i < MQLARTICLES_HASH_TABLE_SIZE; i++)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const uint16_t idx = (h + i) % MQLARTICLES_HASH_TABLE_SIZE;
|
|
|
|
|
const uint16_t pos_idx = m_hash_table[idx];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx == MQLARTICLES_HASH_TABLE_INVALID_INDEX)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx < m_count && m_positions[new_pos_idx].ticket == ticket)
|
|
|
|
|
{
|
|
|
|
|
m_hash_table[idx] = new_pos_idx;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
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(const double new_sl, const ulong ticket)
|
|
|
|
|
{
|
|
|
|
|
const int16_t pos_idx = FindHash(ticket);
|
|
|
|
|
if(pos_idx >= 0)
|
|
|
|
|
m_positions[pos_idx].sl = new_sl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
inline void CTicketsInfo::UpdateTp(const double new_tp, const ulong ticket)
|
|
|
|
|
{
|
|
|
|
|
const int16_t pos_idx = FindHash(ticket);
|
|
|
|
|
if(pos_idx >= 0)
|
|
|
|
|
m_positions[pos_idx].tp = new_tp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
inline void CTicketsInfo::UpdatePositionPartial(const double net, const double close_volume, const int16_t pos_idx)
|
|
|
|
|
{
|
|
|
|
|
if(pos_idx >= 0 && pos_idx < (int16_t)m_count)
|
|
|
|
|
{
|
|
|
|
|
m_positions[pos_idx].volume -= close_volume;
|
|
|
|
|
m_positions[pos_idx].profit += net;
|
2026-05-28 12:01:58 -05:00
|
|
|
#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
|
2026-05-25 09:26:40 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::Add(const Position &p)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(m_count >= MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Límite de posiciones alcanzado (%d/%d). No se puede añadir el ticket %I64u",
|
|
|
|
|
m_count, MQLARTICLES_RM_ACCOUNT_MAX_POSITIONS, p.ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(FindHash(p.ticket) >= 0)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
|
|
|
|
|
FastLog(FUNCION_ACTUAL, CAUTION_TEXT, StringFormat("La posición %I64u ya existe", p.ticket));
|
|
|
|
|
#endif // MQLARTICLES_TICKETSINFO_DEBUG
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_positions[m_count] = p;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(!InsertHash(p.ticket, m_count))
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("No se pudo insertar el ticket %I64u en la tabla hash", p.ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_count++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::DeleteByIndex(const int16_t pos_idx)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
if(pos_idx < 0 || pos_idx >= (int16_t)m_count)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Indice invalido %d", pos_idx));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
const ulong ticket = m_positions[pos_idx].ticket;
|
|
|
|
|
if(!RemoveHash(ticket))
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_count--;
|
|
|
|
|
if(pos_idx != m_count)
|
|
|
|
|
{
|
|
|
|
|
m_positions[pos_idx] = m_positions[m_count];
|
|
|
|
|
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::DeleteByTicket(const ulong ticket)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const int16_t pos_idx = FindHash(ticket);
|
|
|
|
|
if(pos_idx < 0)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(!RemoveHash(ticket))
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_count--;
|
|
|
|
|
if(pos_idx != m_count)
|
|
|
|
|
{
|
|
|
|
|
m_positions[pos_idx] = m_positions[m_count];
|
|
|
|
|
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CTicketsInfo::DeleteByTicket(const ulong ticket, Position &position)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
const int16_t pos_idx = FindHash(ticket);
|
|
|
|
|
if(pos_idx < 0)
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("No se pudo eliminar el ticket %I64u, ya que no existe", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
position = m_positions[pos_idx];
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(!RemoveHash(ticket))
|
|
|
|
|
{
|
2026-05-28 12:01:58 -05:00
|
|
|
FastLog(FUNCION_ACTUAL, ERROR_TEXT, StringFormat("Fallo al eliminar el ticket %I64u de la tabla hash", ticket));
|
2026-05-25 09:26:40 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_count--;
|
|
|
|
|
if(pos_idx != m_count)
|
|
|
|
|
{
|
|
|
|
|
m_positions[pos_idx] = m_positions[m_count];
|
|
|
|
|
UpdateHashAfterSwap(m_positions[pos_idx].ticket, pos_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTicketsInfo::Free(void)
|
|
|
|
|
{
|
|
|
|
|
m_count = 0;
|
|
|
|
|
ArrayInitialize(m_hash_table, MQLARTICLES_HASH_TABLE_INVALID_INDEX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
inline bool CTicketsInfo::Exists(const ulong ticket) const
|
|
|
|
|
{
|
|
|
|
|
return FindHash(ticket) >= 0;
|
|
|
|
|
}
|
2026-05-25 09:29:52 -05:00
|
|
|
|
2026-05-28 12:01:58 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CTicketsInfo::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 = this.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(this.m_positions[ri].ticket);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if((flags & FLAG_CLOSE_ALL_LOSS) != 0 && profit < 0)
|
|
|
|
|
{
|
|
|
|
|
trade.PositionClose(this.m_positions[ri].ticket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CTicketsInfo::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 CTicketsInfo::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..
|
|
|
|
|
void CTicketsInfo::GetPositionsFromMagic(const ulong magic, int& arr[], int &size_arr) const
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < m_count; i++)
|
|
|
|
|
{
|
|
|
|
|
if(m_positions[i].magic != magic && magic != NOT_MAGIC_NUMBER)
|
|
|
|
|
continue;
|
|
|
|
|
arr[size_arr++] = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 09:29:52 -05:00
|
|
|
#endif // MQLARTICLES_RM_TICKETS_TABLE_MQH
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-05-28 12:01:58 -05:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|