forked from nique_372/MQLArticles
291 lines
10 KiB
MQL5
291 lines
10 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| TinyTicketsTable.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_TINY_TICKETS_TABLE_MQH
| |
#define MQLARTICLES_RM_TINY_TICKETS_TABLE_MQH
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
#include "RM_Functions.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
namespace TSN
| |
{
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
Position EMPTY_POSITION;
| |
// Esta es la versino tiny, solko soporta 16 posiciones simultaneas
| |
// Normale el rango para usarla es si ud en su EA suele tener entre 1-8 posiciones COMO Maximo
| |
// a la vez.. si es conciente que puede tener mas use la version con HashMapFast donde el overhead
| |
// de una busqueda lineal ya es mas que justificable para usar la version hashmap
| |
// En la mayoria de casos donde solo se peude tener una op a la vez o 3-4 (casi el 80-90% de los EAs)
| |
// Esto es mas que suficiente y mas rapido que la version con hashmap (entre 2 a 20 veces mas rapido)
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
// Heredamos del main
| |
struct CTicketsTable
| |
{
| |
public:
| |
int m_last_created_position_pos;
| |
| |
private:
| |
//---
| |
Position m_positions[16];
| |
int m_positions_s;
| |
int m_last_pos_find;
| |
| |
public:
| |
CTicketsTable(void)
| |
: m_last_created_position_pos(0), m_positions_s(0) {}
| |
~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 ContainsKey(const ulong key);
| |
bool Add(const Position & pos);
| |
__forceinline int Count() const { return m_positions_s; }
| |
| |
//---
| |
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);
| |
};
| |
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsTable::Add(const Position &pos)
| |
{
| |
if(!ContainsKey(pos.ticket))
| |
{
| |
m_last_created_position_pos = m_positions_s++;
| |
m_positions[m_last_created_position_pos] = pos;
| |
return true;
| |
}
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsTable::ContainsKey(const ulong key)
| |
{
| |
//---
| |
for(m_last_pos_find = 0; m_last_pos_find < m_positions_s; m_last_pos_find++)
| |
if(m_positions[m_last_pos_find].ticket == key)
| |
return true;
| |
//---
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsTable::GetByTicket(const ulong ticket, Position &pos, int &out_idx)
| |
{
| |
out_idx = -1;
| |
if(ContainsKey(ticket))
| |
{
| |
out_idx = m_last_pos_find;
| |
pos = m_positions[out_idx];
| |
return true;
| |
}
| |
return false;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool CTicketsTable::GetByTicket(const ulong ticket, Position &pos)
| |
{
| |
if(ContainsKey(ticket))
| |
{
| |
pos = m_positions[m_last_pos_find];
| |
return true;
| |
}
| |
return false;
| |
}
| |
| |
| |
//+------------------------------------------------------------------+
| |
inline Position CTicketsTable::GetByTicket(const ulong ticket)
| |
{
| |
return ContainsKey(ticket) ? m_positions[m_last_pos_find] : EMPTY_POSITION;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void CTicketsTable::DeleteByIndex(const int pos_idx)
| |
{
| |
if(pos_idx < 0 || pos_idx >= m_positions_s)
| |
return; // nada
| |
| |
//---
| |
m_positions_s--;
| |
for(int i = pos_idx; i < m_positions_s; i++)
| |
m_positions[i] = m_positions[i + 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;
| |
}
| |
| |
//---
| |
position = m_positions[m_last_pos_find];
| |
| |
//---
| |
m_positions_s--;
| |
for(int i = m_last_pos_find; i < m_positions_s; i++)
| |
m_positions[i] = m_positions[i + 1];
| |
| |
//---
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
inline void CTicketsTable::UpdateSl(const double new_sl, const ulong ticket)
| |
{
| |
if(ContainsKey(ticket))
| |
m_positions[m_last_pos_find].sl = new_sl;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
inline void CTicketsTable::UpdateTp(const double new_tp, const ulong ticket)
| |
{
| |
if(ContainsKey(ticket))
| |
m_positions[m_last_pos_find].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)
| |
{
| |
for(int i = 0; i < m_positions_s; i++)
| |
{
| |
if(m_positions[i].magic != magic && magic != NOT_MAGIC_NUMBER)
| |
continue;
| |
arr[size_arr++] = i;
| |
}
| |
}
| |
| |
//---
| |
}
| |
| |
#endif // MQLARTICLES_RM_TINY_TICKETS_TABLE_MQH
| |
//+------------------------------------------------------------------+
|