MQLArticles/RM/RiskManagement.mqh

1166 lines
89 KiB
MQL5
Raw Permalink Normal View History

2025-09-22 09:09:20 -05:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| RiskManagement.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com/es/users/nique_372/news"
#property strict
2025-11-10 09:33:30 -05:00
#ifndef MQLARTICLES_RM_RISKMANAGEMENT_MQH
#define MQLARTICLES_RM_RISKMANAGEMENT_MQH
2025-09-22 09:09:20 -05:00
2025-12-21 12:41:35 -05:00
#include "RM_Hooks.mqh"
2025-09-22 09:09:20 -05:00
/*
ADVERTENCIA al momeneto de a<EFBFBD>adir items a esta clase, esta si son dinamicos las eliminara cuidado, como un modificador.
2025-09-24 14:00:58 -05:00
WARNING when adding items to this class, if they are dynamic, it will remove them carefully, as a modifier.
2025-09-22 09:09:20 -05:00
*/
//open_positions
2026-01-29 08:19:03 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00
//| Clase Base CRiskManagement |
//+------------------------------------------------------------------+
class CRiskManagemet : public CRiskManagemetBase
{
protected:
//---
CGetLote* get_lote;
//---
double chosen_balance;
//--- Extra Variables
ENUM_GET_LOT type_get_lot;
//-- main variables
long StopLoss; //StopLoss
2025-09-24 14:00:58 -05:00
double lote; //Last recorded lot
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- variables to store the values of the maximum losses
double nmlpo; //last expected loss
bool is_init; //flag indicating if it is init
2025-09-22 09:09:20 -05:00
//--- variables that store percentages and enumeration, which will be used for the subsequent calculation of losses
2026-03-05 12:10:29 -05:00
CLossProfitManager loss_profits_manager;
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Modifiers
2026-03-05 12:10:29 -05:00
CRiskManagemeHookManager m_hooks;
2025-09-22 09:09:20 -05:00
bool isCachedModifier[LOSS_PROFIT_COUNT];
2025-09-24 14:00:58 -05:00
//--- general function to assign values to loss variables
2026-01-28 13:02:22 -05:00
virtual double GetValorWithApplied(double percentage_, int type, ENUM_APPLIED_PERCENTAGES applied) const override;
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Update profits function
2026-02-14 17:00:45 -05:00
void UpdateProfit(const double profit, const double acc_total_profit);
virtual void OnOpenTrade() { }
virtual void OnCloseTrade() { }
2025-09-22 09:09:20 -05:00
//---
2025-11-26 07:03:20 -05:00
virtual FuncionLossProfitSuperate SuperateModeToFunctionProfit(ENUM_TYPE_LOSS_PROFIT type) const;
virtual FuncionLossProfitSuperate SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type) const;
2025-09-22 09:09:20 -05:00
2025-11-26 07:03:20 -05:00
//---
virtual void* GetPtrLossProfits() { return &this; }
2025-09-22 09:09:20 -05:00
public:
2025-09-24 14:00:58 -05:00
//--- Constructor and Destructor
2026-02-14 17:00:45 -05:00
CRiskManagemet(const ulong _magic, const ENUM_GET_LOT type_get_lot_);
2025-09-22 09:09:20 -05:00
~CRiskManagemet();
//--- Set
void EndAddProfitLoss();
2025-09-24 14:00:58 -05:00
virtual void SetGeneralParameters(MqlParam &params[]) = 0;
2025-09-22 09:09:20 -05:00
void SetLote(CGetLote* lote_ptr);
inline void SetStopLoss(double dist_open_sl) { this.StopLoss = (long)ConvertPriceToPoints(_Symbol, dist_open_sl); }
inline void SetStopLoss(long _sl_point_) { this.StopLoss = _sl_point_; }
//--- Get losses and profits
2025-11-26 07:03:20 -05:00
const CLossProfit* const GetLossOrProfit(const ENUM_TYPE_LOSS_PROFIT _type) const { return loss_profits_manager[_type]; }
//--- Superated funcionts
2026-03-05 12:10:29 -05:00
const CLossProfitManager* GetLossProfitManager() const { return &loss_profits_manager; }
2025-09-22 09:09:20 -05:00
//--- Add
2025-12-21 12:41:35 -05:00
// Modificators
bool AddModificator(CExtraModifications* modificator); // Modificator pointer
__forceinline bool AddModificator(CExtraModifications &modificator) { return AddModificator(&modificator); }
// Hook
bool AddRmHook(IRiskManagementHook* hook);
__forceinline bool AddRmHook(IRiskManagementHook& hook) { return AddRmHook(&hook); }
2026-01-07 11:00:48 -05:00
2025-12-21 12:41:35 -05:00
// Loss
2025-11-26 07:03:20 -05:00
bool AddLoss(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool strict,
2025-09-24 14:00:58 -05:00
bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL); // Loss
2025-12-21 12:41:35 -05:00
// Profit
2025-11-26 07:03:20 -05:00
bool AddProfit(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool is_strict_,
bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL); // Profit
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Functions inherited from CAccountGestor
2026-01-28 12:07:20 -05:00
// Basic events
2026-02-14 17:00:45 -05:00
void OnNewDay(const datetime curr_time) override;
void OnNewWeek(const datetime curr_time) override;
void OnNewMonth(const datetime curr_time) override;
2026-01-28 12:07:20 -05:00
// Account status
2026-02-14 17:00:45 -05:00
void OnNewProfit(const ROnOpenClosePosition &profit, const datetime curr_time) override;
2026-01-28 12:07:20 -05:00
void OnOpenClosePosition(const ROnOpenClosePosition &pos) override;
2026-02-14 17:00:45 -05:00
void OnWithdrawalDeposit(const double value) override;
2026-01-28 12:07:20 -05:00
void OnInitNewPos(const ROnOpenClosePosition &position) override;
2025-09-22 09:09:20 -05:00
//--- Get the lot using GMPLO and type_get_lot
double GetLote(ENUM_ORDER_TYPE type, double entry_price, ulong deviation, ulong stop_limit);
//--- Obtain the SL by risk per operation
long GetSL(ENUM_ORDER_TYPE type, double entry_price, ulong deviation, ulong stop_limit);
//--- Functions for working with the get lot size mod
void TypeGetLot(ENUM_GET_LOT new_get_mode) { this.type_get_lot = new_get_mode; }
inline ENUM_GET_LOT TypeGetLot() const { return this.type_get_lot; }
2025-09-22 09:09:20 -05:00
2026-04-19 11:47:45 -05:00
//---
__forceinline double LastLotSize() const { return this.lote; }
__forceinline double Nlmpo() const { return this.nmlpo; }
2025-09-24 14:00:58 -05:00
//--- Risk management type
2025-09-22 09:09:20 -05:00
virtual inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement() const = 0;
};
2025-09-24 14:00:58 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
CRiskManagemet::CRiskManagemet(const ulong _magic, const ENUM_GET_LOT type_get_lot_)
2026-01-31 19:44:40 -05:00
: CRiskManagemetBase(_magic)
2025-09-22 09:09:20 -05:00
{
//---
this.chosen_balance = AccountInfoDouble(ACCOUNT_BALANCE);
2025-09-22 09:09:20 -05:00
for(int i = 0 ; i < LOSS_PROFIT_COUNT; i++)
{
isCachedModifier[i] = false;
}
2026-01-28 12:07:20 -05:00
//---
const bool ac_i = account_status.IsInitialized();
//--- Registramos la clase para recibir eventos
// Basicos
2026-01-28 13:02:22 -05:00
CBasicEvents::Register(&this, BASICEVENT_REG_ALL_FLAG); // OnNewDay, Week, Month
2026-01-28 12:07:20 -05:00
// Para trabajar con las posiciones
account_status.RegisterEvents(&this,
(ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT |
ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT |
2026-01-28 13:02:22 -05:00
(ac_i ? 0 : ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS)
2026-01-28 12:07:20 -05:00
));
2025-09-22 09:09:20 -05:00
//---
this.type_get_lot = type_get_lot_;
2026-01-29 08:19:03 -05:00
//---
2026-01-31 19:44:40 -05:00
AddLogger(loss_profits_manager); // Si pertenece..
AddLogger(m_hooks);
2025-09-22 09:09:20 -05:00
//---
2026-01-28 12:07:20 -05:00
if(ac_i) // Se crea luego de que se inicie
2025-09-22 09:09:20 -05:00
{
2026-01-28 12:07:20 -05:00
for(int i = PositionsTotal() - 1; i >= 0; i--)
2025-09-22 09:09:20 -05:00
{
2026-01-28 12:07:20 -05:00
ulong position_ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(position_ticket))
continue;
ulong position_magic = PositionGetInteger(POSITION_MAGIC);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(position_magic == _magic || _magic == NOT_MAGIC_NUMBER)
{
Position new_pos;
new_pos.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
new_pos.ticket = position_ticket;
new_pos.profit = GetTotalPositionProfitNoCurrent(new_pos.ticket);
new_pos.magic = _magic;
AddArrayNoVerification2(open_positions, new_pos, RISK_MANAGEMENT_RESERVE_POS)
}
2025-09-22 09:09:20 -05:00
}
}
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRiskManagemet::~CRiskManagemet()
{
2026-01-28 17:12:28 -05:00
//---
2025-09-22 09:09:20 -05:00
CleanItems("RiskManagement");
2026-01-28 17:12:28 -05:00
2026-01-31 19:44:40 -05:00
//--- Registramos la clase para recibir eventos
// Basicos
if(CBasicEvents::IsActive())
2026-01-28 17:12:28 -05:00
CBasicEvents::Unregister(&this, BASICEVENT_REG_ALL_FLAG); // OnNewDay, Week, Month
2026-01-31 19:44:40 -05:00
// Account status
if(account_status.IsActive())
{
2026-01-28 17:12:28 -05:00
// Para trabajar con las posiciones
account_status.UnregisterEvents(&this,
(ACCOUNT_STATUS_REG_FLAG_ON_OPEN_CLOSE_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT |
ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT |
ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS)
);
}
2025-09-22 09:09:20 -05:00
}
2026-01-28 12:07:20 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRiskManagemet::OnInitNewPos(const ROnOpenClosePosition &position)
{
if(position.position.magic == this.magic_number || this.magic_number == NOT_MAGIC_NUMBER)
{
AddArrayNoVerification2(open_positions, position.position, RISK_MANAGEMENT_RESERVE_POS)
}
}
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
//| Function to check if the maximum profit per day was exceeded |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2026-03-19 18:23:16 -05:00
__forceinline bool RiskMNormal_IsSuperatedProfit(const double value, const double saved_value, void* ptr)
2025-09-22 09:09:20 -05:00
{
2025-11-26 07:03:20 -05:00
return (account_status_curr_profit > value);
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
//| Boolean function to check if a loss was overcome |
//+------------------------------------------------------------------+
2026-03-19 18:23:16 -05:00
__forceinline bool RiskMNormal_IsSuperatedLoss(const double value, const double saved_value, void* ptr)
2025-09-22 09:09:20 -05:00
{
2025-11-26 07:03:20 -05:00
return (-account_status_curr_profit) > value;
2025-09-22 09:09:20 -05:00
}
2026-03-19 18:23:16 -05:00
//#define MQLARTICLES_RM_MULTI_EA_MODE
// En caso se decida usar este bot en varias cuentas use el define: MQLARTICLES_RM_MULTI_EA_MODE
//+------------------------------------------------------------------+
2026-01-28 13:02:22 -05:00
//| Funciones para obtener el tipo de fucnion a superar |
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
FuncionLossProfitSuperate CRiskManagemet::SuperateModeToFunctionProfit(ENUM_TYPE_LOSS_PROFIT type) const
{
2026-03-19 18:23:16 -05:00
#ifdef MQLARTICLES_RM_MULTI_EA_MODE
return CRiskManagemetBase::WraperIsSupertedProfit;
#else
2025-11-26 07:03:20 -05:00
return RiskMNormal_IsSuperatedProfit;
2026-03-19 18:23:16 -05:00
#endif
}
//+------------------------------------------------------------------+
2026-01-28 13:02:22 -05:00
// Si es GMLPO empty
2025-11-26 07:03:20 -05:00
FuncionLossProfitSuperate CRiskManagemet::SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type) const
{
2026-03-19 18:23:16 -05:00
#ifdef MQLARTICLES_RM_MULTI_EA_MODE
return type == LP_GMLPO ? (FuncionLossProfitSuperate)LossProfitEmptyFuncionSup : (FuncionLossProfitSuperate)CRiskManagemetBase::WraperIsSupertedLoss;
#else
2026-01-28 13:02:22 -05:00
return type == LP_GMLPO ? (FuncionLossProfitSuperate)LossProfitEmptyFuncionSup : (FuncionLossProfitSuperate)RiskMNormal_IsSuperatedLoss;
2026-03-19 18:23:16 -05:00
#endif
}
2025-11-26 07:03:20 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| SetLote function |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
void CRiskManagemet::SetLote(CGetLote *lote_ptr)
{
2026-01-28 13:02:22 -05:00
if(!CheckPointer(lote_ptr))
2025-09-22 09:09:20 -05:00
{
LogError("El puntero a CGetlote* es invalido", FUNCION_ACTUAL);
return;
}
2025-09-24 14:00:58 -05:00
//---
2026-01-29 08:19:03 -05:00
if(CheckPointer(get_lote) == POINTER_DYNAMIC) // is not equal to 0 (POINTER_INVALID)
2025-09-22 09:09:20 -05:00
{
2026-01-29 08:19:03 -05:00
//---
2025-09-22 09:09:20 -05:00
RemoveLogger(get_lote);
2026-02-01 16:17:01 -05:00
delete get_lote;
2026-01-29 08:19:03 -05:00
//---
2026-01-31 19:44:40 -05:00
AddLogger(lote_ptr);
2025-09-22 09:09:20 -05:00
this.get_lote = lote_ptr;
}
else //Si es null
{
2026-01-31 19:44:40 -05:00
AddLogger(lote_ptr);
2025-09-22 09:09:20 -05:00
this.get_lote = lote_ptr;
}
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function that will be executed once all the |
//| Maximum profit and loss have been added |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
void CRiskManagemet::EndAddProfitLoss()
{
if(is_init)
return;
LogInfo(StringFormat("The total size between maximum profit and loss is: %d", loss_profits_manager.SizeValids()), FUNCION_ACTUAL);
loss_profits_manager.SetValues();
2025-09-22 09:09:20 -05:00
this.is_init = true;
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function to add a maximum loss |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
bool CRiskManagemet::AddLoss(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool strict,
2025-09-22 09:09:20 -05:00
bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL)
{
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
if(_percentage <= 0.00)
{
2025-09-24 14:00:58 -05:00
LogWarning(StringFormat("Invalid percentage %.2f for lost profit %s, will not be added to the main array", _percentage, EnumToString(type)), FUNCION_ACTUAL);
2025-11-26 07:03:20 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
if(_percentage > 100.00 && _mode == percentage)
{
2025-09-24 14:00:58 -05:00
LogWarning(StringFormat("Percentage %.2f for %s exceeds 100%%, limiting to 100%%", _percentage, EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
_percentage = 100.00;
}
2025-09-24 14:00:58 -05:00
//---
const int index = int(type);
2026-01-28 13:02:22 -05:00
if(index > 5 || index < 0)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
LogError(StringFormat("Invalid maximum loss type: %s (index: %d)", EnumToString(type), index), FUNCION_ACTUAL);
2025-11-26 07:03:20 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
//--- Creamos
2025-11-26 07:03:20 -05:00
CLossProfit* new_loss = NULL;
2026-01-28 13:02:22 -05:00
CRiskManagemetBase* basic_risk = ((CRiskManagemetBase*)&this);
// Por tipo
2025-11-26 07:03:20 -05:00
if(_mode == money)
2026-01-28 13:02:22 -05:00
{
switch(index)
{
case LP_GMLPO:
{
2026-01-31 19:44:40 -05:00
CLossLossGmlpo<CLossProfitMoney<CLossProfit>>* temp = new CLossLossGmlpo<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MDL:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxDaily<CLossProfitMoney<CLossProfit>>* temp = new CLossLossMaxDaily<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MWL:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxWeekly<CLossProfitMoney<CLossProfit>>* temp = new CLossLossMaxWeekly<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MML:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxMon<CLossProfitMoney<CLossProfit>>* temp = new CLossLossMaxMon<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_ML:
{
2026-01-31 19:44:40 -05:00
CLossLossMax<CLossProfitMoney<CLossProfit>>* temp = new CLossLossMax<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_ML_PICO:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxDesdeArriba<CLossProfitMoney<CLossProfit>>* temp = new CLossLossMaxDesdeArriba<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
}
}
else // percentage
{
switch(index)
{
case LP_GMLPO:
{
2026-01-31 19:44:40 -05:00
CLossLossGmlpo<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossGmlpo<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MDL:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxDaily<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossMaxDaily<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MWL:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxWeekly<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossMaxWeekly<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_MML:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxMon<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossMaxMon<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_ML:
{
2026-01-31 19:44:40 -05:00
CLossLossMax<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossMax<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
case LP_ML_PICO:
{
2026-01-31 19:44:40 -05:00
CLossLossMaxDesdeArriba<CLossProfitPercentage<CLossProfit>>* temp = new CLossLossMaxDesdeArriba<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, strict, SuperateModeToFunctionLoss(type));
new_loss = temp;
break;
}
}
}
2025-09-22 09:09:20 -05:00
//---
2025-11-26 07:03:20 -05:00
if(!loss_profits_manager.Add(new_loss))
{
delete new_loss;
return false;
}
2025-09-22 09:09:20 -05:00
//---
2025-11-26 07:03:20 -05:00
new_loss.SetPtrSuperated(GetPtrLossProfits());
//---
2026-01-28 13:02:22 -05:00
if(is_dynamic && percentages_to_activate != NULL && risks_to_be_applied != NULL)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
if(!isCachedModifier[index])
2025-09-22 09:09:20 -05:00
{
new_loss.SetDynamic(percentages_to_activate, risks_to_be_applied, this.chosen_balance);
2025-09-24 14:00:58 -05:00
LogInfo(StringFormat("Dynamic maximum loss %s successfully configured", EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
else
{
2025-09-24 14:00:58 -05:00
LogError(StringFormat("Maximum loss %s already has an active modifier", EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
}
else
{
2026-01-28 13:02:22 -05:00
LogInfo(StringFormat("Maximum loss %s added: %.2f%s", EnumToString(type), _percentage, (_mode == money ? "money" : "%")), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
2025-11-26 07:03:20 -05:00
return true;
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function to add a maximum gain |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
bool CRiskManagemet::AddProfit(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool is_strict_,
bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
if(_percentage <= 0.00)
{
2025-09-24 14:00:58 -05:00
LogWarning(StringFormat("Invalid percentage %.2f for maximum gain %s, will not be added to the main array", _percentage, EnumToString(type)), FUNCION_ACTUAL);
2025-11-26 07:03:20 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
if(_percentage > 100.00 && _mode == percentage)
{
2025-09-24 14:00:58 -05:00
LogWarning(StringFormat("Percentage %.2f for %s exceeds 100%%, limiting to 100%%", _percentage, EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
_percentage = 100.00;
}
2025-09-24 14:00:58 -05:00
//---
const int index = int(type);
2026-01-28 13:02:22 -05:00
if(index < 6 || index > 10) // Profits: LP_MDP=6 a LP_MP_BAJO=10
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
LogError(StringFormat("Invalid maximum gain type: %s (index: %d)", EnumToString(type), index), FUNCION_ACTUAL);
2025-11-26 07:03:20 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2026-01-28 13:02:22 -05:00
//--- Creamos
2025-11-26 07:03:20 -05:00
CLossProfit* new_profit = NULL;
2026-01-28 13:02:22 -05:00
CRiskManagemetBase* basic_risk = ((CRiskManagemetBase*)&this);
2025-09-22 09:09:20 -05:00
2026-01-28 13:02:22 -05:00
// Ajustar <EFBFBD>ndice para el switch (restar 6 para que LP_MDP=0, LP_MWP=1, etc.)
const int switch_index = index - 6;
// Por tipo
if(_mode == money)
{
switch(switch_index)
{
case 0: // LP_MDP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxDaily<CLossProfitMoney<CLossProfit>>* temp = new CLossProfitMaxDaily<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 1: // LP_MWP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxWeekly<CLossProfitMoney<CLossProfit>>* temp = new CLossProfitMaxWeekly<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 2: // LP_MMP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxMon<CLossProfitMoney<CLossProfit>>* temp = new CLossProfitMaxMon<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 3: // LP_MP
{
2026-01-31 19:44:40 -05:00
CLossProfitMax<CLossProfitMoney<CLossProfit>>* temp = new CLossProfitMax<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 4: // LP_MP_BAJO
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxDesdeBaajo<CLossProfitMoney<CLossProfit>>* temp = new CLossProfitMaxDesdeBaajo<CLossProfitMoney<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
}
}
else // percentage
{
switch(switch_index)
{
case 0: // LP_MDP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxDaily<CLossProfitPercentage<CLossProfit>>* temp = new CLossProfitMaxDaily<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 1: // LP_MWP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxWeekly<CLossProfitPercentage<CLossProfit>>* temp = new CLossProfitMaxWeekly<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 2: // LP_MMP
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxMon<CLossProfitPercentage<CLossProfit>>* temp = new CLossProfitMaxMon<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 3: // LP_MP
{
2026-01-31 19:44:40 -05:00
CLossProfitMax<CLossProfitPercentage<CLossProfit>>* temp = new CLossProfitMax<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
case 4: // LP_MP_BAJO
{
2026-01-31 19:44:40 -05:00
CLossProfitMaxDesdeBaajo<CLossProfitPercentage<CLossProfit>>* temp = new CLossProfitMaxDesdeBaajo<CLossProfitPercentage<CLossProfit>>(false, basic_risk);
2026-01-28 13:02:22 -05:00
temp.Init(_percentage, _applied, is_strict_, SuperateModeToFunctionProfit(type));
new_profit = temp;
break;
}
}
}
//---
2025-11-26 07:03:20 -05:00
if(!loss_profits_manager.Add(new_profit))
{
delete new_profit;
return false;
}
2025-09-22 09:09:20 -05:00
//---
2025-11-26 07:03:20 -05:00
new_profit.SetPtrSuperated(GetPtrLossProfits());
//---
2026-01-28 13:02:22 -05:00
if(is_dynamic && percentages_to_activate != NULL && risks_to_be_applied != NULL)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
if(!isCachedModifier[index])
2025-09-22 09:09:20 -05:00
{
new_profit.SetDynamic(percentages_to_activate, risks_to_be_applied, this.chosen_balance);
2025-09-24 14:00:58 -05:00
LogInfo(StringFormat("Maximum dynamic gain %s successfully set", EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
else
{
2025-09-24 14:00:58 -05:00
LogError(StringFormat("Maximum gain %s already has an active modifier", EnumToString(type)), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
}
else
{
2026-01-28 13:02:22 -05:00
LogInfo(StringFormat("Maximum gain %s added: %.2f%s", EnumToString(type), _percentage, (_mode == money ? "money" : "%")), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
}
2026-01-28 13:02:22 -05:00
2025-11-26 07:03:20 -05:00
return true;
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Function to add a risk modifier |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-12-21 12:41:35 -05:00
bool CRiskManagemet::AddModificator(CExtraModifications *modificator)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
//--- Check pointer
if(!CheckPointer(modificator))
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
LogError("The risk modifier is null-invalid", FUNCION_ACTUAL);
2025-12-21 12:41:35 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//--- Property to be modified is invalid ?
2026-01-28 13:02:22 -05:00
const int loss_or_profit = modificator.MaximumProfitOrLossAModify();
2025-09-24 14:00:58 -05:00
if(loss_or_profit == WRONG_VALUE)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
LogError("The risk modifier does not modify any risk.", FUNCION_ACTUAL);
2025-12-21 12:41:35 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//--- Property to modify is "empty" ?
if(loss_profits_manager[loss_or_profit].IsEmpty())
2025-09-22 09:09:20 -05:00
{
2026-01-28 13:02:22 -05:00
LogError(StringFormat("The property to be modified %d is not defined", loss_or_profit), FUNCION_ACTUAL);
2025-12-21 12:41:35 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//--- Is the loss_or_profit already dynamic ?
if(loss_profits_manager[loss_or_profit].IsDynamicMode())
2025-09-22 09:09:20 -05:00
{
2026-01-28 13:02:22 -05:00
LogError(StringFormat("The property to be modified %d is already dynamic", loss_or_profit), FUNCION_ACTUAL);
2025-12-21 12:41:35 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
//---
2025-09-24 14:00:58 -05:00
const int index = int(loss_or_profit);
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
if(isCachedModifier[index])
2025-09-22 09:09:20 -05:00
{
2026-01-28 13:02:22 -05:00
LogWarning(StringFormat("The property to be modified %d is already being modified", loss_or_profit), FUNCION_ACTUAL);
2025-12-21 12:41:35 -05:00
return false;
2025-09-22 09:09:20 -05:00
}
2025-09-24 14:00:58 -05:00
//---
modificator.SetPointer(loss_profits_manager[loss_or_profit]);
2025-09-22 09:09:20 -05:00
ModfierInitInfo init;
init.balance = this.chosen_balance;
init.magic = this.magic_number;
modificator.OnInitModifier(init);
2025-09-24 14:00:58 -05:00
LogCaution(StringFormat("The modifier '%s' is being added\nThis will modify the property '%s'", modificator.Name(), loss_profits_manager[loss_or_profit].Name()), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
//--- Add
2026-03-05 12:10:29 -05:00
m_hooks.Add(modificator); // We use the fast version (we already did all the checkpointer checks etc.)
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
isCachedModifier[index] = true;
2025-12-21 12:41:35 -05:00
return true;
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
2025-12-21 12:41:35 -05:00
//| |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-12-21 12:41:35 -05:00
bool CRiskManagemet::AddRmHook(IRiskManagementHook *hook)
2025-09-22 09:09:20 -05:00
{
2025-12-21 12:41:35 -05:00
//--- Check pointer
if(!CheckPointer(hook))
{
LogError("The RM Hook is null-invalid", FUNCION_ACTUAL);
return false;
}
//--- Add
2026-03-05 12:10:29 -05:00
m_hooks.Add(hook); // We use the fast version (we already did all the checkpointer checks etc.)
2025-12-21 12:41:35 -05:00
//---
return true;
2025-09-22 09:09:20 -05:00
}
2025-12-21 12:41:35 -05:00
2025-09-22 09:09:20 -05:00
//+----------------------------------------------------------------------------------+
//| Get the ideal stop loss based on a specified lot and the maximum loss per trade |
//+----------------------------------------------------------------------------------+
long CRiskManagemet::GetSL(ENUM_ORDER_TYPE type, double entry_price, ulong deviation, ulong stop_limit)
{
if(loss_profits_manager[LP_GMLPO].IsEmpty())
{
2025-09-24 14:00:58 -05:00
LogCriticalError("GMLPO not configured: Stop Loss cannot be calculated", FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
Remover();
return 0;
}
double lot;
2026-01-29 08:19:03 -05:00
long sl = get_lote.MoneyToPoints(type, loss_profits_manager[LP_GMLPO].GetValue(), entry_price, lot, deviation, stop_limit);
2025-09-24 14:00:58 -05:00
//If there is an error or the SL is invalid, it is printed in CGetLote
2025-09-22 09:09:20 -05:00
return sl;
}
//+-----------------------------------------------------------------------------------------------+
//| Function to obtain the ideal lot based on the maximum loss per operation and the stop loss |
//+-----------------------------------------------------------------------------------------------+
double CRiskManagemet::GetLote(ENUM_ORDER_TYPE type, double entry_price, ulong deviation, ulong stop_limit)
{
if(loss_profits_manager[LP_GMLPO].IsEmpty())
{
2025-09-24 14:00:58 -05:00
LogCriticalError("GMLPO not configured: unable to calculate lot size", FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
Remover();
this.lote = 0.00;
return this.lote;
}
if(this.type_get_lot == GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION)
{
2025-09-24 14:00:58 -05:00
double MaxLote = get_lote.GetMaxLote(type, entry_price, deviation, stop_limit); // We do not check max batch data that is done in GetLote... of CGetLote
2025-09-22 09:09:20 -05:00
this.lote = get_lote.GetLoteByRiskPerOperationAndSL(MaxLote, loss_profits_manager[LP_GMLPO].GetValue(), this.nmlpo, this.StopLoss);
}
else
2025-09-24 14:00:58 -05:00
this.lote = get_lote.GetLoteByRiskPerOperation(type, loss_profits_manager[LP_GMLPO].GetValue(), entry_price, deviation, stop_limit); // If it is less than the minimum batch, a message is printed in CGetLote
2025-09-22 09:09:20 -05:00
return this.lote;
}
//+------------------------------------------------------------------+
//| Function to update profits |
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
void CRiskManagemet::UpdateProfit(const double profit, const double acc_total_profit)
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
//--- Update profits
2025-11-26 07:03:20 -05:00
this.monthly_profit += profit;
this.daily_profit += profit;
this.weekly_profit += profit;
this.gross_profit += profit;
2025-09-22 09:09:20 -05:00
2025-11-26 07:03:20 -05:00
//---
if(IsInfoLogEnabled())
{
FastLog(FUNCION_ACTUAL, INFO_TEXT, "New profits:");
Print("Daily Profit: ", this.daily_profit);
Print("Gross profit: ", this.gross_profit);
Print("Weekly Profit: ", this.weekly_profit);
Print("Monthly Profit: ", this.monthly_profit);
2025-09-22 09:09:20 -05:00
}
//---
2025-11-26 07:03:20 -05:00
this.account_profit = acc_total_profit; // Set Account total profit
}
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-09-24 14:00:58 -05:00
//| Initial profit update |
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
void CRiskManagemet::OnNewProfit(const ROnOpenClosePosition & profit, const datetime curr_time)
2025-09-22 09:09:20 -05:00
{
2026-01-29 10:49:55 -05:00
//---Date
2026-01-31 19:44:40 -05:00
this.last_day_time = account_status.last_day_time;
this.last_weekly_time = account_status.last_weekly_time;
this.last_monthly_time = account_status.last_monthly_time;
2026-01-29 10:49:55 -05:00
this.init_trade_time = this.magic_number != NOT_MAGIC_NUMBER ? curr_time : D'1972.01.01 00:00';
2025-09-22 09:09:20 -05:00
2025-09-24 14:00:58 -05:00
//--- Set internal profits
2025-09-22 09:09:20 -05:00
this.account_profit = GetNetProfitSince(true, this.magic_number, D'1972.01.01 00:00');
this.daily_profit = GetNetProfitSince((this.magic_number == NOT_MAGIC_NUMBER ? true : false), this.magic_number, this.last_day_time);
this.weekly_profit = GetNetProfitSince((this.magic_number == NOT_MAGIC_NUMBER ? true : false), this.magic_number, this.last_weekly_time);
this.gross_profit = GetNetProfitSince((this.magic_number == NOT_MAGIC_NUMBER ? true : false), this.magic_number, this.init_trade_time);
this.monthly_profit = GetNetProfitSince((this.magic_number == NOT_MAGIC_NUMBER ? true : false), this.magic_number, this.last_monthly_time);
2025-09-24 14:00:58 -05:00
//--- Info
2025-09-22 09:09:20 -05:00
if(IsInfoLogEnabled())
{
FastLog(FUNCION_ACTUAL, INFO_TEXT, "New profits:");
Print("Daily Profit: ", this.daily_profit);
Print("Gross profit: ", this.gross_profit);
Print("Weekly Profit: ", this.weekly_profit);
Print("Monthly Profit: ", this.monthly_profit);
}
}
//+------------------------------------------------------------------+
//| OnTradeTransaction Event |
//+------------------------------------------------------------------+
2025-12-21 12:41:35 -05:00
void CRiskManagemet::OnOpenClosePosition(const ROnOpenClosePosition & pos)
2025-09-22 09:09:20 -05:00
{
if(pos.deal_entry_type == DEAL_ENTRY_IN && (this.magic_number == pos.position.magic || this.magic_number == NOT_MAGIC_NUMBER))
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
LogInfo(StringFormat("Position %I64u added to internal tickets", pos.position.ticket), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
//--- Activate the flag
2025-09-22 09:09:20 -05:00
this.positions_open = true;
//--- Execute the virtual function
2025-09-22 09:09:20 -05:00
OnOpenTrade();
//--- If there are modifiers then execute the OnOpenPosition function
2026-03-05 12:10:29 -05:00
if(m_hooks.SizeOnOpenPosition() > 0)
2025-09-22 09:09:20 -05:00
{
2026-02-14 17:00:45 -05:00
static ModifierOnOpenCloseStruct new_mod;
2025-09-22 09:09:20 -05:00
// Position
2025-09-22 09:09:20 -05:00
new_mod.position = pos.position;
// Profits
new_mod.profit_diario = this.daily_profit;
new_mod.profit_mensual = this.monthly_profit;
new_mod.profit_semanal = this.weekly_profit;
new_mod.profit_total = this.gross_profit;
// Deal
new_mod.deal_profit = pos.deal_profit;
new_mod.deal_ticket = pos.deal_ticket;
new_mod.deal_entry_type = pos.deal_entry_type;
new_mod.deal_reason = pos.deal_reason;
2025-12-21 12:41:35 -05:00
m_hooks.OnOpenPosition(new_mod);
2025-09-22 09:09:20 -05:00
}
//--- Add the operation to positions opened by the Magic
2025-09-22 09:09:20 -05:00
AddArrayNoVerification2(open_positions, pos.position, RISK_MANAGEMENT_RESERVE_POS);
return;
}
if(pos.deal_entry_type != DEAL_ENTRY_OUT)
return;
2025-11-26 07:03:20 -05:00
//---
2025-09-22 09:09:20 -05:00
if(RemoveIndexFromAnArrayOfPositions(this.open_positions, pos.position.ticket, RISK_MANAGEMENT_RESERVE_POS))
{
//--- Logs
2025-09-22 09:09:20 -05:00
LogCaution(StringFormat("Position with ticket %I64u has been closed", pos.position.ticket), FUNCION_ACTUAL);
2025-09-24 14:00:58 -05:00
LogInfo(StringFormat("Total number of positions for magic number %u is: %d", this.magic_number, this.GetPositionsTotal()), FUNCION_ACTUAL);
2025-09-22 09:09:20 -05:00
//--- Update internal profits
2025-11-26 07:03:20 -05:00
UpdateProfit(pos.position.profit, pos.account_profit_total);
2025-09-22 09:09:20 -05:00
//--- Update risks (dynamic)
loss_profits_manager.CheckDynamic();
2025-09-22 09:09:20 -05:00
//--- If there are no more positions, deactivate the flag
2025-09-22 09:09:20 -05:00
if(GetPositionsTotal() == 0)
this.positions_open = false;
//--- Execute the function that runs every time a position closes
2025-09-22 09:09:20 -05:00
OnCloseTrade();
//--- If there are modifiers, execute OnOpenClosePosition
2026-03-05 12:10:29 -05:00
if(m_hooks.SizeOnClosePosition() > 0)
2025-09-22 09:09:20 -05:00
{
2026-02-14 17:00:45 -05:00
static ModifierOnOpenCloseStruct new_mod;
2025-09-22 09:09:20 -05:00
// Profit
new_mod.profit_diario = this.daily_profit;
new_mod.profit_mensual = this.monthly_profit;
new_mod.profit_semanal = this.weekly_profit;
new_mod.profit_total = this.gross_profit;
// Position
2025-09-22 09:09:20 -05:00
new_mod.position = pos.position;
// Deal
new_mod.deal_profit = pos.deal_profit;
new_mod.deal_ticket = pos.deal_ticket;
new_mod.deal_entry_type = pos.deal_entry_type;
new_mod.deal_reason = pos.deal_reason;
2025-12-21 12:41:35 -05:00
m_hooks.OnClosePosition(new_mod);
2025-09-22 09:09:20 -05:00
}
//--- Set the new GMLPO value
loss_profits_manager[LP_GMLPO].Set(); // Empty check is done internally
2025-09-22 09:09:20 -05:00
}
else // In case a position closes but it's not from the current bot, we need to update profits (Possibly it was closed by the trader or another bot)
2025-09-22 09:09:20 -05:00
{
//--- Udpate profit
2025-11-26 07:03:20 -05:00
UpdateProfit(pos.position.profit, pos.account_profit_total);
2025-09-22 09:09:20 -05:00
//--- Set GMLPO
loss_profits_manager[LP_GMLPO].Set(); // Empty check is done internally
2025-09-22 09:09:20 -05:00
}
// Partials are not necessary since we did that to ensure trans.position profit also counts the partial close, the other class already does that.
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
//| Function that runs every new day |
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
void CRiskManagemet::OnNewDay(const datetime curr_time)
2025-09-22 09:09:20 -05:00
{
this.daily_profit = 0.00;
2026-04-19 11:47:45 -05:00
this.last_day_time = curr_time;
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
//| Function that runs every new week |
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
void CRiskManagemet::OnNewWeek(const datetime curr_time)
2025-09-22 09:09:20 -05:00
{
2026-04-19 11:47:45 -05:00
this.last_weekly_time = curr_time;
this.weekly_profit = 0.00;
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
//| Function that runs every new month |
//+------------------------------------------------------------------+
2026-02-14 17:00:45 -05:00
void CRiskManagemet::OnNewMonth(const datetime curr_time)
2025-09-22 09:09:20 -05:00
{
2026-04-19 11:47:45 -05:00
this.last_monthly_time = curr_time;
this.monthly_profit = 0.00;
2025-09-22 09:09:20 -05:00
}
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
//| Function to obtain the value of a loss or gain based |
//| on the applied value (balance, free margin, equity, net profit) |
//+------------------------------------------------------------------+
2026-01-28 13:02:22 -05:00
double CRiskManagemet::GetValorWithApplied(double percentage_, int type, ENUM_APPLIED_PERCENTAGES applied) const
2025-09-22 09:09:20 -05:00
{
switch(applied)
{
case Balance:
2025-11-26 07:03:20 -05:00
return ((percentage_ * 0.01) * AccountInfoDouble(ACCOUNT_BALANCE));
2025-09-22 09:09:20 -05:00
case ganancianeta:
{
if(this.account_profit <= 0.0000001)
2025-09-22 09:09:20 -05:00
{
LogError(StringFormat("The total profit of the account which is %+.2f is invalid or negative", this.account_profit), FUNCION_ACTUAL);
return 0;
}
else
2025-11-26 07:03:20 -05:00
return ((percentage_ * 0.01) * this.account_profit);
2025-09-22 09:09:20 -05:00
}
case free_margin:
{
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0.00000000001)
2025-09-22 09:09:20 -05:00
{
LogError(StringFormat("Free margin of %+.2f is invalid", AccountInfoDouble(ACCOUNT_MARGIN_FREE)), FUNCION_ACTUAL);
return 0;
}
else
2025-11-26 07:03:20 -05:00
return ((percentage_ * 0.01) * AccountInfoDouble(ACCOUNT_MARGIN_FREE));
2025-09-22 09:09:20 -05:00
}
case equity:
2025-11-26 07:03:20 -05:00
return ((percentage_ * 0.01) * AccountInfoDouble(ACCOUNT_EQUITY));
2025-09-22 09:09:20 -05:00
default:
LogFatalError(StringFormat("It was not found that: %s be part of the allowed enumeration", EnumToString(applied)), FUNCION_ACTUAL);
}
//---
return 0.00;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//--- Define dangerous, if disabled it could increase the risk strongly, for safety reasons do not comment on it
#define RISK_MANAGEMENT_UPDATE_CHOSEN_BALANCE_DEPOSIT_WITDRAWAL
//---
2026-02-14 17:00:45 -05:00
void CRiskManagemet::OnWithdrawalDeposit(const double value) override
2025-09-22 09:09:20 -05:00
{
2025-09-24 14:00:58 -05:00
// Function that is executed every time a device or profit occurs
//--- Uodate
#ifdef RISK_MANAGEMENT_UPDATE_CHOSEN_BALANCE_DEPOSIT_WITDRAWAL
this.chosen_balance += value;
loss_profits_manager.SetNewChossenBalanceForDynamicsAndSet(chosen_balance, true);
#else
loss_profits_manager.SetValuesAndDynamic();
#endif
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
//| Clase de gestion de riesgo normal |
//+------------------------------------------------------------------+
class CRiskManagemetPersonal : public CRiskManagemet
{
public:
2026-01-31 19:44:40 -05:00
CRiskManagemetPersonal(ulong _magic, ENUM_GET_LOT _get)
: CRiskManagemet(_magic, _get) { }
2025-09-22 09:09:20 -05:00
inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement() const override final { return risk_mode_personal_account; }
2025-09-24 14:00:58 -05:00
void SetGeneralParameters(MqlParam &params[]) override { } // No extra parameters required
2025-09-22 09:09:20 -05:00
};
//+------------------------------------------------------------------+
//| Clase de gestion de riesgo para propfirms que usen riesgo |
//| Diario dinamico |
//+------------------------------------------------------------------+
//---
class CRiskManagemetPropFirm : public CRiskManagemet
{
private:
double account_balance_propfirm; // Balance incial del challenge
2025-11-26 07:03:20 -05:00
//---
2026-01-28 13:02:22 -05:00
double GetValorWithApplied(double percentage_, int type, ENUM_APPLIED_PERCENTAGES applied) const override;
2025-11-26 07:03:20 -05:00
FuncionLossProfitSuperate SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type) const override;
2025-09-22 09:09:20 -05:00
public:
2026-01-31 19:44:40 -05:00
CRiskManagemetPropFirm(ulong _magic, ENUM_GET_LOT type_get_lot_)
: CRiskManagemet(_magic, type_get_lot_) { }
2025-09-22 09:09:20 -05:00
//--- General
void SetGeneralParameters(MqlParam &params[]) override;
2025-11-26 07:03:20 -05:00
__forceinline double GetAccountBalancePropFirm() const { return account_balance_propfirm; }
2025-09-22 09:09:20 -05:00
//---
inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement() const override final { return risk_mode_propfirm_dynamic_daiy_loss; }
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-03-19 18:23:16 -05:00
inline bool RiskMPropFirmFN_FTMO_IsSuperatedLoss(const double value, const double saved_value, void* ptr)
2025-09-22 09:09:20 -05:00
{
2025-11-26 07:03:20 -05:00
return AccountInfoDouble(ACCOUNT_EQUITY) < (((CRiskManagemetPropFirm*)ptr).GetAccountBalancePropFirm() - saved_value);
2025-09-22 09:09:20 -05:00
}
//+------------------------------------------------------------------+
2025-11-26 07:03:20 -05:00
FuncionLossProfitSuperate CRiskManagemetPropFirm::SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type) const
2025-09-22 09:09:20 -05:00
{
if(type == LP_ML)
return RiskMPropFirmFN_FTMO_IsSuperatedLoss;
2025-11-26 07:03:20 -05:00
return CRiskManagemet::SuperateModeToFunctionLoss(type);
2025-09-22 09:09:20 -05:00
}
2025-11-26 07:03:20 -05:00
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
void CRiskManagemetPropFirm::SetGeneralParameters(MqlParam & params[]) override
{
account_balance_propfirm = params[0].double_value;
this.chosen_balance = account_balance_propfirm;
}
//+------------------------------------------------------------------+
2026-01-28 13:02:22 -05:00
double CRiskManagemetPropFirm::GetValorWithApplied(double percentage_, int type, ENUM_APPLIED_PERCENTAGES applied) const override
2025-09-22 09:09:20 -05:00
{
if(type == LP_ML || type == LP_MDL)
2025-11-26 07:03:20 -05:00
return account_balance_propfirm * (percentage_ * 0.01);
2025-09-22 09:09:20 -05:00
return CRiskManagemet::GetValorWithApplied(percentage_, type, applied);
}
//+------------------------------------------------------------------+
//| Clase manager para poder obtener un puntero |
//| En base al tipo de gestion de riesgo |
//+------------------------------------------------------------------+
class CRiskPointer
{
private:
RiskParams parametros[];
ulong magic;
ENUM_GET_LOT type_get_lot;
public:
2026-01-31 19:44:40 -05:00
CRiskPointer(ulong _magic, ENUM_GET_LOT _Get);
2025-09-22 09:09:20 -05:00
void SetPropirm(double account_balance_propfirm);
CRiskManagemet* GetRiskPointer(ENUM_MODE_RISK_MANAGEMENT mode);
};
//+------------------------------------------------------------------+
//| Obtener el puntero de CRiskManagement |
//+------------------------------------------------------------------+
CRiskManagemet* CRiskPointer::GetRiskPointer(ENUM_MODE_RISK_MANAGEMENT mode)
{
CRiskManagemet *new_risk = NULL;
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
switch(mode)
{
2026-01-07 11:00:48 -05:00
//---
2025-09-22 09:09:20 -05:00
case risk_mode_propfirm_dynamic_daiy_loss:
{
2026-01-31 19:44:40 -05:00
new_risk = new CRiskManagemetPropFirm(this.magic, this.type_get_lot);
2025-09-22 09:09:20 -05:00
if(ArraySize(parametros[1].params) < 1)
{
2026-01-28 13:02:22 -05:00
Print(FUNCION_ACTUAL, ": Critical error | The parameter array for prop firm mode has not been populated.");
2025-09-22 09:09:20 -05:00
Remover();
break;
}
new_risk.SetGeneralParameters(parametros[1].params);
break;
}
2026-01-07 11:00:48 -05:00
//---
case risk_mode_personal_account:
{
2026-01-31 19:44:40 -05:00
new_risk = new CRiskManagemetPersonal(this.magic, this.type_get_lot);
2026-01-07 11:00:48 -05:00
break;
}
//---
2025-09-22 09:09:20 -05:00
default:
2026-01-28 13:02:22 -05:00
Print(FUNCION_ACTUAL, ": Critical error | The type: ", EnumToString(mode), " It is invalid");
2025-09-22 09:09:20 -05:00
break;
}
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
if(CheckPointer(new_risk) == POINTER_INVALID)
{
2026-01-28 13:02:22 -05:00
Print(FUNCION_ACTUAL, ": Critical error | The pointer to CRiskManagement* is invalid");
2025-09-22 09:09:20 -05:00
return NULL;
}
2025-09-24 14:00:58 -05:00
//---
2025-09-22 09:09:20 -05:00
return new_risk;
}
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
2026-01-31 19:44:40 -05:00
CRiskPointer::CRiskPointer(ulong _magic, ENUM_GET_LOT _Get)
2025-09-22 09:09:20 -05:00
{
this.magic = _magic;
this.type_get_lot = _Get;
ArrayResize(parametros, 2);
parametros[0].mode = risk_mode_personal_account;
parametros[1].mode = risk_mode_propfirm_dynamic_daiy_loss;
}
//+------------------------------------------------------------------+
//| Para setear el modo de gestion de riesgo propfirm |
//+------------------------------------------------------------------+
void CRiskPointer::SetPropirm(double account_balance_propfirm)
{
ArrayFree(parametros[1].params);
ArrayResize(parametros[1].params, 2);
parametros[1].params[0].double_value = account_balance_propfirm;
}
//+------------------------------------------------------------------+
2025-11-10 09:33:30 -05:00
#endif // MQLARTICLES_RM_RISKMANAGEMENT_MQH
2025-09-22 09:09:20 -05:00
//+------------------------------------------------------------------+
2025-12-21 12:41:35 -05:00
//+------------------------------------------------------------------+
2025-09-22 09:09:20 -05:00