2026-07-28 09:08:36 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| RMt_Functions.mqh |
|
|
|
|
|
//| Niquel y Leo, Copyright 2025 |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Niquel y Leo, Copyright 2025"
|
|
|
|
|
#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ón existe y obtener su tiempo de apertura
|
|
|
|
|
if(!PositionSelectByTicket(ticket))
|
|
|
|
|
{
|
|
|
|
|
Print("Error: Posició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é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ó un deal para el ticket ", ticket);
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verificar si el deal corresponde a la posició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ó un deal de apertura vá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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(start_date > 0 && start_date != D'1971.01.01 00: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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
return total_net_profit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Function to close orders |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// Converts an order type to its corresponding flag
|
|
|
|
|
#define OrderTypeToFlag(type) OrdensToFlagArray[type]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int ALL_FLAGS_STOPS = FLAG_ORDER_TYPE_BUY_STOP | FLAG_ORDER_TYPE_SELL_STOP;
|
|
|
|
|
const int ALL_FLAGS_LIMITS = FLAG_ORDER_TYPE_BUY_LIMIT | FLAG_ORDER_TYPE_SELL_LIMIT;
|
|
|
|
|
const int ALL_FLAGS_ORDERS = (FLAG_ORDER_TYPE_BUY | FLAG_ORDER_TYPE_SELL | FLAG_ORDER_TYPE_BUY_LIMIT |
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close all orders that match the flags in `flags`
|
|
|
|
|
void CloseAllOrders(int flags, CTrade & obj_trade, ulong magic_number_ = NOT_MAGIC_NUMBER)
|
|
|
|
|
{
|
|
|
|
|
ResetLastError();
|
|
|
|
|
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);
|
|
|
|
|
int bandera = OrderTypeToFlag(type_order);
|
|
|
|
|
if((bandera & flags) != 0 && (magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
|
|
|
|
|
{
|
|
|
|
|
if(type_order == ORDER_TYPE_BUY || type_order == ORDER_TYPE_SELL)
|
|
|
|
|
obj_trade.PositionClose(ticket);
|
|
|
|
|
else
|
|
|
|
|
obj_trade.OrderDelete(ticket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrintFormat("Error selecting order %d, last error %d", ticket, GetLastError());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Function to obtain the positions opened by the EA or user |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int PositionTypeToFlag(ENUM_POSITION_TYPE type)
|
|
|
|
|
{
|
|
|
|
|
if(type == POSITION_TYPE_BUY)
|
|
|
|
|
return FLAG_POSITION_TYPE_BUY;
|
|
|
|
|
else
|
|
|
|
|
if(type == POSITION_TYPE_SELL)
|
|
|
|
|
return FLAG_POSITION_TYPE_SELL;
|
|
|
|
|
|
|
|
|
|
return FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
int Get_Positions(int flags = FLAG_POSITION_TYPE_BUY | FLAG_POSITION_TYPE_SELL, ulong magic_number_ = NOT_MAGIC_NUMBER)
|
|
|
|
|
{
|
|
|
|
|
int counter = 0;
|
|
|
|
|
|
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
ulong position_ticket = PositionGetTicket(i);
|
|
|
|
|
if(!PositionSelectByTicket(position_ticket))
|
|
|
|
|
continue; // Si la selección falla, pasa a la siguiente posición
|
|
|
|
|
ulong position_magic = PositionGetInteger(POSITION_MAGIC);
|
|
|
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
|
// Check if the position type matches the flags
|
|
|
|
|
if((flags & PositionTypeToFlag(type)) != 0 &&
|
|
|
|
|
(position_magic == magic_number_ || magic_number_ == NOT_MAGIC_NUMBER))
|
|
|
|
|
{
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return counter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template<typename T>
|
|
|
|
|
bool RemoveIndexFromAnArrayOfPositions(T & array[], const ulong ticket, int reserve)
|
|
|
|
|
{
|
|
|
|
|
const int size = ArraySize(array);
|
|
|
|
|
int index = -1;
|
|
|
|
|
// Search index and move elements in a single loop
|
|
|
|
|
for(int i = 0; i < size; i++)
|
|
|
|
|
{
|
|
|
|
|
if(array[i].ticket == ticket)
|
|
|
|
|
{
|
|
|
|
|
index = i;
|
|
|
|
|
}
|
|
|
|
|
if(index != -1 && i < size - 1)
|
|
|
|
|
{
|
|
|
|
|
array[i] = array[i + 1]; // Move the elements
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(index == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Reducir el tamaño del array
|
|
|
|
|
ArrayResize(array, size - 1, reserve);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void PrintArrayAsTable(double & array[], string fila_descripcion, string columna_prefijo = "Valor")
|
|
|
|
|
{
|
|
|
|
|
string header = fila_descripcion;
|
|
|
|
|
int len = StringLen(header);
|
|
|
|
|
string values = StringRepeat(" ", len + 2);
|
|
|
|
|
|
|
|
|
|
int max_len = StringLen(header);
|
|
|
|
|
for(int i = 0; i < ArraySize(array); i++)
|
|
|
|
|
{
|
|
|
|
|
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
|
|
|
|
|
max_len = MathMax(max_len, StringLen(col_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header = StringFormat("%-" + (string)(max_len + 2) + "s", header);
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < ArraySize(array); i++)
|
|
|
|
|
{
|
|
|
|
|
string col_name = columna_prefijo + " " + IntegerToString(i + 1);
|
|
|
|
|
header += StringFormat("| %-10s ", col_name);
|
|
|
|
|
values += StringFormat("| %-10.2f ", array[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print(header);
|
|
|
|
|
Print(values);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CAccountGestor : public CAllClassEventsBasic
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CAccountGestor() {}
|
|
|
|
|
~CAccountGestor() {}
|
|
|
|
|
|
|
|
|
|
//--- Positions
|
|
|
|
|
virtual void OnClosePosition(const ROnClosePosition &pos, const int global_pos_index) {}
|
|
|
|
|
virtual void OnOpenPosition(const ROnOpenPosition &pos) {}
|
|
|
|
|
|
|
|
|
|
//--- Position modify
|
|
|
|
|
virtual void OnPositionModify(const Position& pos, const uint8_t change_flags) { }
|
|
|
|
|
|
|
|
|
|
//--- Orders
|
|
|
|
|
virtual void OnOrderAdd(const ROrder& order) { }
|
|
|
|
|
virtual void OnOrderUpdate(const ROrder& order) { }
|
|
|
|
|
virtual void OnOrderDelete(const ROrder& order) { }
|
|
|
|
|
|
|
|
|
|
//-- Function that is executed only once, where only the account profit fields are filled, such as account_gross_profit
|
|
|
|
|
//daily, weekly, etc.
|
|
|
|
|
virtual void OnNewProfit(const RAccountProfit &profit, const datetime curr_time) { }
|
|
|
|
|
|
|
|
|
|
//--- Function that is executed each time TesterDeposit or TesterWithdrawal is called... or capital is added to the account
|
|
|
|
|
virtual void OnWithdrawalDeposit(const double value) { } //If the value is positive it means a deposit, otherwise a withdrawal
|
|
|
|
|
|
|
|
|
|
//--- Function that is executed only once, only if there are previously open trades, only the position structure
|
|
|
|
|
virtual void OnInitNewPos(const ROnOpenPosition &position) { }
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
virtual void OnLossProfit(const double p) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#endif // MQLARTICLES_RM_RFUNCTIONS_MQH
|