958 lines
70 KiB
MQL5
958 lines
70 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| LossProfit.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_LOSS_PROFIT_MQH
|
|
#define RISK_LOSS_PROFIT_MQH
|
|
|
|
#include "RiskManagementBases.mqh"
|
|
|
|
class CLossProfit; //declaracion anticipada para el typedef
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Defines |
|
|
//+------------------------------------------------------------------+
|
|
typedef bool (*FuncionLossProfitSuperate)(double profit_, double value_, bool strict, double extra_value);
|
|
typedef void (*FLossPoriftDynamic)(CLossProfit&, Dynamic_LossProfit&, Loss_Profit&, bool&, bool&, const int, int&, int&, const int, double&);
|
|
#define MAX_VAL_DEFINES_CLOSS_PROFIT 1.7976931348623158e+300
|
|
#define MIN_VAL_DEFINES_CLOSE_PROFIT 1.7976931348623158e-300
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CLossProfit Class |
|
|
//+------------------------------------------------------------------+
|
|
class CLossProfit : public CLoggerBase
|
|
{
|
|
private:
|
|
//--- General
|
|
ENUM_TYPE_LOSS_PROFIT type; // Type of maximum loss or profit.
|
|
Loss_Profit lp; // Structure that will store info of this maximum loss or profit.
|
|
FuncionLossProfitSuperate funcion_check_superate; // Function that will "check" if this maximum loss or profit has been exceeded.
|
|
FLossPoriftDynamic f_change_dynamic; // Function for dynamic risk
|
|
double initial_percentage; // Initial percentage
|
|
bool empty_flag; // Flag indicating if "this" class is null
|
|
double curr_profit; // Current profit referring to this maximum loss or profit
|
|
bool is_strict; // Flag indicating that if this class is of maximum profit type, the verification of exceeding will be strict
|
|
long cv;
|
|
string name; // Type of maximum loss or profit in string
|
|
|
|
// Variables that store if the type of maximum loss or profit
|
|
// if it's a maximum loss = T_LOSS
|
|
// if it's a maximum profit = T_PROFIT
|
|
ENUM_LOSS_PROFIT type_loss_or_profit;
|
|
|
|
//---
|
|
CRiskManagemetBase *_risk; // Risk management to which this class is subject
|
|
CHashMap<double, double> balanceRiskMap; // HashMap for dynamic risk (percentages to exceed and new risk percentage)
|
|
|
|
//--- Dynamic risk
|
|
Dynamic_LossProfit dynamic_lp; // Structure containing the % to exceed and risks to apply
|
|
bool ActivateDynamicRiskPerOperation; // Flag indicating if dynamic risk will be applied to this "maximum loss or profit"
|
|
double chosen_balance; // Chosen initial balance
|
|
int index_cambio; // Change index between positives and negatives
|
|
int size_dynamic; // Size of changes
|
|
bool min_val_is; // Minimum value exceeded
|
|
bool max_val_is; // Maximum value exceeded
|
|
int pos_derecha; // Current position on right
|
|
int pos_izquierda; // Current position on left
|
|
double NewBalanceToOvercome; // Next balance to overcome
|
|
string m_cache_percentages_to_activate;
|
|
string m_cache_risks_to_be_applied;
|
|
|
|
//--- Extra value
|
|
double extra_value;
|
|
|
|
public:
|
|
//--- Constructor
|
|
CLossProfit(bool im_empty, CRiskManagemetBase *_risk_pointer);
|
|
|
|
//--- Main setters
|
|
inline bool Set();
|
|
void SetProperties(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT _type, FuncionLossProfitSuperate f_check_sup, bool _is_strict = false);
|
|
void SetDynamic(string percentages_to_activate, string risks_to_be_applied, double _chosen_balance);
|
|
void SetDynamic(double _chosen_balance);
|
|
|
|
//--- 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 ActivateDynamicRiskPerOperation; }
|
|
|
|
|
|
//--- General getters
|
|
// Returns true if the "maximum loss or profit" has been exceeded
|
|
inline bool IsSuperated() const;
|
|
|
|
// Returns true if "this" object is invalid
|
|
inline bool IsEmpty() const { return empty_flag; }
|
|
|
|
// Type of "maximum loss or profit" in string (character string)
|
|
inline string Name() const { return name; }
|
|
|
|
// Returns what this "maximum loss or profit" is "applied to" for its calculation
|
|
inline ENUM_APPLIED_PERCENTAGES GetAppliedType() const { return lp.percentage_applied_to; }
|
|
|
|
// Returns the calculation mode (money or % applied to "lp.percentage_applied_to")
|
|
inline ENUM_RISK_CALCULATION_MODE GetModeCalculation() const { return lp.mode_calculation_risk; }
|
|
|
|
// Returns the type of "maximum loss or profit" in ENUM format
|
|
inline ENUM_TYPE_LOSS_PROFIT GetType() const { return type; }
|
|
|
|
|
|
//--- Functions to work with the (Percentage | Value | Accumulated Profit) of the "maximum loss or profit"
|
|
// Initial percentage or money
|
|
inline double GetInitialPercentageOrMoney() const { return this.initial_percentage; } // Getter
|
|
inline void SetInitialPercentageOrMoney() { lp.assigned_percentage = this.initial_percentage; } // Setter
|
|
|
|
// Current value
|
|
inline double GetValue() const { return lp.value; } // Getter
|
|
bool SetValue(double val, int8_t i); // Setter
|
|
|
|
// Percentage (given that if the calculation mode is money, assigned_percentage has the money value, of course.
|
|
// But modifying "lp.assigned_percentage" has no effect.
|
|
inline double GetPercentage() const { return lp.assigned_percentage; } // Getter
|
|
bool SetPercentage(double val); // Setter
|
|
|
|
// Sets the profit
|
|
bool SetProfit(double val, int8_t i);
|
|
|
|
|
|
//--- 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(bool im_empty, CRiskManagemetBase * _risk_pointer)
|
|
: min_val_is(false), max_val_is(false), pos_derecha(0), pos_izquierda(0), type(WRONG_VALUE), funcion_check_superate(NULL), f_change_dynamic(NULL),
|
|
initial_percentage(0.00), empty_flag(false), is_strict(false), name(NULL), ActivateDynamicRiskPerOperation(false), index_cambio(0), type_loss_or_profit(WRONG_VALUE), extra_value(0.00),
|
|
chosen_balance(0.00), NewBalanceToOvercome(0.00), m_cache_percentages_to_activate(NULL), m_cache_risks_to_be_applied(NULL)
|
|
{
|
|
this.empty_flag = im_empty;
|
|
if(empty_flag)
|
|
return;
|
|
|
|
this._risk = _risk_pointer;
|
|
|
|
//---
|
|
this.cv = _risk.d();
|
|
this.extra_value = _risk.GetExtraValueForLossProfit();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to set the percentage |
|
|
//+------------------------------------------------------------------+
|
|
//#define DEBUG_LOSS_PROFIT_DEFINE
|
|
|
|
//---
|
|
bool CLossProfit::SetPercentage(double val)
|
|
{
|
|
//---
|
|
if(empty_flag)
|
|
{
|
|
#ifdef DEBUG_LOSS_PROFIT_DEFINE
|
|
LogWarning(StringFormat("Trying to modify percentage on empty object '%s'", name), FUNCION_ACTUAL);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(val <= 0.00)
|
|
{
|
|
LogError(StringFormat("Invalid percentage %.2f%% for '%s': must be greater than 0", val, name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(val >= 100.00)
|
|
{
|
|
LogWarning(StringFormat("Percentage %.2f for '%s' exceeds 100%%, limiting to 100%%", val, name), FUNCION_ACTUAL);
|
|
val = 100.00;
|
|
}
|
|
|
|
//---
|
|
LogInfo(StringFormat("Risk percentage '%s' updated: %.2f%%", name, val), FUNCION_ACTUAL);
|
|
lp.assigned_percentage = val;
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CLossProfit::SetValue(double val, int8_t i)
|
|
{
|
|
//---
|
|
if(empty_flag)
|
|
{
|
|
#ifdef DEBUG_LOSS_PROFIT_DEFINE
|
|
LogWarning(StringFormat("Trying to modify value in empty object '%s'", name), FUNCION_ACTUAL);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(cv != i)
|
|
{
|
|
LogError(StringFormat("Unauthorized access to modify the value of '%s'", name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(val <= 0.00)
|
|
{
|
|
LogError(StringFormat("Invalid value %.2f for '%s': must be greater than 0", val, name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
LogInfo(StringFormat("Risk value '%s' updated: %.2f", name, val), FUNCION_ACTUAL);
|
|
this.lp.value = val;
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function to set the profit |
|
|
//+------------------------------------------------------------------+
|
|
bool CLossProfit::SetProfit(double val, int8_t i)
|
|
{
|
|
//---
|
|
if(empty_flag)
|
|
{
|
|
#ifdef DEBUG_LOSS_PROFIT_DEFINE
|
|
LogWarning(StringFormat("Trying to modify profit on empty object '%s'", name), FUNCION_ACTUAL);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
if(cv != i)
|
|
{
|
|
LogError(StringFormat("Unauthorized access to modify profit of '%s'", name), FUNCION_ACTUAL);
|
|
return false;
|
|
}
|
|
|
|
//---
|
|
LogInfo(StringFormat("Updated '%s' profit: %.2f", name, val), FUNCION_ACTUAL);
|
|
this.curr_profit = val;
|
|
return true;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------+
|
|
//| Function to check if this maximum loss or profit has been exceeded |
|
|
//+--------------------------------------------------------------------------+
|
|
inline bool CLossProfit::IsSuperated() const
|
|
{
|
|
if(empty_flag || type == LP_GMLPO)
|
|
return false;
|
|
return funcion_check_superate(curr_profit, lp.value, this.is_strict, this.extra_value); //this.type_loss_or_profit == T_PROFIT ? _risk.IsSuperatedProfit(curr_profit, lp.value, mode, is_strict, type) : _risk.IsSuperatedLoss(curr_profit, lp.value, mode, type);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main setter function to set the properties |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfit::SetProperties(double _percentage, ENUM_APPLIED_PERCENTAGES _applied, ENUM_RISK_CALCULATION_MODE _mode, ENUM_TYPE_LOSS_PROFIT _type, FuncionLossProfitSuperate f_check_sup, bool _is_strict = false)
|
|
{
|
|
if(empty_flag)
|
|
return;
|
|
|
|
//--- Check
|
|
if(_percentage < 0.0000000001)
|
|
{
|
|
const string val = RiskCalcModeToString[_mode];
|
|
LogCriticalError(StringFormat("%s %.2f invalid, %s must be greater than 0.00", val, _percentage, val), FUNCION_ACTUAL);
|
|
empty_flag = true;
|
|
return;
|
|
}
|
|
|
|
//---
|
|
lp.assigned_percentage = _percentage;
|
|
lp.mode_calculation_risk = _mode;
|
|
lp.percentage_applied_to = _applied;
|
|
|
|
//---
|
|
this.type = _type;
|
|
this.initial_percentage = _percentage;
|
|
this.is_strict = _is_strict;
|
|
this.lp.value = (this.lp.mode_calculation_risk == money) ? lp.assigned_percentage : 0.00;
|
|
|
|
//--- Get the type
|
|
switch(_type)
|
|
{
|
|
case LP_GMLPO:
|
|
case LP_MDL:
|
|
case LP_MWL:
|
|
case LP_MML:
|
|
case LP_ML:
|
|
this.type_loss_or_profit = T_LOSS;
|
|
break;
|
|
|
|
case LP_MDP:
|
|
case LP_MWP:
|
|
case LP_MMP:
|
|
this.type_loss_or_profit = T_PROFIT;
|
|
break;
|
|
|
|
default:
|
|
LogFatalError(StringFormat("The maximum profit or loss type '%s' is invalid", EnumToString(_type)), FUNCION_ACTUAL);
|
|
Remover();
|
|
break;
|
|
}
|
|
|
|
switch(_type)
|
|
{
|
|
case LP_GMLPO:
|
|
this.name = "Gross maximum loss per operation"; // Risk per operation
|
|
break;
|
|
|
|
case LP_MDL:
|
|
this.name = "Maximum daily loss";
|
|
break;
|
|
|
|
case LP_MWL:
|
|
this.name = "Maximum weekly loss";
|
|
break;
|
|
|
|
case LP_MML:
|
|
this.name = "Maximum monthly loss";
|
|
break;
|
|
|
|
case LP_ML:
|
|
this.name = "Maximum total loss";
|
|
break;
|
|
|
|
case LP_MDP:
|
|
this.name = "Maximum daily profit";
|
|
break;
|
|
|
|
case LP_MWP:
|
|
this.name = "Maximum weekly profit";
|
|
break;
|
|
|
|
case LP_MMP:
|
|
this.name = "Maximum monthly profit";
|
|
break;
|
|
|
|
default:
|
|
LogFatalError(StringFormat("The maximum profit or loss type '%s' is invalid", EnumToString(_type)), FUNCION_ACTUAL);
|
|
Remover();
|
|
break;
|
|
}
|
|
|
|
if(f_check_sup == NULL)
|
|
{
|
|
LogFatalError(StringFormat("The function to check for exceeding '%s' is invalid.", this.name), FUNCION_ACTUAL);
|
|
Remover();
|
|
}
|
|
else
|
|
{
|
|
this.funcion_check_superate = f_check_sup;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------------------------------+
|
|
//| Function to automatically set the value of the "maximum loss or profit" |
|
|
//+------------------------------------------------------------------------------------------+
|
|
inline bool CLossProfit::Set()
|
|
{
|
|
if(empty_flag)
|
|
return false;
|
|
this.lp.value = lp.mode_calculation_risk == percentage ? _risk.GetValorWithApplied(lp.assigned_percentage, type, lp.percentage_applied_to) : lp.value;
|
|
LogCaution(StringFormat("New value of %s: %.2f", this.name, this.lp.value), FUNCION_ACTUAL);
|
|
return true;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Set properties for dynamic risk |
|
|
//+------------------------------------------------------------------+
|
|
//---
|
|
void CLossProfit::SetDynamic(double _chosen_balance)
|
|
{
|
|
if(m_cache_percentages_to_activate == NULL || m_cache_risks_to_be_applied == NULL)
|
|
{
|
|
LogError("Strings invalidos, primero llame a 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(empty_flag)
|
|
{
|
|
LogError("The risk is invalid", FUNCION_ACTUAL);
|
|
this.ActivateDynamicRiskPerOperation = false;
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
if(this.lp.mode_calculation_risk == money)
|
|
{
|
|
this.ActivateDynamicRiskPerOperation = false;
|
|
LogCriticalError("'Money' mode is not valid for dynamic risk, change it to 'Percentage %' or change the group mode to 'No dynamic risk for risk per operation' ", FUNCION_ACTUAL);
|
|
return;
|
|
}
|
|
|
|
//---
|
|
StrTo::CstArray(this.dynamic_lp.balance_to_activate_the_risk, percentages_to_activate, ',');
|
|
StrTo::CstArray(this.dynamic_lp.risk_to_be_adjusted, risks_to_be_applied, ',');
|
|
|
|
//---
|
|
if(this.dynamic_lp.risk_to_be_adjusted.Size() < 1 || this.dynamic_lp.balance_to_activate_the_risk.Size() < 1)
|
|
{
|
|
LogCriticalError("The size of the array is less than 1", FUNCION_ACTUAL);
|
|
this.ActivateDynamicRiskPerOperation = false;
|
|
return;
|
|
}
|
|
|
|
if(this.dynamic_lp.balance_to_activate_the_risk.Size() != this.dynamic_lp.risk_to_be_adjusted.Size())
|
|
{
|
|
LogCriticalError("The double arrays for the risk due to dynamic operation are not equal", FUNCION_ACTUAL);
|
|
this.ActivateDynamicRiskPerOperation = 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(dynamic_lp.balance_to_activate_the_risk, "Negative percentages to modify the risk", "balance");
|
|
PrintArrayAsTable(dynamic_lp.risk_to_be_adjusted, "Risk to be adjusted", "new risk");
|
|
}
|
|
|
|
//---
|
|
balanceRiskMap.Clear();
|
|
int indexes_to_remove[];
|
|
this.chosen_balance = _chosen_balance;
|
|
//---
|
|
for(int i = 0 ; i < ArraySize(dynamic_lp.balance_to_activate_the_risk) ; i++)
|
|
{
|
|
|
|
if(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(balanceRiskMap.ContainsKey(dynamic_lp.balance_to_activate_the_risk[i]) == false)
|
|
balanceRiskMap.Add(dynamic_lp.balance_to_activate_the_risk[i], dynamic_lp.risk_to_be_adjusted[i]);
|
|
else
|
|
AddArrayNoVerification(indexes_to_remove, i, 0);
|
|
}
|
|
|
|
//---
|
|
RemoveMultipleIndexes(dynamic_lp.balance_to_activate_the_risk, indexes_to_remove, 0);
|
|
ArraySort(dynamic_lp.balance_to_activate_the_risk);
|
|
ArrayResize(dynamic_lp.risk_to_be_adjusted, ArraySize(dynamic_lp.balance_to_activate_the_risk));
|
|
|
|
//---
|
|
this.size_dynamic = ArraySize(dynamic_lp.balance_to_activate_the_risk);
|
|
|
|
//---
|
|
const bool hay_positivos = HayPositivosArray(dynamic_lp.balance_to_activate_the_risk);
|
|
const bool hay_negativos = HayNegativosArray(dynamic_lp.balance_to_activate_the_risk);
|
|
bool c = false;
|
|
|
|
//---
|
|
for(int i = 0 ; i < this.size_dynamic; i++)
|
|
{
|
|
double value;
|
|
balanceRiskMap.TryGetValue(this.dynamic_lp.balance_to_activate_the_risk[i], value);
|
|
|
|
dynamic_lp.risk_to_be_adjusted[i] = value;
|
|
dynamic_lp.balance_to_activate_the_risk[i] = (this.chosen_balance + (this.chosen_balance * (dynamic_lp.balance_to_activate_the_risk[i] / 100.0)));
|
|
|
|
if(i < ArraySize(dynamic_lp.balance_to_activate_the_risk) - 1 && hay_negativos && hay_positivos && !c)
|
|
{
|
|
if(dynamic_lp.balance_to_activate_the_risk[i] > 0 && dynamic_lp.balance_to_activate_the_risk[i + 1] < 0)
|
|
{
|
|
index_cambio = i + 1; //WARNING: left position is used by default for the simple case only negatives or positives.
|
|
}
|
|
}
|
|
}
|
|
|
|
//---
|
|
this.min_val_is = false;
|
|
this.max_val_is = false;
|
|
|
|
//---
|
|
if(hay_negativos && !hay_positivos)
|
|
{
|
|
pos_izquierda = this.size_dynamic - 1;
|
|
NewBalanceToOvercome = MAX_VAL_DEFINES_CLOSS_PROFIT;
|
|
f_change_dynamic = CLossProfit::CheckNegatives;
|
|
}
|
|
else
|
|
if(hay_positivos && !hay_negativos)
|
|
{
|
|
pos_izquierda = 0;
|
|
NewBalanceToOvercome = MIN_VAL_DEFINES_CLOSE_PROFIT;
|
|
f_change_dynamic = CLossProfit::CheckPositives;
|
|
}
|
|
else
|
|
{
|
|
pos_izquierda = index_cambio + 1;
|
|
pos_derecha = index_cambio;
|
|
f_change_dynamic = CLossProfit::CheckNegativesAndPositives;
|
|
}
|
|
|
|
//---
|
|
this.ActivateDynamicRiskPerOperation = true;
|
|
if(IsInfoLogEnabled())
|
|
{
|
|
FastLog(FUNCION_ACTUAL, INFO_TEXT, "Arrays ready");
|
|
PrintArrayAsTable(dynamic_lp.balance_to_activate_the_risk, "Negative percentages to modify the risk", "balance");
|
|
PrintArrayAsTable(dynamic_lp.risk_to_be_adjusted, "Risk to be adjusted", "new risk");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Checks and modifies the percentage |
|
|
//+------------------------------------------------------------------+
|
|
inline void CLossProfit::CheckAndModifyThePercentage(void)
|
|
{
|
|
if(!ActivateDynamicRiskPerOperation)
|
|
{
|
|
LogError(StringFormat("Risk %s is not allowed to use dynamic risk", name), FUNCION_ACTUAL);
|
|
Remover();
|
|
return;
|
|
}
|
|
this.f_change_dynamic(this, dynamic_lp, lp, min_val_is, max_val_is, size_dynamic, pos_izquierda, pos_derecha, index_cambio, NewBalanceToOvercome);
|
|
// Print(lp.assigned_percentage);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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.assigned_percentage = 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.assigned_percentage = 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.assigned_percentage = 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.assigned_percentage = ptr.GetInitialPercentageOrMoney(); //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.assigned_percentage = 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.assigned_percentage = 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.assigned_percentage = ptr.GetInitialPercentageOrMoney(); // 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.assigned_percentage = ptr.GetInitialPercentageOrMoney(); // En este caso si o si es porcentaje
|
|
else
|
|
if(normal)
|
|
{
|
|
const int sum = curr_pos_drc < index_change; //si es
|
|
lossprofit.assigned_percentage = 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.assigned_percentage = 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.assigned_percentage = ptr.GetInitialPercentageOrMoney(); // En este caso si o si es porcentaje
|
|
else
|
|
if(normal)
|
|
{
|
|
const int sum = curr_pos_izq < index_change; //si es
|
|
lossprofit.assigned_percentage = l_p.risk_to_be_adjusted[curr_pos_izq + sum];
|
|
}
|
|
else //nos pasmaos entonces, riesgo inicial
|
|
{
|
|
lossprofit.assigned_percentage = 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);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manager class to work with different poses |
|
|
//+------------------------------------------------------------------+
|
|
class CLossProfitManager : public CSpecializedManager
|
|
{
|
|
private:
|
|
//---
|
|
CLossProfit* empty_obj;
|
|
|
|
//---
|
|
CLossProfit* m_losses_profits[LOSS_PROFIT_COUNT];
|
|
bool m_is_add[LOSS_PROFIT_COUNT]; // Si esta añadido
|
|
int m_valid_indexes[];
|
|
|
|
|
|
public:
|
|
~CLossProfitManager();
|
|
CLossProfitManager();
|
|
|
|
//---
|
|
inline int SizeValids() const { return ArraySize(m_valid_indexes); }
|
|
inline int GetIndexValid(const int index) { return m_valid_indexes[index]; }
|
|
|
|
//---
|
|
inline bool IsAdd(const ENUM_TYPE_LOSS_PROFIT type) { return m_is_add[type]; }
|
|
|
|
//--- Clear
|
|
void Clear();
|
|
|
|
//---
|
|
void Add(CLossProfit * new_loss_profit); // Add CLossProfit
|
|
|
|
//---
|
|
void SetNewChossenBalanceForDynamics(double _chosen_balance, bool update);
|
|
void SetValues(); // Setear valores
|
|
void SetValuesAndDynamic(); // Valores y dinamicos
|
|
void CheckDynamic();
|
|
|
|
|
|
//--- Operadores
|
|
CLossProfit* operator[](const ENUM_TYPE_LOSS_PROFIT type);
|
|
CLossProfit* operator[](const int index) { return m_losses_profits[index]; }
|
|
};
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Contructor y Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLossProfitManager::CLossProfitManager()
|
|
{
|
|
ArrayResize(m_valid_indexes, 0);
|
|
empty_obj = new CLossProfit(true, NULL);
|
|
for(int i = 0; i < LOSS_PROFIT_COUNT; i++)
|
|
{
|
|
m_losses_profits[i] = NULL;
|
|
m_is_add[i] = false;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
CLossProfitManager::~CLossProfitManager(void)
|
|
{
|
|
delete empty_obj;
|
|
Clear(); // Eliminamos todos los punteros
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfitManager::SetValues()
|
|
{
|
|
for(int i = 0; i < ArraySize(m_valid_indexes); i++)
|
|
m_losses_profits[m_valid_indexes[i]].Set(); // Setea el valor
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfitManager::SetValuesAndDynamic()
|
|
{
|
|
for(int i = 0; i < ArraySize(m_valid_indexes); i++)
|
|
{
|
|
const int key = m_valid_indexes[i];
|
|
m_losses_profits[key].Set(); // Setea el valor
|
|
if(m_losses_profits[key].IsDynamicMode()) // Check si es dinamico
|
|
m_losses_profits[key].CheckAndModifyThePercentage(); // Modificamos
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfitManager::CheckDynamic(void)
|
|
{
|
|
for(int i = 0; i < ArraySize(m_valid_indexes); i++)
|
|
{
|
|
const int key = m_valid_indexes[i];
|
|
if(m_losses_profits[key].IsDynamicMode()) // Check si es dinamico
|
|
m_losses_profits[key].CheckAndModifyThePercentage(); // Modificamos
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfitManager::SetNewChossenBalanceForDynamics(double _chosen_balance, bool update)
|
|
{
|
|
for(int i = 0; i < ArraySize(m_valid_indexes); i++)
|
|
{
|
|
const int key = m_valid_indexes[i];
|
|
if(m_losses_profits[key].IsDynamicMode()) // Check si es dinamico
|
|
{
|
|
m_losses_profits[key].SetDynamic(_chosen_balance); // Modificamos
|
|
if(update)
|
|
m_losses_profits[key].CheckAndModifyThePercentage();
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CLossProfitManager::Clear(void)
|
|
{
|
|
ArrayInitialize(m_is_add, false);
|
|
ArrayResize(m_valid_indexes, 0);
|
|
CleanItems("CLossProfitManager"); // Delete pointers (m_losses_profits
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| operator[] |
|
|
//+------------------------------------------------------------------+
|
|
CLossProfit* CLossProfitManager::operator[](const ENUM_TYPE_LOSS_PROFIT type)
|
|
{
|
|
return m_is_add[type] ? m_losses_profits[type] : empty_obj;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Function that is executed every time a new item is added |
|
|
//+------------------------------------------------------------------+
|
|
// check_duplicate: No tiene efecto
|
|
void CLossProfitManager::Add(CLossProfit * new_loss_profit)
|
|
{
|
|
//---
|
|
if(CheckPointer(new_loss_profit) == POINTER_INVALID)
|
|
{
|
|
LogFatalError("The pointer to CLossProfit* is invalid", FUNCION_ACTUAL);
|
|
Remover();
|
|
return;
|
|
}
|
|
|
|
//---
|
|
const int idx = (int)new_loss_profit.GetType();
|
|
if(m_is_add[idx])
|
|
{
|
|
LogWarning(StringFormat("The loss or gain %s has already been added, it will not be added.", new_loss_profit.Name()), FUNCION_ACTUAL);
|
|
return;
|
|
}
|
|
|
|
//---
|
|
AddArrayNoVerification2(m_valid_indexes, idx, 0)
|
|
|
|
//---
|
|
m_is_add[idx] = true;
|
|
m_losses_profits[idx] = new_loss_profit;
|
|
|
|
//---
|
|
AddLogger(new_loss_profit);
|
|
}
|
|
|
|
|
|
/*
|
|
ADVERTENCIA:
|
|
- Al momento de añadir items a esta clase, esta si son dinamicos las eliminara cuidado.
|
|
- El unico que maneja esta clase es CRiskManagement, por lo que no se puede remover elementos
|
|
|
|
WARNINGS:
|
|
- When adding items to this class, if they are dynamic, be careful.
|
|
- The only one that handles this class is CRiskManagement, so elements cannot be removed.
|
|
*/
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
#endif
|
|
//+------------------------------------------------------------------+
|