MQLArticles/RM/RM_Functions.mqh

877 lines
58 KiB
MQL5

2025-09-22 09:09:20 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| RMt_Functions.mqh |
//| Niquel y Leo, Copyright 2025 |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
2025-11-10 09:33:30 -05:00
#property copyright "Niquel y Leo, Copyright 2025"
2025-09-22 09:09:20 -05:00
#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<EFBFBD>n existe y obtener su tiempo de apertura
if(!PositionSelectByTicket(ticket))
{
Print("Error: Posici<00>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<EFBFBD>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<00> un deal para el ticket ", ticket);
return 0.0;
}
// Verificar si el deal corresponde a la posici<EFBFBD>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<00> un deal de apertura v<00>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
2025-09-23 11:00:09 -05:00
};
2025-09-22 09:09:20 -05:00
//---
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
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
//---
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
if(start_date > 0 && start_date != D'1971.01.01 00:00')
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05: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);
}
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
}
//---
2025-11-23 08:03:53 -05:00
return total_net_profit;
}
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
//| Function to close orders |
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
// Converts an order type to its corresponding flag
#define OrderTypeToFlag(type) OrdensToFlagArray[type]
2025-09-22 09:09:20 -05:00
const int ALL_FLAGS_STOPS = FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP;
2025-11-23 08:03:53 -05:00
const int ALL_FLAGS_LIMITS = FLAG_ORDER_TYPE_BUY_LIMIT | FLAG_ORDER_TYPE_SELL_LIMIT;
2025-11-26 07:03:20 -05:00
const int ALL_FLAGS_ORDERS = (FLAG_ORDER_TYPE_BUY | FLAG_ORDER_TYPE_SELL | FLAG_ORDER_TYPE_BUY_LIMIT |
2025-11-23 08:03:53 -05:00
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);
2025-11-26 07:03:20 -05:00
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
// Close all orders that match the flags in `flags`
void CloseAllOrders(int flags, CTrade & obj_trade, ulong magic_number_ = NOT_MAGIC_NUMBER)
2025-11-26 07:03:20 -05:00
{
2025-11-23 08:03:53 -05:00
ResetLastError();
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
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);
2025-09-22 09:09:20 -05:00
int bandera = OrderTypeToFlag(type_order);
2025-11-23 08:03:53 -05:00
if((bandera & flags) != 0 && (magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
2025-09-22 09:09:20 -05:00
{
2025-11-23 08:03:53 -05:00
if(type_order == ORDER_TYPE_BUY || type_order == ORDER_TYPE_SELL)
2025-09-22 09:09:20 -05:00
obj_trade.PositionClose(ticket);
2025-11-23 08:03:53 -05:00
else
2025-09-22 09:09:20 -05:00
obj_trade.OrderDelete(ticket);
2025-11-23 08:03:53 -05:00
}
}
2025-09-22 09:09:20 -05:00
else
{
2025-11-23 08:03:53 -05:00
PrintFormat("Error selecting order %d, last error %d", ticket, GetLastError());
}
2025-09-22 09:09:20 -05:00
}
2025-11-23 08:03:53 -05:00
}
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
//| Function to obtain the positions opened by the EA or user |
//+------------------------------------------------------------------+
int PositionTypeToFlag(ENUM_POSITION_TYPE type)
{
2025-11-23 08:03:53 -05:00
if(type == POSITION_TYPE_BUY)
return FLAG_POSITION_TYPE_BUY;
else
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
if(type == POSITION_TYPE_SELL)
return FLAG_POSITION_TYPE_SELL;
2025-09-22 09:09:20 -05:00
return FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL;
}
2025-11-23 08:03:53 -05:00
//---
2025-09-22 09:09:20 -05:00
int Get_Positions(int flags = FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL, ulong magic_number_ = NOT_MAGIC_NUMBER)
{
2025-11-23 08:03:53 -05:00
int counter = 0;
2025-09-22 09:09:20 -05:00
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
2025-11-23 08:03:53 -05:00
ulong position_ticket = PositionGetTicket(i);
2025-09-22 09:09:20 -05:00
if(!PositionSelectByTicket(position_ticket))
continue; // Si la selecci<EFBFBD>n falla, pasa a la siguiente posici<EFBFBD>n
ulong position_magic = PositionGetInteger(POSITION_MAGIC);
2025-11-23 08:03:53 -05:00
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
2025-09-22 09:09:20 -05:00
// Check if the position type matches the flags
2025-11-23 08:03:53 -05:00
if((flags & PositionTypeToFlag(type)) != 0 &&
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
(position_magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
{
2025-09-22 09:09:20 -05:00
counter++;
2025-11-23 08:03:53 -05:00
}
2025-09-22 09:09:20 -05:00
}
return counter;
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
//| |
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-23 08:03:53 -05:00
template<typename T>
bool RemoveIndexFromAnArrayOfPositions(T & array[], const ulong ticket, int reserve)
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
{
const int size = ArraySize(array);
2025-09-22 09:09:20 -05:00
int index = -1;
// Search index and move elements in a single loop
2025-11-23 08:03:53 -05:00
for(int i = 0; i < size; i++)
{
2025-09-22 09:09:20 -05:00
if(array[i].ticket == ticket)
2025-11-23 08:03:53 -05:00
{
2025-09-22 09:09:20 -05:00
index = i;
}
2025-11-23 08:03:53 -05:00
if(index != -1 && i < size - 1)
2025-09-22 09:09:20 -05:00
{
array[i] = array[i + 1]; // Move the elements
}
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
}
if(index == -1)
return false;
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
// Reducir el tama<EFBFBD>o del array
ArrayResize(array, size - 1, reserve);
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
return true;
}
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
//| |
//+------------------------------------------------------------------+
void PrintArrayAsTable(double & array[], string fila_descripcion, string columna_prefijo = "Valor")
{
2025-11-23 08:03:53 -05:00
string header = fila_descripcion;
2025-09-22 09:09:20 -05:00
int len = StringLen(header);
2025-11-23 08:03:53 -05:00
string values = StringRepeat(" ", len + 2);
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
int max_len = StringLen(header);
2025-09-22 09:09:20 -05:00
for(int i = 0; i < ArraySize(array); i++)
2025-11-23 08:03:53 -05:00
{
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
max_len = MathMax(max_len, StringLen(col_name));
}
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
header = StringFormat("%-" + (string)(max_len + 2) + "s", header);
2025-09-22 09:09:20 -05:00
for(int i = 0; i < ArraySize(array); i++)
2025-11-23 08:03:53 -05:00
{
2025-09-22 09:09:20 -05:00
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
2025-11-23 08:03:53 -05:00
header += StringFormat("| %-10s ", col_name);
2025-09-22 09:09:20 -05:00
values += StringFormat("| %-10.2f ", array[i]);
}
Print(header);
Print(values);
}
//+------------------------------------------------------------------+
2025-11-23 08:03:53 -05:00
//| |
2025-11-26 07:03:20 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
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)
2025-11-23 08:03:53 -05:00
{
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);
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
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);
}
2025-11-26 07:03:20 -05:00
//+------------------------------------------------------------------+
//| Clase CTicketsInfo |
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
Position EMPTY_POSITION;
//---
class CTicketsInfo : public CLoggerBase
{
private:
//--- Data
2025-09-22 09:09:20 -05:00
Position m_positions[];
2025-11-26 07:03:20 -05:00
2025-09-22 09:09:20 -05:00
//--- Hash
2025-11-26 07:03:20 -05:00
uint16_t m_hash_table[];
2025-11-23 08:03:53 -05:00
2025-11-26 07:03:20 -05:00
bool m_hash_filled[];
uint16_t m_hash_size;
uint16_t m_hash_count;
2025-09-22 09:09:20 -05:00
//--- Static
2025-11-23 08:03:53 -05:00
static const float LOAD_FACTOR_LIMIT;
2025-09-22 09:09:20 -05:00
static const uint16_t INITIAL_HASH_SIZE;
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
//--- Internal functions
int16_t FindHash(const ulong ticket) const;
2025-11-23 08:03:53 -05:00
bool InsertHash(ulong ticket, uint16_t pos_idx);
bool RemoveHash(ulong ticket);
2025-09-22 09:09:20 -05:00
void UpdateHashAfterSwap(ulong ticket, uint16_t new_pos_idx);
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
void ResizeHashTable();
2025-11-23 08:03:53 -05:00
bool IsPrime(uint16_t n);
2025-09-22 09:09:20 -05:00
public:
2025-11-23 08:03:53 -05:00
CTicketsInfo(void);
2025-09-22 09:09:20 -05:00
~CTicketsInfo(void);
2025-11-26 07:03:20 -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;
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
2025-09-22 09:09:20 -05:00
inline void UpdatePositionPartial(double net, double close_volume, int16_t pos_idx);
2025-11-23 08:03:53 -05:00
inline void UpdateTp(double new_tp, ulong ticket);
2025-09-22 09:09:20 -05:00
inline void UpdateSl(double new_sl, ulong ticket);
2025-11-23 08:03:53 -05:00
//--- General Getters
2025-09-22 09:09:20 -05:00
// Index
inline bool IsValidIdx(int16_t index) const; // Is Valid index
__forceinline int GetIndex(const ulong ticket) const { return FindHash(ticket);}
2025-11-23 08:03:53 -05:00
2025-09-22 09:09:20 -05:00
// Total
2025-11-23 08:03:53 -05:00
__forceinline int16_t Total() const { return (int16_t)ArraySize(m_positions); }
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
__forceinline uint16_t TotalU() const { return (uint16_t)m_positions.Size(); }
2025-09-22 09:09:20 -05:00
//--- Exist
inline bool Exists(const ulong ticket) const;
2025-11-23 08:03:53 -05:00
//--- Free
2025-09-22 09:09:20 -05:00
void Free();
};
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
//| |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
const float CTicketsInfo::LOAD_FACTOR_LIMIT = 0.7f;
const uint16_t CTicketsInfo::INITIAL_HASH_SIZE = 9973;
2025-11-23 08:03:53 -05:00
#define CTicketsInfo_HashTicket(ticket) uint16_t(ticket % m_hash_size)
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
2025-11-23 08:03:53 -05:00
//| |
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
CTicketsInfo::CTicketsInfo(void) : m_hash_size(INITIAL_HASH_SIZE), m_hash_count(0)
{
ArrayResize(m_hash_table, m_hash_size);
2025-11-23 08:03:53 -05:00
ArrayResize(m_hash_filled, m_hash_size);
ArrayInitialize(m_hash_table, 65535);
2025-09-22 09:09:20 -05:00
ArrayInitialize(m_hash_filled, false);
}
//+------------------------------------------------------------------+
CTicketsInfo::~CTicketsInfo(void)
{
ArrayFree(m_positions);
2025-11-23 08:03:53 -05:00
2025-09-24 14:00:58 -05:00
ArrayFree(m_hash_table);
2025-11-23 19:15:34 -05:00
2025-11-23 08:03:53 -05:00
ArrayFree(m_hash_filled);
}
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
//| |
2025-09-24 14:00:58 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
inline bool CTicketsInfo::IsValidIdx(int16_t index) const
2025-09-24 14:00:58 -05:00
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
if(index >= 0 && index < Total())
2025-09-22 09:09:20 -05:00
return true;
2025-09-24 14:00:58 -05:00
2025-09-22 09:09:20 -05:00
LogError(StringFormat("El <00>ndice %d es inv<00>lido", index), FUNCION_ACTUAL);
2025-09-24 14:00:58 -05:00
return false;
2025-11-28 20:21:30 -05:00
2025-11-26 07:03:20 -05:00
}
2025-09-22 09:09:20 -05:00
2025-11-26 07:03:20 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
int16_t CTicketsInfo::FindHash(const ulong ticket) const
2025-11-23 08:03:53 -05:00
{