2026-07-28 09:08:36 -05:00
//+------------------------------------------------------------------+
//| 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
# ifndef MQLARTICLES_RM_RISKMANAGEMENT_MQH
# define MQLARTICLES_RM_RISKMANAGEMENT_MQH
# include "RM_Hooks.mqh"
/*
ADVERTENCIA al momeneto de añadir items a esta clase , esta si son dinamicos las eliminara cuidado , como un modificador .
WARNING when adding items to this class , if they are dynamic , it will remove them carefully , as a modifier .
* /
//open_positions
//+------------------------------------------------------------------+
//| 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
double lote ; //Last recorded lot
//--- variables to store the values of the maximum losses
double nmlpo ; //last expected loss
bool is_init ; //flag indicating if it is init
//--- variables that store percentages and enumeration, which will be used for the subsequent calculation of losses
CLossProfitManager loss_profits_manager ;
//--- Modifiers
CRiskManagemeHookManager m_hooks ;
bool isCachedModifier [ LOSS_PROFIT_COUNT ] ;
//--- general function to assign values to loss variables
virtual double GetValorWithApplied ( double percentage_ , int type , ENUM_APPLIED_PERCENTAGES applied ) const override ;
//--- Update profits function
void UpdateProfit ( const double profit , const double acc_total_profit ) ;
virtual void OnOpenTrade ( ) { }
virtual void OnCloseTrade ( ) { }
//---
virtual FuncionLossProfitSuperate SuperateModeToFunctionProfit ( ENUM_TYPE_LOSS_PROFIT type ) const ;
virtual FuncionLossProfitSuperate SuperateModeToFunctionLoss ( ENUM_TYPE_LOSS_PROFIT type ) const ;
//---
virtual void * GetPtrLossProfits ( ) { return & this ; }
public :
//--- Constructor and Destructor
CRiskManagemet ( const ulong _magic , const ENUM_GET_LOT type_get_lot_ ) ;
~ CRiskManagemet ( ) ;
//--- Set
void EndAddProfitLoss ( ) ;
virtual void SetGeneralParameters ( MqlParam & params [ ] ) = 0 ;
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
const CLossProfit * const GetLossOrProfit ( const ENUM_TYPE_LOSS_PROFIT _type ) const { return loss_profits_manager [ _type ] ; }
//--- Superated funcionts
const CLossProfitManager * GetLossProfitManager ( ) const { return & loss_profits_manager ; }
//--- Add
// 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 ) ; }
// Loss
bool AddLoss ( double _percentage , ENUM_APPLIED_PERCENTAGES _applied , ENUM_RISK_CALCULATION_MODE _mode , ENUM_TYPE_LOSS_PROFIT type , bool strict ,
bool is_dynamic = false , string percentages_to_activate = NULL , string risks_to_be_applied = NULL ) ; // Loss
// Profit
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
//--- Functions inherited from CAccountGestor
// Basic events
void OnNewDay ( const datetime curr_time ) override ;
void OnNewWeek ( const datetime curr_time ) override ;
void OnNewMonth ( const datetime curr_time ) override ;
// Account status
void OnNewProfit ( const RAccountProfit & profit , const datetime curr_time ) override ;
void OnClosePosition ( const ROnClosePosition & pos , const int global_pos_index ) override ;
void OnOpenPosition ( const ROnOpenPosition & pos ) override ;
void OnWithdrawalDeposit ( const double value ) override ;
void OnInitNewPos ( const ROnOpenPosition & position ) override ;
//--- 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 ; }
//---
__forceinline double LastLotSize ( ) const { return this . lote ; }
__forceinline double Nlmpo ( ) const { return this . nmlpo ; }
//--- Risk management type
virtual inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement ( ) const = 0 ;
} ;
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CRiskManagemet : : CRiskManagemet ( const ulong _magic , const ENUM_GET_LOT type_get_lot_ )
: CRiskManagemetBase ( _magic )
{
//---
this . chosen_balance = AccountInfoDouble ( ACCOUNT_BALANCE ) ;
for ( int i = 0 ; i < LOSS_PROFIT_COUNT ; i + + )
{
isCachedModifier [ i ] = false ;
}
//---
const bool ac_i = account_status . IsInitialized ( ) ;
//--- Registramos la clase para recibir eventos
// Basicos
CBasicEvents : : Register ( & this , BASICEVENT_REG_ALL_FLAG ) ; // OnNewDay, Week, Month
// Para trabajar con las posiciones
account_status . RegisterEvents ( & this ,
( ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT |
ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT |
( ac_i ? 0 : ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS )
) ) ;
//---
this . type_get_lot = type_get_lot_ ;
//---
AddLogger ( loss_profits_manager ) ; // Si pertenece..
AddLogger ( m_hooks ) ;
//---
if ( ac_i ) // Se crea luego de que se inicie
{
g_account_open_positions . GetPositionsFromMagic ( _magic , m_positions , m_positions_size ) ;
}
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRiskManagemet : : ~ CRiskManagemet ( )
{
//---
CleanItems ( " RiskManagement " ) ;
//--- Registramos la clase para recibir eventos
// Basicos
if ( CBasicEvents : : IsActive ( ) )
CBasicEvents : : Unregister ( & this , BASICEVENT_REG_ALL_FLAG ) ; // OnNewDay, Week, Month
// Account status
if ( account_status . IsActive ( ) )
{
// Para trabajar con las posiciones
account_status . UnregisterEvents ( & this ,
( ACCOUNT_STATUS_REG_FLAG_ON_OPEN_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_CLOSE_POSITION |
ACCOUNT_STATUS_REG_FLAG_ON_WITHDRAWAL_DEPOSIT |
ACCOUNT_STATUS_REG_FLAG_ON_NEW_PROFIT |
ACCOUNT_STATUS_REG_FLAG_ON_INIT_NEW_POS )
) ;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnInitNewPos ( const ROnOpenPosition & position )
{
if ( position . position . magic = = this . magic_number | | this . magic_number = = NOT_MAGIC_NUMBER )
{
m_positions [ m_positions_size + + ] = g_account_open_positions . Count ( ) - 1 ; // Crece hacia arriba
}
}
//+------------------------------------------------------------------+
//| Function to check if the maximum profit per day was exceeded |
//+------------------------------------------------------------------+
__forceinline bool RiskMNormal_IsSuperatedProfit ( const double value , const double saved_value , void * ptr )
{
return ( account_status_curr_profit > value ) ;
}
//+------------------------------------------------------------------+
//| Boolean function to check if a loss was overcome |
//+------------------------------------------------------------------+
__forceinline bool RiskMNormal_IsSuperatedLoss ( const double value , const double saved_value , void * ptr )
{
return ( - account_status_curr_profit ) > value ;
}
//#define MQLARTICLES_RM_MULTI_EA_MODE
// En caso se decida usar este bot en varias cuentas use el define: MQLARTICLES_RM_MULTI_EA_MODE
//+------------------------------------------------------------------+
//| Funciones para obtener el tipo de fucnion a superar |
//+------------------------------------------------------------------+
FuncionLossProfitSuperate CRiskManagemet : : SuperateModeToFunctionProfit ( ENUM_TYPE_LOSS_PROFIT type ) const
{
# ifdef MQLARTICLES_RM_MULTI_EA_MODE
return CRiskManagemetBase : : WraperIsSupertedProfit ;
# else
return RiskMNormal_IsSuperatedProfit ;
# endif
}
//+------------------------------------------------------------------+
// Si es GMLPO empty
FuncionLossProfitSuperate CRiskManagemet : : SuperateModeToFunctionLoss ( ENUM_TYPE_LOSS_PROFIT type ) const
{
# ifdef MQLARTICLES_RM_MULTI_EA_MODE
return type = = LP_GMLPO ? ( FuncionLossProfitSuperate ) LossProfitEmptyFuncionSup : ( FuncionLossProfitSuperate ) CRiskManagemetBase : : WraperIsSupertedLoss ;
# else
return type = = LP_GMLPO ? ( FuncionLossProfitSuperate ) LossProfitEmptyFuncionSup : ( FuncionLossProfitSuperate ) RiskMNormal_IsSuperatedLoss ;
# endif
}
//+------------------------------------------------------------------+
//| SetLote function |
//+------------------------------------------------------------------+
void CRiskManagemet : : SetLote ( CGetLote * lote_ptr )
{
if ( ! CheckPointer ( lote_ptr ) )
{
LogError ( " El puntero a CGetlote* es invalido " , FUNCION_ACTUAL ) ;
return ;
}
//---
if ( CheckPointer ( get_lote ) = = POINTER_DYNAMIC ) // is not equal to 0 (POINTER_INVALID)
{
//---
RemoveLogger ( get_lote ) ;
delete get_lote ;
//---
AddLogger ( lote_ptr ) ;
this . get_lote = lote_ptr ;
}
else //Si es null
{
AddLogger ( lote_ptr ) ;
this . get_lote = lote_ptr ;
}
}
//+------------------------------------------------------------------+
//| Function that will be executed once all the |
//| Maximum profit and loss have been added |
//+------------------------------------------------------------------+
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 ( ) ;
this . is_init = true ;
}
//+------------------------------------------------------------------+
//| Function to add a maximum loss |
//+------------------------------------------------------------------+
bool CRiskManagemet : : AddLoss ( double _percentage , ENUM_APPLIED_PERCENTAGES _applied , ENUM_RISK_CALCULATION_MODE _mode , ENUM_TYPE_LOSS_PROFIT type , bool strict ,
bool is_dynamic = false , string percentages_to_activate = NULL , string risks_to_be_applied = NULL )
{
//---
if ( _percentage < = 0.00 )
{
LogWarning ( StringFormat ( " Invalid percentage %.2f for lost profit %s, will not be added to the main array " , _percentage , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
if ( _percentage > 100.00 & & _mode = = percentage )
{
LogWarning ( StringFormat ( " Percentage %.2f for %s exceeds 100%%, limiting to 100%% " , _percentage , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
_percentage = 100.00 ;
}
//---
const int index = int ( type ) ;
if ( index > 5 | | index < 0 )
{
LogError ( StringFormat ( " Invalid maximum loss type: %s (index: %d) " , EnumToString ( type ) , index ) , FUNCION_ACTUAL ) ;
return false ;
}
//--- Creamos
CLossProfit * new_loss = NULL ;
CRiskManagemetBase * basic_risk = ( ( CRiskManagemetBase * ) & this ) ;
// Por tipo
if ( _mode = = money )
{
switch ( index )
{
case LP_GMLPO :
{
CLossLossGmlpo < CLossProfitMoney < CLossProfit > > * temp = new CLossLossGmlpo < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MDL :
{
CLossLossMaxDaily < CLossProfitMoney < CLossProfit > > * temp = new CLossLossMaxDaily < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MWL :
{
CLossLossMaxWeekly < CLossProfitMoney < CLossProfit > > * temp = new CLossLossMaxWeekly < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MML :
{
CLossLossMaxMon < CLossProfitMoney < CLossProfit > > * temp = new CLossLossMaxMon < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_ML :
{
CLossLossMax < CLossProfitMoney < CLossProfit > > * temp = new CLossLossMax < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_ML_PICO :
{
CLossLossMaxDesdeArriba < CLossProfitMoney < CLossProfit > > * temp = new CLossLossMaxDesdeArriba < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
}
}
else // percentage
{
switch ( index )
{
case LP_GMLPO :
{
CLossLossGmlpo < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossGmlpo < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MDL :
{
CLossLossMaxDaily < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossMaxDaily < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MWL :
{
CLossLossMaxWeekly < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossMaxWeekly < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_MML :
{
CLossLossMaxMon < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossMaxMon < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_ML :
{
CLossLossMax < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossMax < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
case LP_ML_PICO :
{
CLossLossMaxDesdeArriba < CLossProfitPercentage < CLossProfit > > * temp = new CLossLossMaxDesdeArriba < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , strict , SuperateModeToFunctionLoss ( type ) ) ;
new_loss = temp ;
break ;
}
}
}
//---
if ( ! loss_profits_manager . Add ( new_loss ) )
{
delete new_loss ;
return false ;
}
//---
new_loss . SetPtrSuperated ( GetPtrLossProfits ( ) ) ;
//---
if ( is_dynamic & & percentages_to_activate ! = NULL & & risks_to_be_applied ! = NULL )
{
if ( ! isCachedModifier [ index ] )
{
new_loss . SetDynamic ( percentages_to_activate , risks_to_be_applied , this . chosen_balance ) ;
LogInfo ( StringFormat ( " Dynamic maximum loss %s successfully configured " , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
}
else
{
LogError ( StringFormat ( " Maximum loss %s already has an active modifier " , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
}
}
else
{
LogInfo ( StringFormat ( " Maximum loss %s added: %.2f%s " , EnumToString ( type ) , _percentage , ( _mode = = money ? " money " : " % " ) ) , FUNCION_ACTUAL ) ;
}
return true ;
}
//+------------------------------------------------------------------+
//| Function to add a maximum gain |
//+------------------------------------------------------------------+
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 )
{
//---
if ( _percentage < = 0.00 )
{
LogWarning ( StringFormat ( " Invalid percentage %.2f for maximum gain %s, will not be added to the main array " , _percentage , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
if ( _percentage > 100.00 & & _mode = = percentage )
{
LogWarning ( StringFormat ( " Percentage %.2f for %s exceeds 100%%, limiting to 100%% " , _percentage , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
_percentage = 100.00 ;
}
//---
const int index = int ( type ) ;
if ( index < 6 | | index > 10 ) // Profits: LP_MDP=6 a LP_MP_BAJO=10
{
LogError ( StringFormat ( " Invalid maximum gain type: %s (index: %d) " , EnumToString ( type ) , index ) , FUNCION_ACTUAL ) ;
return false ;
}
//--- Creamos
CLossProfit * new_profit = NULL ;
CRiskManagemetBase * basic_risk = ( ( CRiskManagemetBase * ) & this ) ;
// Ajustar í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
{
CLossProfitMaxDaily < CLossProfitMoney < CLossProfit > > * temp = new CLossProfitMaxDaily < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 1 : // LP_MWP
{
CLossProfitMaxWeekly < CLossProfitMoney < CLossProfit > > * temp = new CLossProfitMaxWeekly < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 2 : // LP_MMP
{
CLossProfitMaxMon < CLossProfitMoney < CLossProfit > > * temp = new CLossProfitMaxMon < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 3 : // LP_MP
{
CLossProfitMax < CLossProfitMoney < CLossProfit > > * temp = new CLossProfitMax < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 4 : // LP_MP_BAJO
{
CLossProfitMaxDesdeBaajo < CLossProfitMoney < CLossProfit > > * temp = new CLossProfitMaxDesdeBaajo < CLossProfitMoney < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
}
}
else // percentage
{
switch ( switch_index )
{
case 0 : // LP_MDP
{
CLossProfitMaxDaily < CLossProfitPercentage < CLossProfit > > * temp = new CLossProfitMaxDaily < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 1 : // LP_MWP
{
CLossProfitMaxWeekly < CLossProfitPercentage < CLossProfit > > * temp = new CLossProfitMaxWeekly < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 2 : // LP_MMP
{
CLossProfitMaxMon < CLossProfitPercentage < CLossProfit > > * temp = new CLossProfitMaxMon < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 3 : // LP_MP
{
CLossProfitMax < CLossProfitPercentage < CLossProfit > > * temp = new CLossProfitMax < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
case 4 : // LP_MP_BAJO
{
CLossProfitMaxDesdeBaajo < CLossProfitPercentage < CLossProfit > > * temp = new CLossProfitMaxDesdeBaajo < CLossProfitPercentage < CLossProfit > > ( false , basic_risk ) ;
temp .Init ( _percentage , _applied , is_strict_ , SuperateModeToFunctionProfit ( type ) ) ;
new_profit = temp ;
break ;
}
}
}
//---
if ( ! loss_profits_manager . Add ( new_profit ) )
{
delete new_profit ;
return false ;
}
//---
new_profit . SetPtrSuperated ( GetPtrLossProfits ( ) ) ;
//---
if ( is_dynamic & & percentages_to_activate ! = NULL & & risks_to_be_applied ! = NULL )
{
if ( ! isCachedModifier [ index ] )
{
new_profit . SetDynamic ( percentages_to_activate , risks_to_be_applied , this . chosen_balance ) ;
LogInfo ( StringFormat ( " Maximum dynamic gain %s successfully set " , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
}
else
{
LogError ( StringFormat ( " Maximum gain %s already has an active modifier " , EnumToString ( type ) ) , FUNCION_ACTUAL ) ;
}
}
else
{
LogInfo ( StringFormat ( " Maximum gain %s added: %.2f%s " , EnumToString ( type ) , _percentage , ( _mode = = money ? " money " : " % " ) ) , FUNCION_ACTUAL ) ;
}
return true ;
}
//+------------------------------------------------------------------+
//| Function to add a risk modifier |
//+------------------------------------------------------------------+
bool CRiskManagemet : : AddModificator ( CExtraModifications * modificator )
{
//--- Check pointer
if ( ! CheckPointer ( modificator ) )
{
LogError ( " The risk modifier is null-invalid " , FUNCION_ACTUAL ) ;
return false ;
}
//--- Property to be modified is invalid ?
const int loss_or_profit = modificator . MaximumProfitOrLossAModify ( ) ;
if ( loss_or_profit = = WRONG_VALUE )
{
LogError ( " The risk modifier does not modify any risk. " , FUNCION_ACTUAL ) ;
return false ;
}
//--- Property to modify is "empty" ?
if ( loss_profits_manager [ loss_or_profit ] . IsEmpty ( ) )
{
LogError ( StringFormat ( " The property to be modified %d is not defined " , loss_or_profit ) , FUNCION_ACTUAL ) ;
return false ;
}
//--- Is the loss_or_profit already dynamic ?
if ( loss_profits_manager [ loss_or_profit ] . IsDynamicMode ( ) )
{
LogError ( StringFormat ( " The property to be modified %d is already dynamic " , loss_or_profit ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
const int index = int ( loss_or_profit ) ;
if ( isCachedModifier [ index ] )
{
LogWarning ( StringFormat ( " The property to be modified %d is already being modified " , loss_or_profit ) , FUNCION_ACTUAL ) ;
return false ;
}
//---
modificator . SetPointer ( loss_profits_manager [ loss_or_profit ] ) ;
ModfierInitInfo init ;
init . balance = this . chosen_balance ;
init . magic = this . magic_number ;
modificator . OnInitModifier ( init ) ;
LogCaution ( StringFormat ( " The modifier '%s' is being added \n This will modify the property '%s' " , modificator . Name ( ) , loss_profits_manager [ loss_or_profit ] . Name ( ) ) , FUNCION_ACTUAL ) ;
//--- Add
m_hooks . Add ( modificator ) ; // We use the fast version (we already did all the checkpointer checks etc.)
//---
isCachedModifier [ index ] = true ;
return true ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CRiskManagemet : : AddRmHook ( IRiskManagementHook * hook )
{
//--- Check pointer
if ( ! CheckPointer ( hook ) )
{
LogError ( " The RM Hook is null-invalid " , FUNCION_ACTUAL ) ;
return false ;
}
//--- Add
m_hooks . Add ( hook ) ; // We use the fast version (we already did all the checkpointer checks etc.)
//---
return true ;
}
//+----------------------------------------------------------------------------------+
//| 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 ( ) )
{
LogCriticalError ( " GMLPO not configured: Stop Loss cannot be calculated " , FUNCION_ACTUAL ) ;
Remover ( ) ;
return 0 ;
}
double lot ;
long sl = get_lote . MoneyToPoints ( type , loss_profits_manager [ LP_GMLPO ] . GetValue ( ) , entry_price , lot , deviation , stop_limit ) ;
//If there is an error or the SL is invalid, it is printed in CGetLote
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 ( ) )
{
LogCriticalError ( " GMLPO not configured: unable to calculate lot size " , FUNCION_ACTUAL ) ;
Remover ( ) ;
this . lote = 0.00 ;
return this . lote ;
}
if ( this . type_get_lot = = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION )
{
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
this . lote = get_lote . GetLoteByRiskPerOperationAndSL ( MaxLote , loss_profits_manager [ LP_GMLPO ] . GetValue ( ) , this . nmlpo , this . StopLoss ) ;
}
else
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
return this . lote ;
}
//+------------------------------------------------------------------+
//| Function to update profits |
//+------------------------------------------------------------------+
void CRiskManagemet : : UpdateProfit ( const double profit , const double acc_total_profit )
{
//--- Update profits
this . monthly_profit + = profit ;
this . daily_profit + = profit ;
this . weekly_profit + = profit ;
this . gross_profit + = profit ;
//---
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 ) ;
}
//---
this . account_profit = acc_total_profit ; // Set Account total profit
}
//+------------------------------------------------------------------+
//| Initial profit update |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnNewProfit ( const RAccountProfit & profit , const datetime curr_time )
{
//---Date
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 ;
this . init_trade_time = this . magic_number ! = NOT_MAGIC_NUMBER ? curr_time : D ' 1972.01 .01 00 : 00 ' ;
//--- Set internal profits
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 ) ;
//--- Info
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 |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnOpenPosition ( const ROnOpenPosition & pos )
{
if ( this . magic_number = = pos . position . magic | | this . magic_number = = NOT_MAGIC_NUMBER )
{
LogInfo ( StringFormat ( " Position %I64u added to internal tickets " , pos . position . ticket ) , FUNCION_ACTUAL ) ;
//--- Activate the flag
this . positions_open = true ;
//--- Execute the virtual function
OnOpenTrade ( ) ;
//--- If there are modifiers then execute the OnOpenPosition function
if ( m_hooks . SizeOnOpenPosition ( ) > 0 )
{
static ModifierOnOpenCloseStruct new_mod ;
// Position
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 ;
m_hooks . OnOpenPosition ( new_mod ) ;
}
//--- Add the operation to positions opened by the Magic
2026-08-04 17:02:36 -05:00
m_positions [ m_positions_size + + ] = ( int ) g_account_open_positions . m_last_created_pos ; // Crece hacia arriba
2026-07-28 09:08:36 -05:00
return ;
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnClosePosition ( const ROnClosePosition & pos , const int global_pos_index )
{
//---
if ( TryRemoveFromArrayOfPos ( global_pos_index ) )
{
//--- Logs
LogCaution ( StringFormat ( " Position with ticket %I64u has been closed " , pos . position . ticket ) , FUNCION_ACTUAL ) ;
LogInfo ( StringFormat ( " Total number of positions for magic number %u is: %d " , this . magic_number , m_positions_size ) , FUNCION_ACTUAL ) ;
//--- Update internal profits
UpdateProfit ( pos . position . profit , pos . account_profit_total ) ;
//--- Update risks (dynamic)
loss_profits_manager . CheckDynamic ( ) ;
//--- If there are no more positions, deactivate the flag
if ( m_positions_size = = 0 )
this . positions_open = false ;
//--- Execute the function that runs every time a position closes
OnCloseTrade ( ) ;
//--- If there are modifiers, execute OnOpenClosePosition
if ( m_hooks . SizeOnClosePosition ( ) > 0 )
{
static ModifierOnOpenCloseStruct new_mod ;
// 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
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 ;
m_hooks . OnClosePosition ( new_mod ) ;
}
//--- Set the new GMLPO value
loss_profits_manager [ LP_GMLPO ] .Set ( ) ; // Empty check is done internally
}
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)
{
//--- Udpate profit
UpdateProfit ( pos . position . profit , pos . account_profit_total ) ;
//--- Set GMLPO
loss_profits_manager [ LP_GMLPO ] .Set ( ) ; // Empty check is done internally
}
// Partials are not necessary since we did that to ensure trans.position profit also counts the partial close, the other class already does that.
}
//+------------------------------------------------------------------+
//| Function that runs every new day |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnNewDay ( const datetime curr_time )
{
this . daily_profit = 0.00 ;
this . last_day_time = curr_time ;
}
//+------------------------------------------------------------------+
//| Function that runs every new week |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnNewWeek ( const datetime curr_time )
{
this . last_weekly_time = curr_time ;
this . weekly_profit = 0.00 ;
}
//+------------------------------------------------------------------+
//| Function that runs every new month |
//+------------------------------------------------------------------+
void CRiskManagemet : : OnNewMonth ( const datetime curr_time )
{
this . last_monthly_time = curr_time ;
this . monthly_profit = 0.00 ;
}
//+------------------------------------------------------------------+
//| Function to obtain the value of a loss or gain based |
//| on the applied value (balance, free margin, equity, net profit) |
//+------------------------------------------------------------------+
double CRiskManagemet : : GetValorWithApplied ( double percentage_ , int type , ENUM_APPLIED_PERCENTAGES applied ) const
{
switch ( applied )
{
case Balance :
return ( ( percentage_ * 0.01 ) * AccountInfoDouble ( ACCOUNT_BALANCE ) ) ;
case ganancianeta :
{
if ( this . account_profit < = 0.0000001 )
{
LogError ( StringFormat ( " The total profit of the account which is %+.2f is invalid or negative " , this . account_profit ) , FUNCION_ACTUAL ) ;
return 0 ;
}
else
return ( ( percentage_ * 0.01 ) * this . account_profit ) ;
}
case free_margin :
{
if ( AccountInfoDouble ( ACCOUNT_MARGIN_FREE ) < = 0.00000000001 )
{
LogError ( StringFormat ( " Free margin of %+.2f is invalid " , AccountInfoDouble ( ACCOUNT_MARGIN_FREE ) ) , FUNCION_ACTUAL ) ;
return 0 ;
}
else
return ( ( percentage_ * 0.01 ) * AccountInfoDouble ( ACCOUNT_MARGIN_FREE ) ) ;
}
case equity :
return ( ( percentage_ * 0.01 ) * AccountInfoDouble ( ACCOUNT_EQUITY ) ) ;
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
//---
void CRiskManagemet : : OnWithdrawalDeposit ( const double value ) override
{
// 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
}
//+------------------------------------------------------------------+
//| Clase de gestion de riesgo normal |
//+------------------------------------------------------------------+
class CRiskManagemetPersonal : public CRiskManagemet
{
public :
CRiskManagemetPersonal ( ulong _magic , ENUM_GET_LOT _get )
: CRiskManagemet ( _magic , _get ) { }
inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement ( ) const override final { return risk_mode_personal_account ; }
void SetGeneralParameters ( MqlParam & params [ ] ) override { } // No extra parameters required
} ;
//+------------------------------------------------------------------+
//| 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
//---
double GetValorWithApplied ( double percentage_ , int type , ENUM_APPLIED_PERCENTAGES applied ) const override ;
FuncionLossProfitSuperate SuperateModeToFunctionLoss ( ENUM_TYPE_LOSS_PROFIT type ) const override ;
public :
CRiskManagemetPropFirm ( ulong _magic , ENUM_GET_LOT type_get_lot_ )
: CRiskManagemet ( _magic , type_get_lot_ ) { }
//--- General
void SetGeneralParameters ( MqlParam & params [ ] ) override ;
__forceinline double GetAccountBalancePropFirm ( ) const { return account_balance_propfirm ; }
//---
inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement ( ) const override final { return risk_mode_propfirm_dynamic_daiy_loss ; }
} ;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
inline bool RiskMPropFirmFN_FTMO_IsSuperatedLoss ( const double value , const double saved_value , void * ptr )
{
return AccountInfoDouble ( ACCOUNT_EQUITY ) < ( ( ( CRiskManagemetPropFirm * ) ptr ) . GetAccountBalancePropFirm ( ) - saved_value ) ;
}
//+------------------------------------------------------------------+
FuncionLossProfitSuperate CRiskManagemetPropFirm : : SuperateModeToFunctionLoss ( ENUM_TYPE_LOSS_PROFIT type ) const
{
if ( type = = LP_ML )
return RiskMPropFirmFN_FTMO_IsSuperatedLoss ;
return CRiskManagemet : : SuperateModeToFunctionLoss ( type ) ;
}
//+------------------------------------------------------------------+
void CRiskManagemetPropFirm : : SetGeneralParameters ( MqlParam & params [ ] ) override
{
account_balance_propfirm = params [ 0 ] . double_value ;
this . chosen_balance = account_balance_propfirm ;
}
//+------------------------------------------------------------------+
double CRiskManagemetPropFirm : : GetValorWithApplied ( double percentage_ , int type , ENUM_APPLIED_PERCENTAGES applied ) const override
{
if ( type = = LP_ML | | type = = LP_MDL )
return account_balance_propfirm * ( percentage_ * 0.01 ) ;
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 :
CRiskPointer ( ulong _magic , ENUM_GET_LOT _Get ) ;
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 ;
//---
switch ( mode )
{
//---
case risk_mode_propfirm_dynamic_daiy_loss :
{
new_risk = new CRiskManagemetPropFirm ( this . magic , this . type_get_lot ) ;
if ( ArraySize ( parametros [ 1 ] . params ) < 1 )
{
Print ( FUNCION_ACTUAL , " : Critical error | The parameter array for prop firm mode has not been populated. " ) ;
Remover ( ) ;
break ;
}
new_risk . SetGeneralParameters ( parametros [ 1 ] . params ) ;
break ;
}
//---
case risk_mode_personal_account :
{
new_risk = new CRiskManagemetPersonal ( this . magic , this . type_get_lot ) ;
break ;
}
//---
default :
Print ( FUNCION_ACTUAL , " : Critical error | The type: " , EnumToString ( mode ) , " It is invalid " ) ;
break ;
}
//---
if ( CheckPointer ( new_risk ) = = POINTER_INVALID )
{
Print ( FUNCION_ACTUAL , " : Critical error | The pointer to CRiskManagement* is invalid " ) ;
return NULL ;
}
//---
return new_risk ;
}
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CRiskPointer : : CRiskPointer ( ulong _magic , ENUM_GET_LOT _Get )
{
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 ;
}
//+------------------------------------------------------------------+
# endif // MQLARTICLES_RM_RISKMANAGEMENT_MQH
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+