//+------------------------------------------------------------------+ //| 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 RISK_MANAGEMENT_GEN_MQH #define RISK_MANAGEMENT_GEN_MQH #include "Modificators.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; ulong last_deal_ticket; //The last deal ticket (it can be from a position opening deal or a position closing deal) ulong last_position_ticket; //-- 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; bool isCachedAddLossProfit[LOSS_PROFIT_COUNT]; //--- Modifiers CManagerModificator modificators; bool isCachedModifier[LOSS_PROFIT_COUNT]; //--- general function to assign values to loss variables virtual double GetValorWithApplied(double percentage_, ENUM_TYPE_LOSS_PROFIT type, ENUM_APPLIED_PERCENTAGES applied) const override; //--- Update profits function virtual void UpdateProfit(const ROnOpenClosePosition &pos, bool actulizar_profits); virtual void OnOpenTrade() { }; virtual void OnCloseTrade() { }; //--- virtual FuncionLossProfitSuperate SuperateModeToFunctionProfit(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const; virtual FuncionLossProfitSuperate SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const; public: //--- Constructor and Destructor CRiskManagemet(ulong _magic, ENUM_GET_LOT type_get_lot_); ~CRiskManagemet(); //--- Set void EndAddProfitLoss(); virtual void SetGeneralParameters(MqlParam ¶ms[]) = 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 inline CLossProfit* operator[](const ENUM_TYPE_LOSS_PROFIT _type) { return loss_profits_manager[_type]; }; inline CLossProfit* operator[](const int index) { return loss_profits_manager[index]; }; //--- Add void AddModificator(CExtraModifications* modificator); // Modificator pointer void AddModificator(CExtraModifications &modificator); // Modificator instance by reference void AddLoss(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE sop_mode = CLOSE_POSITION_AND_EQUITY, bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL); // Loss void AddProfit(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool is_strict_, MODE_SUPERATE sop_mode = CLOSE_POSITION_AND_EQUITY, bool is_dynamic = false, string percentages_to_activate = NULL, string risks_to_be_applied = NULL); // Profit //--- Functions inherited from CAccountGestor void OnNewProfit(const ROnOpenClosePosition &profit) override; virtual void OnOpenClosePosition(const ROnOpenClosePosition &pos) override; void OnNewDay(datetime init_time) override; void OnNewWeek(datetime init_time) override; void OnNewMonth(datetime init_time) override; void OnWithdrawalDeposit(double value) 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; } //--- Magic number inline ulong MagicNumber() const { return this.magic_number; } //--- Risk management type virtual inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement() const = 0; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CRiskManagemet::CRiskManagemet(ulong _magic, ENUM_GET_LOT type_get_lot_) : CRiskManagemetBase(_magic) { this.chosen_balance = AccountInfoDouble(ACCOUNT_BALANCE); MathSrand(GetTickCount()); vector ramdom = vector::Random(1, -100, 100); c = (int8_t)ramdom[0]; for(int i = 0 ; i < LOSS_PROFIT_COUNT; i++) { isCachedAddLossProfit[i] = false; isCachedModifier[i] = false; } this.is_init = false; //--- this.last_position_ticket = INVALID_TICKET; this.last_deal_ticket = INVALID_TICKET; //--- this.type_get_lot = type_get_lot_; loss_profits_manager = new CLossProfitManager(); //--- AddLogger(loss_profits_manager); AddLogger(modificators); //--- for(int i = PositionsTotal() - 1; i >= 0; i--) { 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) } } } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CRiskManagemet::~CRiskManagemet() { CleanItems("RiskManagement"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ FuncionLossProfitSuperate CRiskManagemet::SuperateModeToFunctionProfit(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const { const static FuncionLossProfitSuperate functions[3] = {RiskMNormal_IsSuperatedProfit_Equity, RiskMNormal_IsSuperatedProfit_Close, RiskMNormal_IsSuperatedProfit_CloseAndEquity}; return functions[int(mode)]; } //+------------------------------------------------------------------+ FuncionLossProfitSuperate CRiskManagemet::SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const { const static FuncionLossProfitSuperate functions[3] = {RiskMNormal_IsSuperatedLoss_Equity, RiskMNormal_IsSuperatedLoss_Close, RiskMNormal_IsSuperatedLoss_CloseAndEquity}; return functions[int(mode)]; } //+------------------------------------------------------------------+ //| SetLote function | //+------------------------------------------------------------------+ void CRiskManagemet::SetLote(CGetLote *lote_ptr) { if(CheckPointer(lote_ptr) == POINTER_INVALID) { LogError("El puntero a CGetlote* es invalido", FUNCION_ACTUAL); return; } //--- if(CheckPointer(get_lote)) // is not equal to 0 (POINTER_INVALID) { if(CheckPointer(get_lote) == POINTER_DYNAMIC) delete get_lote; RemoveLogger(get_lote); AddLogger(lote_ptr); this.get_lote = lote_ptr; return; } 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 | //+------------------------------------------------------------------+ void CRiskManagemet::AddLoss(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE sop_mode = CLOSE_POSITION_AND_EQUITY, 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; } //--- 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 < 3) { LogError(StringFormat("Invalid maximum loss type: %s (index: %d)", EnumToString(type), index), FUNCION_ACTUAL); return; } //--- if(isCachedAddLossProfit[index]) { LogWarning(StringFormat("Maximum loss %s already exists, ignoring duplicate", EnumToString(type)), FUNCION_ACTUAL); return; } CLossProfit* new_loss; new_loss = new CLossProfit(false, dynamic_cast(&this)); //--- new_loss.SetProperties(_percentage, _applied, _mode, type, SuperateModeToFunctionLoss(type, sop_mode)); loss_profits_manager.Add(new_loss); if(is_dynamic == true && 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%%", EnumToString(type), _percentage), FUNCION_ACTUAL); } } //+------------------------------------------------------------------+ //| Function to add a maximum gain | //+------------------------------------------------------------------+ void CRiskManagemet::AddProfit(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT type, bool is_strict_, MODE_SUPERATE sop_mode = CLOSE_POSITION_AND_EQUITY, 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; } //--- 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 >= 3) { LogError(StringFormat("Invalid maximum gain type: %s (index: %d)", EnumToString(type), index), FUNCION_ACTUAL); return; } //--- if(isCachedAddLossProfit[index]) { LogWarning(StringFormat("Maximum gain %s already exists, ignoring duplicate", EnumToString(type)), FUNCION_ACTUAL); return; } CLossProfit* new_profit; new_profit = new CLossProfit(false, dynamic_cast(&this)); //--- new_profit.SetProperties(_percentage, _applied, _mode, type, SuperateModeToFunctionProfit(type, sop_mode), is_strict_); loss_profits_manager.Add(new_profit); if(is_dynamic == true && 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%%", EnumToString(type), _percentage), FUNCION_ACTUAL); } } //+------------------------------------------------------------------+ //| Function to add a risk modifier | //+------------------------------------------------------------------+ void CRiskManagemet::AddModificator(CExtraModifications *modificator) { //--- Check pointer if(!CheckPointer(modificator)) { LogError("The risk modifier is null-invalid", FUNCION_ACTUAL); return; } //--- Property to be modified is invalid ? const ENUM_TYPE_LOSS_PROFIT loss_or_profit = modificator.MaximumProfitOrLossAModify(); if(loss_or_profit == WRONG_VALUE) { LogError("The risk modifier does not modify any risk.", FUNCION_ACTUAL); return; } //--- Property to modify is "empty" ? if(loss_profits_manager[loss_or_profit].IsEmpty()) { LogError(StringFormat("The property to be modified %s is not defined", EnumToString(loss_or_profit)), FUNCION_ACTUAL); return; } //--- Is the loss_or_profit already dynamic ? if(loss_profits_manager[loss_or_profit].IsDynamicMode()) { LogError(StringFormat("The property to be modified %s is already dynamic", EnumToString(loss_or_profit)), FUNCION_ACTUAL); return; } //--- const int index = int(loss_or_profit); if(isCachedModifier[index]) { LogWarning(StringFormat("The property to be modified %s is already being modified", EnumToString(loss_or_profit)), FUNCION_ACTUAL); return; } //--- 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\nThis will modify the property '%s'", modificator.Name(), loss_profits_manager[loss_or_profit].Name()), FUNCION_ACTUAL); //--- Add modificators.AddItemFast(modificator); // We use the fast version (we already did all the checkpointer checks etc.) //--- isCachedModifier[index] = true; } //+------------------------------------------------------------------+ //| Function to add a risk modifier | //+------------------------------------------------------------------+ void CRiskManagemet::AddModificator(CExtraModifications & modificator) { AddModificator(&modificator); } //+----------------------------------------------------------------------------------+ //| 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.CalculateSL(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 check if the maximum profit per day was exceeded | //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedProfit_Close(double profit_, double value_, bool strict, double extra_value) { return (profit_ > value_); } //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedProfit_CloseAndEquity(double profit_, double value_, bool strict, double extra_value) { double new_mdp = profit_ > 0 ? value_ - profit_ : (!strict ? value_ : value_ + (profit_ * -1)); return (account_status_curr_profit > new_mdp); } //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedProfit_Equity(double profit_, double value_, bool strict, double extra_value) { return (account_status_curr_profit > value_); } //+------------------------------------------------------------------+ //| Boolean function to check if a loss was overcome | //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedLoss_Close(double profit_, double value_, bool strict, double extra_value) { return (profit_ * -1 > value_); } //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedLoss_CloseAndEquity(double profit_, double value_, bool strict, double extra_value) { double new_loss = profit_ < 0 ? value_ - fabs(profit_) : value_; return (account_status_curr_profit * -1 > new_loss); } //+------------------------------------------------------------------+ inline bool RiskMNormal_IsSuperatedLoss_Equity(double profit_, double value_, bool strict, double extra_value) { return (account_status_curr_profit * -1 > value_); } //+------------------------------------------------------------------+ //| Function to update profits | //+------------------------------------------------------------------+ void CRiskManagemet::UpdateProfit(const ROnOpenClosePosition &pos, bool actulizar_profits) { //--- Update profits if(actulizar_profits) { this.monthly_profit += pos.position.profit; this.daily_profit += pos.position.profit; this.weekly_profit += pos.position.profit; this.gross_profit += pos.position.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 = pos.account_profit_total; // Set Account total profit //--- loss_profits_manager[LP_MDL].SetProfit(pos.account_profit_diario, this.c); loss_profits_manager[LP_MDP].SetProfit(pos.account_profit_diario, this.c); loss_profits_manager[LP_MWL].SetProfit(pos.account_profit_semanal, this.c); loss_profits_manager[LP_MWP].SetProfit(pos.account_profit_semanal, this.c); loss_profits_manager[LP_MML].SetProfit(pos.account_profit_mensual, this.c); loss_profits_manager[LP_MMP].SetProfit(pos.account_profit_mensual, this.c); } //+------------------------------------------------------------------+ //| Initial profit update | //+------------------------------------------------------------------+ void CRiskManagemet::OnNewProfit(const ROnOpenClosePosition &profit) { //--- Update profits (SetProfit checks if it's empty, no need to do it here) loss_profits_manager[LP_MDL].SetProfit(profit.account_profit_diario, this.c); loss_profits_manager[LP_MDP].SetProfit(profit.account_profit_diario, this.c); loss_profits_manager[LP_MWL].SetProfit(profit.account_profit_semanal, this.c); loss_profits_manager[LP_MWP].SetProfit(profit.account_profit_semanal, this.c); loss_profits_manager[LP_MML].SetProfit(profit.account_profit_mensual, this.c); loss_profits_manager[LP_MMP].SetProfit(profit.account_profit_mensual, this.c); loss_profits_manager[LP_ML].SetProfit(profit.account_profit_total, this.c); //---Dates this.last_day_time = iTime(_Symbol, PERIOD_D1, 0); this.last_weekly_time = iTime(_Symbol, PERIOD_W1, 0); this.last_monthly_time = iTime(_Symbol, PERIOD_MN1, 0); this.init_trade_time = this.magic_number != NOT_MAGIC_NUMBER ? TimeCurrent() : 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::OnOpenClosePosition(const ROnOpenClosePosition &pos) { if(pos.deal_entry_type == DEAL_ENTRY_IN && (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); //--- Assign tickets so child classes have access to them this.last_deal_ticket = pos.deal_ticket; this.last_position_ticket = pos.position.ticket; //--- Activate the flag this.positions_open = true; //--- Execute the virtual function OnOpenTrade(); //--- If there are modifiers then execute the OnOpenPosition function if(modificators.Size() > 0) { 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; modificators.OnOpenPosition(new_mod); } //--- Add the operation to positions opened by the Magic AddArrayNoVerification2(open_positions, pos.position, RISK_MANAGEMENT_RESERVE_POS); return; } if(pos.deal_entry_type != DEAL_ENTRY_OUT) return; if(RemoveIndexFromAnArrayOfPositions(this.open_positions, pos.position.ticket, RISK_MANAGEMENT_RESERVE_POS)) { //--- 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, this.GetPositionsTotal()), FUNCION_ACTUAL); //--- Set the last tickets this.last_deal_ticket = pos.deal_ticket; this.last_position_ticket = pos.position.ticket; //--- Update internal profits UpdateProfit(pos, true); //--- Update risks (dynamic) loss_profits_manager.CheckDynamic(); //--- If there are no more positions, deactivate the flag if(GetPositionsTotal() == 0) this.positions_open = false; //--- Execute the function that runs every time a position closes OnCloseTrade(); //--- If there are modifiers, execute OnOpenClosePosition if(modificators.Size() > 0) { 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; modificators.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, false); //--- 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(datetime init_time) { this.daily_profit = 0.00; this.last_day_time = init_time; modificators.OnNewDay(); //--- MDL if(loss_profits_manager[LP_MDL].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MDL].SetProfit(0.00, this.c); // Reset profit //--- MDP if(loss_profits_manager[LP_MDP].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MDP].SetProfit(0.00, this.c); // Reset profit } //+------------------------------------------------------------------+ //| Function that runs every new week | //+------------------------------------------------------------------+ void CRiskManagemet::OnNewWeek(datetime init_time) { this.last_weekly_time = init_time; this.weekly_profit = 0.00; //--- MWL if(loss_profits_manager[LP_MWL].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MWL].SetProfit(0.00, this.c); // Reset profit //--- MWP if(loss_profits_manager[LP_MWP].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MWP].SetProfit(0.00, this.c); // Reset profit } //+------------------------------------------------------------------+ //| Function that runs every new month | //+------------------------------------------------------------------+ void CRiskManagemet::OnNewMonth(datetime init_time) { this.last_monthly_time = init_time; this.monthly_profit = 0.00; //--- MML if(loss_profits_manager[LP_MML].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MML].SetProfit(0.00, this.c); // Reset profit //--- MMP if(loss_profits_manager[LP_MMP].Set()) // If the value could be set (means it is not empty) loss_profits_manager[LP_MMP].SetProfit(0.00, this.c); // Reset profit } //+------------------------------------------------------------------+ //| 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_, ENUM_TYPE_LOSS_PROFIT type, ENUM_APPLIED_PERCENTAGES applied) const { switch(applied) { case Balance: return NormalizeDouble((percentage_ / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2); case ganancianeta: { if(this.account_profit <= 0) { LogError(StringFormat("The total profit of the account which is %+.2f is invalid or negative", this.account_profit), FUNCION_ACTUAL); return 0; } else return NormalizeDouble((percentage_ / 100.0) * this.account_profit, 2); } case free_margin: { if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) { LogError(StringFormat("Free margin of %+.2f is invalid", AccountInfoDouble(ACCOUNT_MARGIN_FREE)), FUNCION_ACTUAL); return 0; } else return NormalizeDouble((percentage_ / 100.0) * AccountInfoDouble(ACCOUNT_MARGIN_FREE), 2); } case equity: return NormalizeDouble((percentage_ / 100.0) * AccountInfoDouble(ACCOUNT_EQUITY), 2); 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(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.SetNewChossenBalanceForDynamics(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 ¶ms[]) 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 bool m_update_loss; // Bandera que indica si se aumentara el MDL void UpdateDailyLossFTMO(); double GetValorWithApplied(double percentage_, ENUM_TYPE_LOSS_PROFIT type, ENUM_APPLIED_PERCENTAGES applied) const override; FuncionLossProfitSuperate SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const override; void OnCloseTrade() override; public: CRiskManagemetPropFirm(ulong _magic, ENUM_GET_LOT type_get_lot_) : CRiskManagemet(_magic, type_get_lot_), m_update_loss(true) { } //--- General double GetExtraValueForLossProfit() override { return account_balance_propfirm; } void SetGeneralParameters(MqlParam ¶ms[]) override; inline double GetAccountBalancePropFirm() const { return account_balance_propfirm; } //--- Actulizacion del MDL inline bool UpdateLoss() const { return m_update_loss; } void UpdateLoss(bool new_val) { m_update_loss = new_val;} //--- inline ENUM_MODE_RISK_MANAGEMENT ModeRiskManagement() const override final { return risk_mode_propfirm_dynamic_daiy_loss; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ inline bool RiskMPropFirmFN_FTMO_IsSuperatedLoss(double profit_, double value_, bool strict, double extra_value) { return AccountInfoDouble(ACCOUNT_EQUITY) < (extra_value - value_); } //+------------------------------------------------------------------+ FuncionLossProfitSuperate CRiskManagemetPropFirm::SuperateModeToFunctionLoss(ENUM_TYPE_LOSS_PROFIT type, MODE_SUPERATE mode) const { if(type == LP_ML) return RiskMPropFirmFN_FTMO_IsSuperatedLoss; return CRiskManagemet::SuperateModeToFunctionLoss(type, mode); } //+------------------------------------------------------------------+ void CRiskManagemetPropFirm::OnCloseTrade(void) { if(m_update_loss) UpdateDailyLossFTMO(); } //+------------------------------------------------------------------+ void CRiskManagemetPropFirm::SetGeneralParameters(MqlParam & params[]) override { account_balance_propfirm = params[0].double_value; this.chosen_balance = account_balance_propfirm; } //+------------------------------------------------------------------+ void CRiskManagemetPropFirm::UpdateDailyLossFTMO() { double prev = loss_profits_manager[LP_MDL].GetValue(); prev += this.daily_profit > 0.00 ? this.daily_profit : 0; loss_profits_manager[LP_MDL].SetValue(prev, this.c); LogCaution(StringFormat("The maximum loss per operation has been modified, its new value: %.2f", loss_profits_manager[LP_MDL].GetValue()), FUNCION_ACTUAL); } //+------------------------------------------------------------------+ double CRiskManagemetPropFirm::GetValorWithApplied(double percentage_, ENUM_TYPE_LOSS_PROFIT type, ENUM_APPLIED_PERCENTAGES applied) const override { if(type == LP_ML || type == LP_MDL) return account_balance_propfirm * (percentage_ / 100.0); return CRiskManagemet::GetValorWithApplied(percentage_, type, applied); //--- return 0.00; } //+------------------------------------------------------------------+ //| 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_personal_account: { new_risk = new CRiskManagemetPersonal(this.magic, this.type_get_lot); break; } case risk_mode_propfirm_dynamic_daiy_loss: { new_risk = new CRiskManagemetPropFirm(this.magic, this.type_get_lot); if(ArraySize(parametros[1].params) < 1) { Print(__FUNCTION__, ": Critical error | The parameter array for prop firm mode has not been populated."); Remover(); break; } new_risk.SetGeneralParameters(parametros[1].params); break; } default: Print(__FUNCTION__, ": Critical error | The type: ", EnumToString(mode), " It is invalid"); break; } //--- if(CheckPointer(new_risk) == POINTER_INVALID) { Print(__FUNCTION__, ": 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 //+------------------------------------------------------------------+