533 lines
39 KiB
MQL5
533 lines
39 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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<CExtraModifications>
|
|
{
|
|
public:
|
|
CManagerModificator() : CManagerBase<CExtraModifications>(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
|
|
//+------------------------------------------------------------------+
|