//+------------------------------------------------------------------+ //| Modificators.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_MODIFICATORS_MQH #define RISK_MODIFICATORS_MQH #include "LossProfit.mqh" //+--------------------------------------------------------------------+ //| Class to integrate external modifications to risk management | //+--------------------------------------------------------------------+ class CExtraModifications : public CLoggerBase { protected: CLossProfit* m_modifier; ENUM_TYPE_LOSS_PROFIT m_property_to_modify; string m_modifier_name; public: CExtraModifications(ENUM_TYPE_LOSS_PROFIT _property_to_modify = WRONG_VALUE) { m_property_to_modify = _property_to_modify; } //--- Non-modifiable functions // Function that returns the type of "maximum loss or profit" that this class is modifying inline ENUM_TYPE_LOSS_PROFIT MaximumProfitOrLossAModify() const { return m_property_to_modify; }; // Function that will be used in CRiskManagement to assign the "maximum loss or profit" based on the type of "maximum loss or profit" chosen // in the constructor. void SetPointer(CLossProfit* _modifier); // Name of the custom modifier (to differentiate from others) inline string Name() const { return m_modifier_name; } //--- Virtual functions // Function that will be executed each time a position is closed virtual void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) { } // Function that will be executed each time an operation is opened virtual void OnOpenPosition(ModifierOnOpenCloseStruct &on_open_close_struct) { } // Function that will be executed only once (at the moment of creating the m_modifier) virtual void OnInitModifier(ModfierInitInfo &on_init) { } // Function that will be executed each new day virtual void OnNewDay() { } }; //+------------------------------------------------------------------+ //| Set pointer | //+------------------------------------------------------------------+ void CExtraModifications::SetPointer(CLossProfit * _modifier) { //--- if(CheckPointer(_modifier) == POINTER_INVALID) { LogFatalError(StringFormat("The pointer to CLossProfit* for m_modifier %s is invalid", Name()), FUNCION_ACTUAL); Remover(); return; } //--- if(_modifier.GetType() != MaximumProfitOrLossAModify()) { LogFatalError(StringFormat("The type of the maximum loss/gain = %s, is different from the property to be modified = %s", _modifier.Name(), EnumToString(MaximumProfitOrLossAModify())), FUNCION_ACTUAL); Remover(); return; } //--- m_modifier = _modifier; } //+------------------------------------------------------------------+ //| Clase para aumentar el riesgo | //+------------------------------------------------------------------+ enum ENUM_MULTIPLIER_METHOD_DR { DR_MULTIPLIER = 0, // Multiplier DR_EXPONECIAL = 1, // Exponential DR_SUMATORIO = 2 // Additive (Summation) }; //--- Clase class CDynamicRisk : public CExtraModifications { private: double paso; double val; double percentage_a_empezar_modfiicacioneS; ENUM_MULTIPLIER_METHOD_DR metod; void Aumentar(); bool is_set; public: CDynamicRisk(double step, double percentage_to_empezar, ENUM_TYPE_LOSS_PROFIT property_to_modify, ENUM_MULTIPLIER_METHOD_DR multiplier_); void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override; void OnInitModifier(ModfierInitInfo &on_init) override; }; //+------------------------------------------------------------------+ CDynamicRisk::CDynamicRisk(double step, double percentage_to_empezar, ENUM_TYPE_LOSS_PROFIT property_to_modify, ENUM_MULTIPLIER_METHOD_DR multiplier_) : CExtraModifications(property_to_modify) { m_modifier_name = "Risk enhancer by default"; paso = step; percentage_a_empezar_modfiicacioneS = percentage_to_empezar; metod = multiplier_; is_set = false; if((int)metod > 2 || metod < 0) { LogFatalError(StringFormat("The method %s is invalid", EnumToString(metod)), FUNCION_ACTUAL); Remover(); } } //+------------------------------------------------------------------+ void CDynamicRisk::OnInitModifier(ModfierInitInfo & on_init) { percentage_a_empezar_modfiicacioneS /= 100.0; percentage_a_empezar_modfiicacioneS *= on_init.balance; Print("Balance to increase risk: ", percentage_a_empezar_modfiicacioneS); val = m_modifier.GetPercentage(); //Print("Valor del porcentaje: ", val); } //+------------------------------------------------------------------+ void CDynamicRisk::OnClosePosition(ModifierOnOpenCloseStruct & on_open_close_struct) { if(on_open_close_struct.position.profit > 0 && on_open_close_struct.profit_total > percentage_a_empezar_modfiicacioneS) { LogInfo(StringFormat("By increasing the risk, a profit of %.2f has been obtained. The initial balance has been exceeded by %.2f.", on_open_close_struct.position.profit, percentage_a_empezar_modfiicacioneS), FUNCION_ACTUAL); Aumentar(); m_modifier.SetPercentage(val); } else { m_modifier.SetInitialPercentageOrMoney(); val = m_modifier.GetPercentage(); } } //+------------------------------------------------------------------+ void CDynamicRisk::Aumentar(void) { switch(metod) { case DR_MULTIPLIER: val *= paso; break; case DR_EXPONECIAL: val *= MathExp(paso); break; case DR_SUMATORIO: val += paso; break; default: Remover(); break; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CModificatorOscar : public CExtraModifications { private: double max_risk; double riesgo_inciial; double fat_mul; double prev_risk; int hora_ny_init; int min_ny_inti; datetime hora_pa; datetime hora_pd; public: CModificatorOscar(double max_risk_percent, double risk_init_percent, double multiplicator_, int init_ny_h, int init_ny_min); void OnNewDay() override; void OnInitModifier(ModfierInitInfo &on_init) override; void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override; }; //+------------------------------------------------------------------+ CModificatorOscar::CModificatorOscar(double max_risk_percent, double risk_init_percent, double multiplicator_, int init_ny_h, int init_ny_min) : CExtraModifications(LP_GMLPO) //Modify the risk by operation { max_risk = max_risk_percent; riesgo_inciial = risk_init_percent; fat_mul = multiplicator_; hora_ny_init = init_ny_h; min_ny_inti = init_ny_min; m_modifier_name = "Modifier by oscar"; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CModificatorOscar::OnNewDay(void) { datetime hora_ny = HoraYMinutoADatetime(hora_ny_init, min_ny_inti, TimeCurrent()); hora_pa = hora_ny - 60; hora_pd = hora_ny + 60; } //+------------------------------------------------------------------+ void CModificatorOscar::OnInitModifier(ModfierInitInfo &on_init) { riesgo_inciial /= 100.0; riesgo_inciial *= on_init.balance; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CModificatorOscar::OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) { bool es_perdida = on_open_close_struct.position.profit < 0.00; bool cerro_por_sl = on_open_close_struct.deal_reason == DEAL_REASON_SL; bool cerro_por_tp = on_open_close_struct.deal_reason == DEAL_REASON_TP; bool cerrado_en_ny = TimeCurrent() >= hora_pa && TimeCurrent() <= hora_pd; //--- if(cerrado_en_ny) { LogInfo("Operation closed during NY hours - current risk remains", FUNCION_ACTUAL); return; } //--- if(es_perdida && cerro_por_sl) { LogInfo(StringFormat("SL reached - increasing risk. Loss: %.2f", on_open_close_struct.position.profit), FUNCION_ACTUAL); double new_risk = m_modifier.GetPercentage() * fat_mul; if(new_risk <= max_risk) m_modifier.SetPercentage(new_risk); else m_modifier.SetPercentage(max_risk); } else if(!es_perdida && cerro_por_tp) { LogInfo("TP reached - resetting to initial risk", FUNCION_ACTUAL); m_modifier.SetInitialPercentageOrMoney(); } } //+--------------------------------------------------------------------------------------+ //| Clase para modificar el riesgo en base a la estrategia de 2.0 | //+--------------------------------------------------------------------------------------+ class CModifierDynamicRisk : public CExtraModifications { private: double to_applied[]; int index_gmlpo; public: CModifierDynamicRisk(string percentages_to_applied); void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override; }; //+------------------------------------------------------------------+ CModifierDynamicRisk::CModifierDynamicRisk(string percentages_to_applied) : CExtraModifications(LP_GMLPO) { StrTo::CstArray(to_applied, percentages_to_applied, ','); index_gmlpo = 0; m_modifier_name = "Dynamic Risk 2.0"; } //+------------------------------------------------------------------+ void CModifierDynamicRisk::OnClosePosition(ModifierOnOpenCloseStruct & on_open_close_struct) { if(on_open_close_struct.position.profit < 0.00) //La posicion salio SL { // Print(__FUNCTION__, "Se a obtenido un sl modificando el riesgo..."); m_modifier.SetPercentage(to_applied[index_gmlpo]); if(index_gmlpo < (int)to_applied.Size() - 1) index_gmlpo++; } else //La posicion salio TP { //Print(__FUNCTION__, "Se a obtenido un tp modificando el riesgo..."); index_gmlpo = 0; m_modifier.SetInitialPercentageOrMoney(); //Modificar al riesgo inicial } } //+------------------------------------------------------------------+ //| Modificador de riesgo 2 | //+------------------------------------------------------------------+ class CDynamicRiskAlma : public CExtraModifications { private: double multiplier; public: CDynamicRiskAlma(double mul) : CExtraModifications(LP_GMLPO) { multiplier = mul; m_modifier_name = "Dynamic Risk Alma v2"; } void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override; }; //+------------------------------------------------------------------+ void CDynamicRiskAlma::OnClosePosition(ModifierOnOpenCloseStruct & on_open_close_struct) { if(on_open_close_struct.profit_total < 0) { double r = m_modifier.GetPercentage() * multiplier; m_modifier.SetPercentage(r); } else { m_modifier.SetInitialPercentageOrMoney(); } } //+------------------------------------------------------------------+ //| Clase para modificar riesgo 3 | //+------------------------------------------------------------------+ inline double FunctionKevin(double defect_risk, double loss_percentage, double constante) { return (defect_risk * MathExp(loss_percentage / constante)); } //+------------------------------------------------------------------+ class CMathDynamicRisk : public CExtraModifications { private: double constante; double balance_inicial; public: CMathDynamicRisk(double constante_) : CExtraModifications(LP_GMLPO) { constante = constante_; m_modifier_name = "Modifier By Kevin"; } void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override; void OnInitModifier(ModfierInitInfo &on_init) override; }; //+------------------------------------------------------------------+ void CMathDynamicRisk::OnInitModifier(ModfierInitInfo & on_init) { balance_inicial = on_init.balance; } //+------------------------------------------------------------------+ void CMathDynamicRisk::OnClosePosition(ModifierOnOpenCloseStruct & on_open_close_struct) { double percentage_ = CalcularPorcentaje(on_open_close_struct.profit_total, balance_inicial); if(percentage_ < 0.00) { double new_percentage = FunctionKevin(m_modifier.GetInitialPercentageOrMoney(), percentage_, constante); m_modifier.SetPercentage(NormalizeDouble(new_percentage, 2)); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CDynamicFOptimal : public CExtraModifications { private: int num_pos; // Número total de posiciones cerradas double profit[]; // Profits de operaciones ganadoras double loss[]; // Pérdidas (valor absoluto) double sum_profit; // Suma acumulada de profits double sum_loss; // Suma acumulada de pérdidas double avg_profit; // Promedio de profits double avg_loss; // Promedio de pérdidas double rr; // Relación ganancia/pérdida (R) double win_probability; // Probabilidad de ganar (p) int min_trades; // Mínimo de operaciones para Kelly double kelly_fraction; // Fracción de Kelly (por ejemplo, 0.5) int max_data; // Máximo número de operaciones a almacenar bool enable_kelly_mod; // Agregar como variable de clase public: CDynamicFOptimal(int min_trades_, int max_data_to_update, double kelly_fraction_) : CExtraModifications(LP_GMLPO) { m_modifier_name = "Optimal F Modifier"; num_pos = 0; sum_profit = 0.0; sum_loss = 0.0; avg_profit = 0.0; avg_loss = 0.0; rr = 0.0; win_probability = 0.0; min_trades = MathMax(min_trades_, 10); kelly_fraction = MathMax(0.0, MathMin(kelly_fraction_, 1.0)); // Corregir max_data = MathMax(max_data_to_update, 30); enable_kelly_mod = true; ArrayResize(profit, 0); ArrayResize(loss, 0); } void TrimOldData() { int total_data = ArraySize(profit) + ArraySize(loss); if(total_data <= max_data) return; while(total_data > max_data) { if(ArraySize(profit) > 0 && (ArraySize(loss) == 0 || profit[0] <= loss[0])) { sum_profit -= profit[0]; ArrayRemove(profit, 0, 1); int win_count = ArraySize(profit); avg_profit = win_count > 0 ? sum_profit / win_count : 0.0; num_pos--; // Decrementar } else if(ArraySize(loss) > 0) { sum_loss -= loss[0]; ArrayRemove(loss, 0, 1); int loss_count = ArraySize(loss); avg_loss = loss_count > 0 ? sum_loss / loss_count : 0.0; num_pos--; // Decrementar } total_data = ArraySize(profit) + ArraySize(loss); } } void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) override { double profit_value = on_open_close_struct.position.profit; num_pos++; if(profit_value == 0) { Print("Break-even operation detected"); return; } if(profit_value > 0) { AddArrayNoVerification(profit, profit_value, 0); sum_profit += profit_value; int win_count = ArraySize(profit); avg_profit = win_count > 0 ? sum_profit / win_count : 0.0; } else { double val = fabs(profit_value); AddArrayNoVerification(loss, val, 0); sum_loss += fabs(profit_value); int loss_count = ArraySize(loss); avg_loss = loss_count > 0 ? sum_loss / loss_count : 0.0; } TrimOldData(); int win_count = ArraySize(profit); int total_data = ArraySize(profit) + ArraySize(loss); win_probability = total_data > 0 ? (double)win_count / total_data : 0.0; rr = avg_loss > 0 ? avg_profit / avg_loss : 0.0; PrintFormat("%s: Debug | win_count: %d, loss_count: %d, avg_profit: %.2f, avg_loss: %.2f", __FUNCTION__, ArraySize(profit), ArraySize(loss), avg_profit, avg_loss); if(enable_kelly_mod && num_pos >= min_trades && rr > 0 && win_probability > 0) { double kelly = (win_probability * (rr + 1) - 1) / rr; kelly = MathMax(0.0, MathMin(kelly, 1.0)); double adjusted_kelly = kelly * kelly_fraction; m_modifier.SetPercentage(adjusted_kelly * 100.0); PrintFormat("%s: Info | Kelly ajustado: %+.2f, Probabilidad: %.2f, R: %.2f, Operaciones: %d", __FUNCTION__, adjusted_kelly * 100.0, win_probability, rr, num_pos); } } }; //+------------------------------------------------------------------+ //| Clase manager para modidicadores | //+------------------------------------------------------------------+ class CManagerModificator : public CManagerBase { public: CManagerModificator() : CManagerBase(false) { } inline void OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct); inline void OnOpenPosition(ModifierOnOpenCloseStruct &on_open_close_struct); inline void OnNewDay(); }; //+------------------------------------------------------------------+ inline void CManagerModificator::OnClosePosition(ModifierOnOpenCloseStruct &on_open_close_struct) { for(int i = 0; i < total; i++) items[i].OnClosePosition(on_open_close_struct); } //+------------------------------------------------------------------+ inline void CManagerModificator::OnOpenPosition(ModifierOnOpenCloseStruct &on_open_close_struct) { for(int i = 0; i < total; i++) items[i].OnOpenPosition(on_open_close_struct); } //+------------------------------------------------------------------+ inline void CManagerModificator::OnNewDay(void) { for(int i = 0; i < total; i++) items[i].OnNewDay(); } /* ADVERTENCIA al momeneto de añadir items a esta clase, esta si son dinamicos las eliminara cuidado. WARNING when adding items to this class, if they are dynamic, be careful. */ //+------------------------------------------------------------------+ #endif //+------------------------------------------------------------------+