//+------------------------------------------------------------------+ //| MaxLosses.mqh | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372 | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372" #property strict #ifndef MQLARTICLES_RM_LOSSPROFIT_BASIC_MAXLOSSES_MQH #define MQLARTICLES_RM_LOSSPROFIT_BASIC_MAXLOSSES_MQH //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "Classes.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template class CLossLossGmlpo : public CLossProfitBaseLoss { public: CLossLossGmlpo(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossGmlpo(void) {} }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template CLossLossGmlpo::CLossLossGmlpo(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_GMLPO, empty, false, risk_pointer) { } //+------------------------------------------------------------------+ //| MDL - Maximum Daily Loss | //+------------------------------------------------------------------+ template class CLossLossMaxDaily : public CLossProfitBaseLoss { public: CLossLossMaxDaily(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossMaxDaily(void) { if(!m_empty_flag && CBasicEvents::IsActive()) CBasicEvents::UnregisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_DAY); } //--- void OnNewDay(const datetime curr_time) override final; }; //+------------------------------------------------------------------+ template CLossLossMaxDaily::CLossLossMaxDaily(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_MDL, empty, false, risk_pointer) { if(empty) return; CBasicEvents::RegisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_DAY); // Registramos para recibir OnNewDay } //+------------------------------------------------------------------+ template void CLossLossMaxDaily::OnNewDay(const datetime curr_time) { if(Set()) m_saved_value = m_lp.value; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| MWL - Maximum Weekly Loss | //+------------------------------------------------------------------+ template class CLossLossMaxWeekly : public CLossProfitBaseLoss { public: CLossLossMaxWeekly(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossMaxWeekly(void) { if(!m_empty_flag && CBasicEvents::IsActive()) CBasicEvents::UnregisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_WEEK); } //--- void OnNewWeek(const datetime curr_time) override final; }; //+------------------------------------------------------------------+ template CLossLossMaxWeekly::CLossLossMaxWeekly(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_MWL, empty, false, risk_pointer) { if(empty) return; CBasicEvents::RegisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_WEEK); // Registramos para recibir OnNewWeek } //+------------------------------------------------------------------+ template void CLossLossMaxWeekly::OnNewWeek(const datetime curr_time) { if(Set()) m_saved_value = m_lp.value; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| MML - Maximum Monthly Loss | //+------------------------------------------------------------------+ template class CLossLossMaxMon : public CLossProfitBaseLoss { public: CLossLossMaxMon(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossMaxMon(void) { if(!m_empty_flag && CBasicEvents::IsActive()) CBasicEvents::UnregisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_MON); } //--- void OnNewMonth(const datetime curr_time) override final; }; //+------------------------------------------------------------------+ template CLossLossMaxMon::CLossLossMaxMon(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_MML, empty, false, risk_pointer) { if(empty) return; CBasicEvents::RegisterEvent(&this, BASICEVENT_REG_FLAG_ON_NEW_MON); // Registramos para recibir OnNewMonth } //+------------------------------------------------------------------+ template void CLossLossMaxMon::OnNewMonth(const datetime curr_time) { if(Set()) m_saved_value = m_lp.value; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| ML - Maximum Loss (sin reseteo temporal) | //+------------------------------------------------------------------+ template class CLossLossMax : public CLossProfitBaseLoss { public: CLossLossMax(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossMax(void) {} }; //+------------------------------------------------------------------+ template CLossLossMax::CLossLossMax(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_ML, empty, false, risk_pointer) { // No registra eventos temporales - tracking continuo } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Pérdida máxima desde pico | //+------------------------------------------------------------------+ #define LOSSPROFIT_MAXLOSS_FLAG_PICO_LISTO (1) template class CLossLossMaxDesdeArriba : public CLossProfitBaseLoss { private: uint8_t m_flags; double m_positive_profit; double m_max_positive_profit; FuncionLossProfitSuperate m_func_base; //--- void OnSuperated() override final; public: CLossLossMaxDesdeArriba(bool empty, CRiskManagemetBase* risk_pointer); ~CLossLossMaxDesdeArriba(void) {} //--- void OnLossProfit(const double p) override final; //--- void Init(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, bool _is_strict, FuncionLossProfitSuperate fsup); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template CLossLossMaxDesdeArriba::CLossLossMaxDesdeArriba(bool empty, CRiskManagemetBase *risk_pointer) : CLossProfitBaseLoss(LOSSPROFIT_TYPE_MDDPICO, empty, true, risk_pointer), m_func_base(NULL), m_positive_profit(0.00), m_max_positive_profit(0.00), m_flags(0) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CLossLossMaxDesdeArriba::Init(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, bool _is_strict, FuncionLossProfitSuperate fsup) { if(m_empty_flag) return; if(fsup == NULL) { LogError("Function superation cannot be NULL", FUNCION_ACTUAL); return; } m_func_base = fsup; CLossProfitBaseLoss::Init(_percentage, _applied, _is_strict, fsup); // Llamamos a la del padre m_funcion_check_sup = LossProfitEmptyFuncionSup; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CLossLossMaxDesdeArriba::OnSuperated() { m_flags = 0; m_positive_profit = 0.00; m_max_positive_profit = 0.00; m_funcion_check_sup = LossProfitEmptyFuncionSup; if(Set()) m_saved_value = m_lp.value; #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogInfo("Drawdown limit reached, resetting to search new peak", FUNCION_ACTUAL); #endif } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void CLossLossMaxDesdeArriba::OnLossProfit(const double p) { //--- if(m_magic_number != NOT_MAGIC_NUMBER && m_magic_number != account_status.LastDealMagic()) return; //--- const bool is_loss = (p < 0.00); m_positive_profit += p; //--- Nuevo pico detectado - recalcular objetivo if(m_positive_profit > m_max_positive_profit) { //--- Calculamos el nuevo valor (pérdida máxima desde este pico) if(Set()) m_saved_value = m_lp.value; m_flags = 0; m_max_positive_profit = m_positive_profit; m_funcion_check_sup = LossProfitEmptyFuncionSup; // Volver a buscar pico #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogInfo(StringFormat("New peak detected: %.2f, DD limit: %.2f", m_max_positive_profit, m_saved_value), FUNCION_ACTUAL); #endif } //--- Primera pérdida después de pico - activar tracking if(is_loss && m_max_positive_profit != 0.00 && (m_flags & LOSSPROFIT_MAXLOSS_FLAG_PICO_LISTO) == 0) { m_flags |= LOSSPROFIT_MAXLOSS_FLAG_PICO_LISTO; m_funcion_check_sup = m_func_base; // Activar verificación real #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogInfo("Drawdown tracking activated", FUNCION_ACTUAL); #endif } //--- Ajustar valor si estamos tracking drawdown if((m_flags & LOSSPROFIT_MAXLOSS_FLAG_PICO_LISTO) != 0) { if(is_loss) // Pérdida - aumenta el drawdown { m_lp.value += p; // p es negativo, suma a pérdida acumulada } else if(p > 0.00) // Profit - reduce el drawdown { const double nv = m_lp.value + p; m_lp.value = (!m_strict && nv > m_saved_value) ? nv : m_saved_value; } } } //+------------------------------------------------------------------+ #endif // MQLARTICLES_RM_LOSSPROFIT_BASIC_MAXLOSSES_MQH