//+------------------------------------------------------------------+ //| Base.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_BASE_MQH #define MQLARTICLES_RM_LOSSPROFIT_BASE_MQH //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include "..\\RiskManagementBases.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CLossProfit; // Advance declaration for the typedef //+------------------------------------------------------------------+ //| Defines | //+------------------------------------------------------------------+ typedef bool (*FuncionLossProfitSuperate)(const double& value, const double& saved_value, void* ptr); typedef void (*FLossPoriftDynamic)(CLossProfit&, Dynamic_LossProfit&, Loss_Profit&, bool&, bool&, const int, int&, int&, const int, double&); #define MAX_VAL_DEFINES_CLOSS_PROFIT (DBL_MAX) #define MIN_VAL_DEFINES_CLOSE_PROFIT (-DBL_MAX) //--- __forceinline bool LossProfitEmptyFuncionSup(const double& value, const double& saved_value, void* ptr) { return false; } //--- Tipos basicos #define LOSSPROFIT_TYPE_GMLPO 0 // Tipo base % a arriesgar //--- #define LOSSPROFIT_TYPE_MDL 1 // Maxima perdida diaria #define LOSSPROFIT_TYPE_MWL 2 // Maxima perdida semanal #define LOSSPROFIT_TYPE_MML 3 // Maxima perdida mensual #define LOSSPROFIT_TYPE_ML 4 // Maxima perdida #define LOSSPROFIT_TYPE_MDDPICO 5 // Maxima perdida desde nuevo pico de la cuenta //--- #define LOSSPROFIT_TYPE_MDP 6 // Maxima ganancia diaria #define LOSSPROFIT_TYPE_MWP 7 // Maxima ganancia semanal #define LOSSPROFIT_TYPE_MMP 8 // Maxima ganancia mensual #define LOSSPROFIT_TYPE_MP 9 // Maxima ganancia #define LOSSPROFIT_TYPE_MDDBAJO 10 // Maxima ganancia desde el bajo de la cuenta //--- Losses Profits #define LOSS_PROFIT_COUNT 11 //+------------------------------------------------------------------+ //| CLossProfit Class | //+------------------------------------------------------------------+ class CLossProfit : public CAccountGestor { protected: //--- General const bool m_active_hook_on_superated; const int m_type; // Type of maximum loss or profit. Loss_Profit m_lp; // Structure that will store info of this maximum loss or profit. FLossPoriftDynamic m_f_change_dynamic; // Function for dynamic risk double m_initial_calc_value; // Initial percentage bool m_empty_flag; // Empty flag double m_saved_value; // Valor base string m_name; // Nombre ENUM_LOSS_PROFIT m_type_loss_or_profit; // Tipo FuncionLossProfitSuperate m_funcion_check_sup; // Funcion custom que chekea si se ha superado const ulong m_magic_number; // Numero magico para filtrar //--- void* m_ptr_to_superate; // Ptr al superar //--- CRiskManagemetBase *m_risk; // Risk management to which this class is subject CHashMap m_balance_risk_map; // HashMap for dynamic risk (percentages to exceed and new risk percentage) //--- Dynamic risk Dynamic_LossProfit m_dynamic_lp; // Structure containing the % to exceed and risks to apply bool m_activate_dynamic_risk_per_operation; // Flag indicating if dynamic risk will be applied to this "maximum loss or profit" double m_chosen_balance; // Chosen initial balance int m_index_cambio; // Change index between positives and negatives int m_size_dynamic; // Size of changes bool m_min_val_is; // Minimum value exceeded bool m_max_val_is; // Maximum value exceeded int m_pos_derecha; // Current position on right int m_pos_izquierda; // Current position on left double m_new_balance_to_overcome; // Next balance to overcome // Cache for dynamic risk parameters string m_cache_percentages_to_activate; string m_cache_risks_to_be_applied; //--- virtual void OnSuperated() {} public: //--- Constructor CLossProfit(const int type, const bool im_empty, const bool act_hook, CRiskManagemetBase* _risk_pointer); ~CLossProfit() {} //--- Setters basicos void SetDynamic(string percentages_to_activate, string risks_to_be_applied, double _chosen_balance); void SetDynamic(double _chosen_balance); void SetPtrSuperated(void* ptr) { m_ptr_to_superate = ptr; } //--- Dynamic risk // Verifies and checks if the risk can be modified (only works in dynamic risk) inline void CheckAndModifyThePercentage(); // Returns true if the risk is dynamic inline bool IsDynamicMode() const { return m_activate_dynamic_risk_per_operation; } //--- General getters // Returns true if the "maximum loss or profit" has been exceeded bool IsSuperated() const; __forceinline bool IsEmpty() const { return m_empty_flag; } // Esta vacio ? inline string Name() const { return m_name; } // Nombre inline ENUM_APPLIED_PERCENTAGES AppliedType() const { return m_lp.percentage_applied_to; } // Porcentage aplicado inline int Type() const { return m_type; } // Tipo inline ENUM_LOSS_PROFIT TypeLossProfit() const { return m_type_loss_or_profit; } // Tipo de loss profit //--- Functions to work with the (Percentage | Value | Profit) of the "maximum loss or profit" // Set value with calc value virtual inline bool Set() = 0; virtual bool ForzeCalcValue() const = 0; // Initial calc value (percentage or money) inline double GetInitialCalcValue() const { return m_initial_calc_value; } // Getter inline double SetInitialCalcValue() const { return (((CLossProfit*)&this).m_lp.calculation_value = m_initial_calc_value); } // Setter // Current value __forceinline double GetValue() const { return m_lp.value; } // Getter bool SetValue(double val); // Setter // Percentage (given that if the calculation mode is money, calculation_value has the money value, of course. // But modifying "m_lp.calculation_value" has no effect. inline double GetCalcValue() const { return m_lp.calculation_value; } // Getter virtual bool SetCalcValue(double val) const = 0; // Setter // Saved value double SetSavedValue() { return(m_saved_value = m_lp.value); } inline double GetSavedValue() const { return m_saved_value; } // Magic number __forceinline ulong MagicNumber() const { return m_magic_number; } //--- Static functions for risk modification (only used for dynamic risk) static void CheckPositives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_is_sup, bool &empty_, const int size_cambios, int& curr_pos, int &empty_i, const int empy_a, double& new_balance_to_superate_arriba); static void CheckNegatives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_is_sup, bool &empty_, const int size_cambios, int& curr_pos, int &empty_i, const int empy_a, double& new_balance_to_superate_arriba); static void CheckNegativesAndPositives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_sup, bool& max_val_sup, const int size_cambios, int& curr_pos_izq, int& curr_pos_drc, const int index_change, double& new_balance_to_superate_arriba); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CLossProfit::CLossProfit(const int type, const bool im_empty, const bool act_hook, CRiskManagemetBase* _risk_pointer) : //--- Basico m_empty_flag(im_empty), m_type(type), m_initial_calc_value(0.00), m_saved_value(0.00), m_ptr_to_superate(NULL), //--- Hook m_active_hook_on_superated(act_hook), //--- Riesgo dinamico m_min_val_is(false), m_max_val_is(false), m_pos_derecha(0), m_pos_izquierda(0), m_f_change_dynamic(NULL), m_activate_dynamic_risk_per_operation(false), m_index_cambio(0), m_chosen_balance(0.00), m_new_balance_to_overcome(0.00), m_cache_percentages_to_activate(NULL), m_cache_risks_to_be_applied(NULL), m_funcion_check_sup(NULL), m_magic_number(im_empty ? NOT_MAGIC_NUMBER : _risk_pointer.MagicNumber()) { //--- if(m_empty_flag) return; //--- this.m_risk = _risk_pointer; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CLossProfit::IsSuperated(void) const { if(m_funcion_check_sup(m_lp.value, m_saved_value, m_ptr_to_superate)) { if(m_active_hook_on_superated) ((CLossProfit*)&this).OnSuperated(); return true; } return false; } //+------------------------------------------------------------------+ //| Establecer el valor | //+------------------------------------------------------------------+ bool CLossProfit::SetValue(double val) { //--- if(m_empty_flag) { #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogWarning(StringFormat("Trying to modify value in empty object '%s'", m_name), FUNCION_ACTUAL); #endif return false; } //--- if(val <= 0.0000001) { LogError(StringFormat("Invalid value %.2f for '%s': must be greater than 0.00", val, m_name), FUNCION_ACTUAL); return false; } //--- #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogInfo(StringFormat("Risk value '%s' updated: %.2f", m_name, val), FUNCION_ACTUAL); #endif //--- m_lp.value = val; return true; } //+------------------------------------------------------------------+ //| Establecer el riesgo dinamico | //+------------------------------------------------------------------+ void CLossProfit::SetDynamic(double _chosen_balance) { if(m_cache_percentages_to_activate == NULL || m_cache_risks_to_be_applied == NULL) { LogError("Invalid strings, first call SetDynamic(string, string, double)", FUNCION_ACTUAL); return; } //--- SetDynamic(m_cache_percentages_to_activate, m_cache_risks_to_be_applied, _chosen_balance); } //+------------------------------------------------------------------+ void CLossProfit::SetDynamic(string percentages_to_activate, string risks_to_be_applied, double _chosen_balance) { //--- if(m_empty_flag) { LogError("The risk is invalid", FUNCION_ACTUAL); this.m_activate_dynamic_risk_per_operation = false; Remover(); return; } //--- StrTo::CstArray(this.m_dynamic_lp.balance_to_activate_the_risk, percentages_to_activate, ','); StrTo::CstArray(this.m_dynamic_lp.risk_to_be_adjusted, risks_to_be_applied, ','); //--- if(this.m_dynamic_lp.risk_to_be_adjusted.Size() < 1 || this.m_dynamic_lp.balance_to_activate_the_risk.Size() < 1) { LogCriticalError("The size of the array is less than 1", FUNCION_ACTUAL); this.m_activate_dynamic_risk_per_operation = false; return; } if(this.m_dynamic_lp.balance_to_activate_the_risk.Size() != this.m_dynamic_lp.risk_to_be_adjusted.Size()) { LogCriticalError("The double arrays for the risk due to dynamic operation are not equal", FUNCION_ACTUAL); this.m_activate_dynamic_risk_per_operation = false; return; } //--- Cache values m_cache_percentages_to_activate = percentages_to_activate; m_cache_risks_to_be_applied = risks_to_be_applied; //--- if(IsInfoLogEnabled()) { FastLog(FUNCION_ACTUAL, INFO_TEXT, "Arrays before revision"); PrintArrayAsTable(m_dynamic_lp.balance_to_activate_the_risk, "Negative percentages to modify the risk", "balance"); PrintArrayAsTable(m_dynamic_lp.risk_to_be_adjusted, "Risk to be adjusted", "new risk"); } //--- m_balance_risk_map.Clear(); int indexes_to_remove[]; this.m_chosen_balance = _chosen_balance; //--- for(int i = 0 ; i < ArraySize(m_dynamic_lp.balance_to_activate_the_risk) ; i++) { if(m_dynamic_lp.balance_to_activate_the_risk[i] == 0) { LogWarning("The percentage value that will be exceeded to modify the risk is 0 or less than this (it will not be taken into account)", FUNCION_ACTUAL); AddArrayNoVerification(indexes_to_remove, i, 0); continue; } if(m_balance_risk_map.ContainsKey(m_dynamic_lp.balance_to_activate_the_risk[i]) == false) m_balance_risk_map.Add(m_dynamic_lp.balance_to_activate_the_risk[i], m_dynamic_lp.risk_to_be_adjusted[i]); else AddArrayNoVerification(indexes_to_remove, i, 0); } //--- RemoveMultipleIndexes(m_dynamic_lp.balance_to_activate_the_risk, indexes_to_remove, 0); ArraySort(m_dynamic_lp.balance_to_activate_the_risk); ArrayResize(m_dynamic_lp.risk_to_be_adjusted, ArraySize(m_dynamic_lp.balance_to_activate_the_risk)); //--- this.m_size_dynamic = ArraySize(m_dynamic_lp.balance_to_activate_the_risk); //--- const bool hay_positivos = HayPositivosArray(m_dynamic_lp.balance_to_activate_the_risk); const bool hay_negativos = HayNegativosArray(m_dynamic_lp.balance_to_activate_the_risk); bool c = false; //--- for(int i = 0 ; i < this.m_size_dynamic; i++) { double value; m_balance_risk_map.TryGetValue(this.m_dynamic_lp.balance_to_activate_the_risk[i], value); m_dynamic_lp.risk_to_be_adjusted[i] = value; m_dynamic_lp.balance_to_activate_the_risk[i] = (this.m_chosen_balance + (this.m_chosen_balance * (m_dynamic_lp.balance_to_activate_the_risk[i] / 100.0))); if(i < ArraySize(m_dynamic_lp.balance_to_activate_the_risk) - 1 && hay_negativos && hay_positivos && !c) { if(m_dynamic_lp.balance_to_activate_the_risk[i] > 0 && m_dynamic_lp.balance_to_activate_the_risk[i + 1] < 0) { m_index_cambio = i + 1; //WARNING: left position is used by default for the simple case only negatives or positives. } } } //--- this.m_min_val_is = false; this.m_max_val_is = false; //--- if(hay_negativos && !hay_positivos) { m_pos_izquierda = this.m_size_dynamic - 1; m_new_balance_to_overcome = MAX_VAL_DEFINES_CLOSS_PROFIT; m_f_change_dynamic = CLossProfit::CheckNegatives; } else if(hay_positivos && !hay_negativos) { m_pos_izquierda = 0; m_new_balance_to_overcome = MIN_VAL_DEFINES_CLOSE_PROFIT; m_f_change_dynamic = CLossProfit::CheckPositives; } else { m_pos_izquierda = m_index_cambio + 1; m_pos_derecha = m_index_cambio; m_f_change_dynamic = CLossProfit::CheckNegativesAndPositives; } //--- this.m_activate_dynamic_risk_per_operation = true; if(IsInfoLogEnabled()) { FastLog(FUNCION_ACTUAL, INFO_TEXT, "Arrays ready"); PrintArrayAsTable(m_dynamic_lp.balance_to_activate_the_risk, "Negative percentages to modify the risk", "balance"); PrintArrayAsTable(m_dynamic_lp.risk_to_be_adjusted, "Risk to be adjusted", "new risk"); } } //+------------------------------------------------------------------+ //| Checks and modifies the percentage | //+------------------------------------------------------------------+ inline void CLossProfit::CheckAndModifyThePercentage(void) { if(!m_activate_dynamic_risk_per_operation) { LogError(StringFormat("Risk %s is not allowed to use dynamic risk", m_name), FUNCION_ACTUAL); Remover(); return; } this.m_f_change_dynamic(this, m_dynamic_lp, m_lp, m_min_val_is, m_max_val_is, m_size_dynamic, m_pos_izquierda, m_pos_derecha, m_index_cambio, m_new_balance_to_overcome); // Print(m_lp.calculation_value); } //+------------------------------------------------------------------+ //| Funciones estaticas para chekear y modficar el porcentaje | //+------------------------------------------------------------------+ static void CLossProfit::CheckPositives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_is_sup, bool &empty_, const int size_cambios, int& curr_pos, int &empty_i, const int empy_a, double& new_balance_to_superate_arriba) { //--- const double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); //--- if(!min_val_is_sup && account_equity > l_p.balance_to_activate_the_risk[curr_pos]) //toca aumentar riesgo o parar { bool normal = false; const int last = size_cambios - 1; while(curr_pos < last) { if(account_equity > l_p.balance_to_activate_the_risk[curr_pos++] && l_p.balance_to_activate_the_risk[curr_pos] > account_equity) { normal = true; break; } } if(normal) { new_balance_to_superate_arriba = l_p.balance_to_activate_the_risk[curr_pos - 1]; lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos - 1]; } else if(account_equity > l_p.balance_to_activate_the_risk[curr_pos]) { //verificamos si es mayor, si lo es entonces asiganmos como el maixmo, (hacemos esto por que si size_cambio es 1 no entratra al while, si es mayor a 2 esta verificaion no tiene sentido ) //pero para no estar haciendo otro else if, enotnce shacmeos esto lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos]; //llegamos al maximo asignamos el maximo posible new_balance_to_superate_arriba = curr_pos > 0 ? l_p.balance_to_activate_the_risk[curr_pos - 1] : l_p.balance_to_activate_the_risk[curr_pos]; min_val_is_sup = true; //llegamos al maximo } } //--- if(account_equity < new_balance_to_superate_arriba) //toca disminir riesgo o parar { bool normal = false; while(curr_pos > 0) { curr_pos--; if(l_p.balance_to_activate_the_risk[curr_pos] < account_equity) { normal = true; break; } } if(normal) { lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos]; new_balance_to_superate_arriba = l_p.balance_to_activate_the_risk[curr_pos]; curr_pos++; } else { new_balance_to_superate_arriba = MIN_VAL_DEFINES_CLOSE_PROFIT; // En este caso si o si es porcentaje lossprofit.calculation_value = ptr.GetInitialCalcValue(); //nos pasamos del miinimo entonces aisgnamos el porcentake inicial } min_val_is_sup = false; //Reseteamos, ahora si es pobible aumentar el riesgo } } //+------------------------------------------------------------------+ static void CLossProfit::CheckNegatives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_is_sup, bool &empty_, const int size_cambios, int& curr_pos, int &empty_i, const int empy_a, double& new_balance_to_superate_arriba) { //--- const double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); //--- if(!min_val_is_sup && account_equity < l_p.balance_to_activate_the_risk[curr_pos]) //toca aumentar riesgo o parar { bool normal = false; while(curr_pos > 0) { curr_pos--; if(account_equity > l_p.balance_to_activate_the_risk[curr_pos]) { normal = true; break; } } if(normal) empty_i = curr_pos + 1; //curpos apunta abajo, y empty a arriab else { empty_i = curr_pos; //ambos abajo pero eso cambia min_val_is_sup = true; //llegamos al minimo } //--- lossprofit.calculation_value = l_p.risk_to_be_adjusted[empty_i]; new_balance_to_superate_arriba = l_p.balance_to_activate_the_risk[empty_i]; //arriba } //--- if(account_equity > new_balance_to_superate_arriba) //toca disminir riesgo o parar { const int last = size_cambios - 1; bool normal = false; while(curr_pos < last) { if(account_equity < l_p.balance_to_activate_the_risk[curr_pos++] && account_equity < l_p.balance_to_activate_the_risk[curr_pos]) { normal = true; break; } } if(normal) { lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos]; //usa el riesgo de aqui empty_i = curr_pos; new_balance_to_superate_arriba = l_p.balance_to_activate_the_risk[empty_i]; curr_pos--; //apunta abajo } else //nos pasmaos entonces, riesgo inicial { lossprofit.calculation_value = ptr.GetInitialCalcValue(); // En este caso si o si es porcentaje new_balance_to_superate_arriba = MAX_VAL_DEFINES_CLOSS_PROFIT; } min_val_is_sup = false; //llegamos al maximo } } //+------------------------------------------------------------------+ static void CLossProfit::CheckNegativesAndPositives(CLossProfit& ptr, Dynamic_LossProfit& l_p, Loss_Profit &lossprofit, bool& min_val_sup, bool& max_val_sup, const int size_cambios, int& curr_pos_izq, int& curr_pos_drc, const int index_change, double& new_balance_to_superate_arriba) { //--- const double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); // PrintFormat("%d - %d - cambio en: %d",curr_pos_izq,curr_pos_drc, index_change); //--- if(!min_val_sup && account_equity < l_p.balance_to_activate_the_risk[curr_pos_drc]) //toca aumentar riesgo o parar { if(max_val_sup) curr_pos_izq++; //size + 1, y luego en while ya se ve la direfencia de 1 //--- bool normal = false; bool mid = false; //--- while(curr_pos_drc > 0) { curr_pos_izq--; curr_pos_drc--; if(account_equity > l_p.balance_to_activate_the_risk[curr_pos_drc]) { mid = curr_pos_drc == index_change; normal = true; break; } } //--- if(mid) lossprofit.calculation_value = ptr.GetInitialCalcValue(); // En este caso si o si es porcentaje else if(normal) { const int sum = curr_pos_drc < index_change; //si es lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos_drc + sum]; //+0 si estamos en linea psoitov y +1 si en negacitva (dado que en positiva los porecntjases son <-) } else { lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos_drc]; min_val_sup = true; //llegamos al minimo curr_pos_izq = curr_pos_drc; } max_val_sup = false; } //--- if(!max_val_sup && account_equity > l_p.balance_to_activate_the_risk[curr_pos_izq]) //toca disminir riesgo o parar { if(min_val_sup) { curr_pos_drc--; //-1 } const int last = size_cambios - 1; bool normal = false; bool mid = false; while(curr_pos_izq < last) { curr_pos_drc++; curr_pos_izq++; if(l_p.balance_to_activate_the_risk[curr_pos_izq] > account_equity && account_equity > l_p.balance_to_activate_the_risk[curr_pos_drc]) { normal = true; mid = curr_pos_drc == index_change; break; } } if(mid) lossprofit.calculation_value = ptr.GetInitialCalcValue(); // En este caso si o si es porcentaje else if(normal) { const int sum = curr_pos_izq < index_change; //si es lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos_izq + sum]; } else //nos pasmaos entonces, riesgo inicial { lossprofit.calculation_value = l_p.risk_to_be_adjusted[curr_pos_izq]; //maximo max_val_sup = true; //llegamos al maximo curr_pos_drc = curr_pos_izq; } min_val_sup = false; } //--- //PrintFormat("%d - %d - cambio en: %d",curr_pos_izq,curr_pos_drc, index_change); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template class CLossProfitPercentage : public TPadre { public: //--- CLossProfitPercentage(const int type, const bool im_empty, const bool act_hook, CRiskManagemetBase* &_risk_pointer) : TPadre(type, im_empty, act_hook, _risk_pointer) {} ~CLossProfitPercentage(void) {} //--- bool ForzeCalcValue() const override; bool SetCalcValue(double val) const override; bool Set() override; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool CLossProfitPercentage::ForzeCalcValue() const { if(m_empty_flag || m_type == LOSSPROFIT_TYPE_GMLPO) // Si esta vacio o es gmplo retornamos return false; //--- const double dff = GetSavedValue() - m_lp.value; CLossProfitPercentage* este = (CLossProfitPercentage*)&this; este.m_lp.value = m_risk.GetValorWithApplied(m_lp.calculation_value, m_type, m_lp.percentage_applied_to) - dff; este.m_saved_value = este.m_lp.value; return true; } //+------------------------------------------------------------------+ template inline bool CLossProfitPercentage::Set() { if(m_empty_flag) return false; m_lp.value = m_risk.GetValorWithApplied(m_lp.calculation_value, m_type, m_lp.percentage_applied_to); #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogCaution(StringFormat("New value of %s: %.2f", this.m_name, m_lp.value), FUNCION_ACTUAL); #endif return true; } //+------------------------------------------------------------------+ template bool CLossProfitPercentage::SetCalcValue(double val) const { //--- if(m_empty_flag) { #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogWarning(StringFormat("Trying to modify percentage on empty object '%s'", m_name), FUNCION_ACTUAL); #endif return false; } //--- if(val <= 0.00) { LogError(StringFormat("Invalid percentage %.2f%% for '%s': must be greater than 0", val, m_name), FUNCION_ACTUAL); return false; } //--- if(val >= 100.00) { LogWarning(StringFormat("Percentage %.2f for '%s' exceeds 100%%, limiting to 100%%", val, m_name), FUNCION_ACTUAL); val = 100.00; } //--- LogInfo(StringFormat("Risk percentage '%s' updated: %.2f%%", m_name, val), FUNCION_ACTUAL); //--- ((CLossProfitPercentage*)&this).m_lp.calculation_value = val; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template class CLossProfitMoney : public TPadre { public: //--- CLossProfitMoney(const int type, const bool im_empty, const bool act_hook, CRiskManagemetBase* &_risk_pointer) : TPadre(type, im_empty, act_hook, _risk_pointer) {} ~CLossProfitMoney(void) {} //--- bool SetCalcValue(double val) const override; bool Set() override; bool ForzeCalcValue() const override; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool CLossProfitMoney::ForzeCalcValue() const { if(m_empty_flag || m_type == LOSSPROFIT_TYPE_GMLPO) // Si esta vacio o es gmplo retornamos return false; //--- const double dff = GetSavedValue() - m_lp.calculation_value; CLossProfitMoney* este = (CLossProfitMoney*)&this; este.m_lp.value = m_lp.calculation_value - dff; este.m_saved_value = este.m_lp.value; return true; } //+------------------------------------------------------------------+ template inline bool CLossProfitMoney::Set() { if(m_empty_flag) return false; m_lp.value = m_lp.calculation_value; #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogCaution(StringFormat("New value of %s: %.2f", this.m_name, m_lp.value), FUNCION_ACTUAL); #endif return true; } //+------------------------------------------------------------------+ template bool CLossProfitMoney::SetCalcValue(double val) const { //--- if(m_empty_flag) { #ifdef MORE_INFO_LOSS_PROFIT_DEFINE LogWarning(StringFormat("Trying to modify base money on empty object '%s'", m_name), FUNCION_ACTUAL); #endif return false; } //--- if(val <= 0.00) { LogError(StringFormat("Invalid new base money %.2f for '%s': must be greater than 0.00", val, m_name), FUNCION_ACTUAL); return false; } //--- LogInfo(StringFormat("Risk base money '%s' updated: %.2f", m_name, val), FUNCION_ACTUAL); //--- ((CLossProfitMoney*)&this).m_lp.calculation_value = val; return true; } //+------------------------------------------------------------------+ #endif // MQLARTICLES_RM_LOSSPROFIT_BASE_MQH