MQLArticles/RM/TicketsTable.mqh

305 lines
11 KiB
MQL5
Raw Permalink Normal View History

2026-07-28 08:55:53 -05:00
//+------------------------------------------------------------------+
2026-05-25 09:26:40 -05:00
//| TicketsTable.mqh |
2026-07-28 08:55:53 -05:00
//| Copyright 2026, Niquel Mendoza |
//| https://www.mql5.com |
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
#property copyright "Copyright 2026, Niquel Mendoza"
#property link "https://www.mql5.com"
2026-05-25 09:26:40 -05:00
#property strict
#ifndef MQLARTICLES_RM_TICKETS_TABLE_MQH
#define MQLARTICLES_RM_TICKETS_TABLE_MQH
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
//| |
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
#include <TSN\\GCol\\HashMap.mqh>
#include <TSN\\GCol\\GenericHashes.mqh>
#include "RM_Functions.mqh"
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
//| 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
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| |
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
Position EMPTY_POSITION;
2026-05-28 12:01:58 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Heredamos del main
class CTicketsTable : public CHashMapFastBase<ulong, int, TSN::CGenericHash_ulong_fibbo>
2026-05-25 09:26:40 -05:00
{
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);
2026-07-28 08:55:53 -05:00
~CTicketsTable(void) {}
2026-05-25 09:26:40 -05:00
2026-07-28 08:55:53 -05:00
//---
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; }
2026-05-25 09:26:40 -05:00
2026-07-28 08:55:53 -05:00
//---
__forceinline void DeleteByIndex(const int pos_idx);
2026-05-25 09:26:40 -05:00
bool DeleteByTicket(const ulong ticket);
2026-07-28 08:55:53 -05:00
bool DeleteByTicket(const ulong ticket, Position & position);
2026-05-25 09:26:40 -05:00
//---
bool Add(const Position & pos);
2026-05-25 09:26:40 -05:00
2026-07-28 08:55:53 -05:00
//---
inline void UpdatePositionPartial(const double net, const double close_volume, const int pos_idx);
2026-05-25 09:26:40 -05:00
inline void UpdateTp(const double new_tp, const ulong ticket);
inline void UpdateSl(const double new_sl, const ulong ticket);
2026-07-28 08:55:53 -05:00
//---
void CloseAllPositionMask(const int& index_arr[], const int size_arr, CTrade & trade, const uint flags) const;
2026-05-28 12:01:58 -05:00
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);
2026-05-25 09:26:40 -05:00
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CTicketsTable::CTicketsTable(void)
: CHashMapFastBase<ulong, int, TSN::CGenericHash_ulong_fibbo>(512),
m_last_created_position_pos(0), m_free_list_s(0), m_next_fresh(0), m_it(&this)
{
}
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTicketsTable::Add(const Position &pos)
2026-05-25 09:26:40 -05:00
{
if(ReserveSlot(pos.ticket))
2026-05-25 09:26:40 -05:00
{
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;
2026-05-25 09:26:40 -05:00
return true;
}
2026-07-28 08:55:53 -05:00
return false;
2026-05-25 09:26:40 -05:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
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)
2026-05-25 09:26:40 -05:00
{
if(ContainsKey(ticket))
2026-05-25 09:26:40 -05:00
{
pos = m_positions[m_table[m_last_pos_tbl].value];
2026-05-25 09:26:40 -05:00
return true;
}
2026-07-28 08:55:53 -05:00
return false;
2026-05-25 09:26:40 -05:00
}
//+------------------------------------------------------------------+
inline Position CTicketsTable::GetByTicket(const ulong ticket)
2026-05-25 09:26:40 -05:00
{
return ContainsKey(ticket) ? m_positions[m_table[m_last_pos_tbl].value] : EMPTY_POSITION;
2026-05-25 09:26:40 -05:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
__forceinline void CTicketsTable::DeleteByIndex(const int pos_idx)
2026-05-25 09:26:40 -05:00
{
if(Remove(m_positions[pos_idx].ticket))
m_free_list[m_free_list_s++] = pos_idx;
2026-05-25 09:26:40 -05:00
}
2026-08-04 17:02:36 -05:00
// a = i * 8 + k / 8
/// 8 + 2 = 10 = a
// 1
2026-05-25 09:26:40 -05:00
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
bool CTicketsTable::DeleteByTicket(const ulong ticket, Position &position)
2026-05-25 09:26:40 -05:00
{
//---
if(!ContainsKey(ticket))
2026-05-25 09:26:40 -05:00
{
2026-07-28 08:55:53 -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;
}
//---
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();
2026-05-25 09:26:40 -05:00
//---
return true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
inline void CTicketsTable::UpdateSl(const double new_sl, const ulong ticket)
2026-05-25 09:26:40 -05:00
{
if(ContainsKey(ticket))
m_positions[m_table[m_last_pos_tbl].value].sl = new_sl;
2026-05-25 09:26:40 -05:00
}
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
inline void CTicketsTable::UpdateTp(const double new_tp, const ulong ticket)
2026-05-25 09:26:40 -05:00
{
if(ContainsKey(ticket))
m_positions[m_table[m_last_pos_tbl].value].tp = new_tp;
2026-05-25 09:26:40 -05:00
}
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
inline void CTicketsTable::UpdatePositionPartial(const double net, const double close_volume, const int pos_idx)
2026-05-25 09:26:40 -05:00
{
m_positions[pos_idx].volume -= close_volume;
m_positions[pos_idx].profit += net;
2026-07-28 08:55:53 -05:00
#ifdef MQLARTICLES_TICKETSINFO_DEBUG
2026-08-04 17:02:36 -05:00
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,
2026-08-04 17:02:36 -05:00
net,
m_positions[pos_idx].ticket));
2026-07-28 08:55:53 -05:00
#endif // MQLARTICLES_TICKETSINFO_DEBUG
2026-05-25 09:26:40 -05:00
}
2026-05-25 09:29:52 -05:00
2026-05-28 12:01:58 -05:00
//+------------------------------------------------------------------+
//| Mask functions |
2026-05-28 12:01:58 -05:00
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
void CTicketsTable::CloseAllPositionMask(const int &index_arr[], const int size_arr, CTrade& trade, const uint flags) const
2026-05-28 12:01:58 -05:00
{
for(int i = 0; i < size_arr; i++)
{
const int ri = index_arr[i];
if(!PositionSelectByTicket(m_positions[ri].ticket))
2026-05-28 12:01:58 -05:00
continue;
//---
const double profit = PositionGetDouble(POSITION_PROFIT);
const ENUM_POSITION_TYPE type = m_positions[ri].type;
2026-05-28 12:01:58 -05:00
//---
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);
2026-05-28 12:01:58 -05:00
}
else
if((flags & FLAG_CLOSE_ALL_LOSS) != 0 && profit < 0)
{
trade.PositionClose(m_positions[ri].ticket);
2026-05-28 12:01:58 -05:00
}
}
}
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
int CTicketsTable::GetPositionsMask(const int &index_arr[], const int size_arr, const uint flags) const
2026-05-28 12:01:58 -05:00
{
int count = 0;
for(int i = 0; i < size_arr; i++)
{
const ENUM_POSITION_TYPE type = m_positions[index_arr[i]].type;
2026-05-28 12:01:58 -05:00
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;
}
//+------------------------------------------------------------------+
2026-07-28 08:55:53 -05:00
double CTicketsTable::GetProfitMask(const int &index_arr[], const int size_arr) const
2026-05-28 12:01:58 -05:00
{
double v = 0.0;
for(int i = 0; i < size_arr; i++)
{
if(!PositionSelectByTicket(m_positions[index_arr[i]].ticket))
2026-05-28 12:01:58 -05:00
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)
2026-05-28 12:01:58 -05:00
{
m_it.Reset();
while(m_it.IsValid())
2026-05-28 12:01:58 -05:00
{
const int pos_idx = m_it.Val();
if(m_positions[pos_idx].magic != magic && magic != NOT_MAGIC_NUMBER)
{
m_it.Next();
2026-05-28 12:01:58 -05:00
continue;
}
//---
arr[size_arr++] = pos_idx;
//---
m_it.Next();
2026-05-28 12:01:58 -05:00
}
}
2026-07-28 08:55:53 -05:00
//---
}
2026-05-28 12:01:58 -05:00
2026-07-28 08:55:53 -05:00
#endif // MQLARTICLES_RM_TICKETS_TABLE_MQH
2026-05-28 12:01:58 -05:00
//+------------------------------------------------------------------+