640 líneas
60 KiB
MQL5
640 líneas
60 KiB
MQL5
//+------------------------------------------------------------------+
|
||
//| Risk Management.mqh |
|
||
//| Your name |
|
||
//| https://www.mql5.com |
|
||
//+------------------------------------------------------------------+
|
||
#property copyright "Your name "
|
||
#property link "https://www.mql5.com"
|
||
#property strict
|
||
|
||
#include <Trade/Trade.mqh>
|
||
|
||
//+-------------------------------------------------------------------------------------------------+
|
||
//+----------------------------------- Functions -------------------------------------+
|
||
//+-------------------------------------------------------------------------------------------------+
|
||
//+----------------------------------------------------------------------+
|
||
//| Function to check if you have the funds available to execute a trade |
|
||
//+----------------------------------------------------------------------+
|
||
bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type)
|
||
{
|
||
//--- obtenemos el precio de apertura
|
||
MqlTick mqltick;
|
||
SymbolInfoTick(symb,mqltick);
|
||
double price=mqltick.ask;
|
||
if(type==ORDER_TYPE_SELL)
|
||
price=mqltick.bid;
|
||
//--- valores del margen libre imprescindible
|
||
double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
||
//--- llamamos a la función de comprobación
|
||
if(!OrderCalcMargin(type,symb,lots,price,margin))
|
||
{
|
||
//--- algo no ha salido bien, informamos y retornamos false
|
||
Print("Error in ",__FUNCTION__," code=",GetLastError());
|
||
return(false);
|
||
}
|
||
//--- si los recursos para ejecutar una operación son insuficientes
|
||
if(margin>free_margin)
|
||
{
|
||
//--- informamos sobre el error y retornamos false
|
||
Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
|
||
return(false);
|
||
}
|
||
//--- la comprobación ha tenido éxito
|
||
return(true);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------------------------+
|
||
//| Calculates the maximum lot size that can be traded based on free margin and symbol specifics |
|
||
//+----------------------------------------------------------------------------------------------+
|
||
double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50)
|
||
{
|
||
double VOLUME = 1.0; // Initial volume size
|
||
ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type);
|
||
double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type
|
||
double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol
|
||
double margin = EMPTY_VALUE; // Required margin, initialized as empty
|
||
double max_lote = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
||
ResetLastError();
|
||
if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin))
|
||
{
|
||
Print("OrderCalcMargin() failed. Error ", GetLastError());
|
||
return 0.0; // Exit the function if margin calculation fails
|
||
}
|
||
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
|
||
{
|
||
PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE));
|
||
return 0.0;
|
||
}
|
||
double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step;
|
||
for(int i = 1 ; i < 100 ; i++)
|
||
{
|
||
if(CheckMoneyForTrade(_Symbol,result,type) == true)
|
||
break;
|
||
result -= volume_step;
|
||
}
|
||
return result >= max_lote ? max_lote : result; // Return the calculated maximum lot size
|
||
}
|
||
|
||
//+------------------------------------------------------------------------+
|
||
//| Returns the market order type corresponding to the provided order type |
|
||
//+------------------------------------------------------------------------+
|
||
ENUM_ORDER_TYPE MarketOrderByOrderType(ENUM_ORDER_TYPE type)
|
||
{
|
||
switch(type)
|
||
{
|
||
case ORDER_TYPE_BUY:
|
||
case ORDER_TYPE_BUY_LIMIT:
|
||
case ORDER_TYPE_BUY_STOP:
|
||
case ORDER_TYPE_BUY_STOP_LIMIT:
|
||
return(ORDER_TYPE_BUY);
|
||
case ORDER_TYPE_SELL:
|
||
case ORDER_TYPE_SELL_LIMIT:
|
||
case ORDER_TYPE_SELL_STOP:
|
||
case ORDER_TYPE_SELL_STOP_LIMIT:
|
||
return(ORDER_TYPE_SELL);
|
||
}
|
||
return(WRONG_VALUE); // Return an invalid value if no match is found
|
||
}
|
||
|
||
//+---------------------------------------------------------------------------------+
|
||
//| Calculates the appropriate price based on order type, deviation, and stop limit |
|
||
//+---------------------------------------------------------------------------------+
|
||
double PriceByOrderType(const string symbol, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50)
|
||
{
|
||
double point=0;
|
||
MqlTick tick= {};
|
||
ResetLastError();
|
||
if(!SymbolInfoDouble(symbol, SYMBOL_POINT, point))
|
||
{
|
||
Print("SymbolInfoDouble() failed. Error ", GetLastError());
|
||
return 0;
|
||
}
|
||
long value=0;
|
||
if(!SymbolInfoInteger(symbol, SYMBOL_DIGITS, value))
|
||
{
|
||
Print("SymbolInfoInteger() failed. Error ", GetLastError());
|
||
return 0;
|
||
}
|
||
int digits=(int)value;
|
||
if(!SymbolInfoTick(symbol, tick))
|
||
{
|
||
Print("SymbolInfoTick() failed. Error ", GetLastError());
|
||
return 0;
|
||
}
|
||
switch(order_type)
|
||
{
|
||
case ORDER_TYPE_BUY:
|
||
return(tick.ask);
|
||
case ORDER_TYPE_SELL:
|
||
return(tick.bid);
|
||
case ORDER_TYPE_BUY_LIMIT:
|
||
return(NormalizeDouble(tick.ask - DEVIATION * point, digits));
|
||
case ORDER_TYPE_SELL_LIMIT:
|
||
return(NormalizeDouble(tick.bid + DEVIATION * point, digits));
|
||
case ORDER_TYPE_BUY_STOP:
|
||
return(NormalizeDouble(tick.ask + DEVIATION * point, digits));
|
||
case ORDER_TYPE_SELL_STOP:
|
||
return(NormalizeDouble(tick.bid - DEVIATION * point, digits));
|
||
case ORDER_TYPE_BUY_STOP_LIMIT:
|
||
return(NormalizeDouble(tick.ask + DEVIATION * point - STOP_LIMIT * point, digits));
|
||
case ORDER_TYPE_SELL_STOP_LIMIT:
|
||
return(NormalizeDouble(tick.bid - DEVIATION * point + STOP_LIMIT * point, digits));
|
||
default:
|
||
return(0);
|
||
}
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Retrieve the magic number associated with a ticket |
|
||
//+------------------------------------------------------------------+
|
||
inline ulong GetMagic(const ulong ticket)
|
||
{
|
||
HistoryOrderSelect(ticket);
|
||
return HistoryOrderGetInteger(ticket, ORDER_MAGIC);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------------------+
|
||
//| Calculates the net profit since a given date for a specific magic number or all trades |
|
||
//+----------------------------------------------------------------------------------------+
|
||
double GetNetProfitSince(bool include_all_magic, ulong specific_magic, datetime start_date)
|
||
{
|
||
double total_net_profit = 0.0; // Initialize the total net profit
|
||
ResetLastError(); // Reset any previous errors
|
||
if(start_date > 0 && start_date != D'1971.01.01 00:00')
|
||
{
|
||
if(!HistorySelect(start_date, TimeCurrent()))
|
||
{
|
||
Print("Error when selecting orders: ", _LastError);
|
||
return 0.0; // Exit if unable to select the history
|
||
}
|
||
int total_deals = HistoryDealsTotal(); // Count total deals in the history
|
||
for(int i = 0; i < total_deals; i++)
|
||
{
|
||
ulong deal_ticket = HistoryDealGetTicket(i); // Get the deal ticket
|
||
if(HistoryDealGetInteger(deal_ticket, DEAL_TYPE) == DEAL_TYPE_BALANCE)
|
||
continue;
|
||
ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); // Get deal entry type
|
||
datetime deal_close_time = (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME); // Get deal close time
|
||
ulong position_id = HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID); // Get the position ID
|
||
if(deal_close_time >= start_date && (deal_entry == DEAL_ENTRY_OUT || deal_entry == DEAL_ENTRY_IN))
|
||
{
|
||
if((HistoryDealGetInteger(deal_ticket, DEAL_MAGIC) == specific_magic || specific_magic == GetMagic(position_id))
|
||
|| include_all_magic)
|
||
{
|
||
double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); // Get deal profit
|
||
double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); // Get commission
|
||
double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); // Get swap fees
|
||
double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit
|
||
total_net_profit += deal_net_profit; // Accumulate total net profit
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return NormalizeDouble(total_net_profit, 2);
|
||
}
|
||
|
||
//+---------------------------------------------------------------------+
|
||
//| Determine the optimal lot size based on risk and current conditions |
|
||
//+---------------------------------------------------------------------+
|
||
void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss)
|
||
{
|
||
if(StopLoss <= 0)
|
||
{
|
||
Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss);
|
||
nlot = 0.0;
|
||
return;
|
||
}
|
||
Print("Max Lot: ", glot, " | RiskPerOperation: ", max_risk_per_operation);
|
||
new_risk_per_operation = 0;
|
||
long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
|
||
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||
double rpo = (glot * (spread + 1 + (StopLoss * tick_value)));
|
||
if(rpo > max_risk_per_operation)
|
||
{
|
||
if(max_risk_per_operation <= 0)
|
||
return;
|
||
double new_lot = (max_risk_per_operation / rpo) * glot;
|
||
Print("prev lot: ", new_lot);
|
||
new_lot = MathRound(new_lot / step) * step;
|
||
Print("New lot: ",new_lot);
|
||
if(new_risk_per_operation > max_risk_per_operation)
|
||
{
|
||
new_lot -= step;
|
||
}
|
||
new_risk_per_operation = new_lot * (spread + 1 + (StopLoss * tick_value));
|
||
nlot = new_lot;
|
||
}
|
||
else
|
||
{
|
||
new_risk_per_operation = rpo;
|
||
nlot = glot;
|
||
}
|
||
if(nlot >= SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX))
|
||
{
|
||
nlot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
||
}
|
||
PrintFormat("Risk per operation(%.2f) = %.2f * ( %i + 1 + (%i * %f)) ",new_risk_per_operation,nlot,spread,StopLoss,tick_value);
|
||
if(nlot <= 0)
|
||
PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss);
|
||
else
|
||
PrintFormat("For the stop loss %i in points and risk per operation: %.2f, the ideal lot is: %.2f",StopLoss,max_risk_per_operation,nlot);
|
||
}
|
||
|
||
//+-----------------------------------------------------------------------+
|
||
//| Calculate the stop loss distance in points based on risk and lot size |
|
||
//+-----------------------------------------------------------------------+
|
||
long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50)
|
||
{
|
||
double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT);
|
||
chosen_lot = lot;
|
||
long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
|
||
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
double result = ((risk_per_operation / lot) - spread - 1) / tick_value;
|
||
long ideal_sl = long(MathRound(result));
|
||
if(ideal_sl <= 0)
|
||
{
|
||
PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation);
|
||
return 0;
|
||
}
|
||
return ideal_sl;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Converts a distance in the base unit to a point-based measurement|
|
||
//+------------------------------------------------------------------+
|
||
inline int DistanceToPoint(double dist, string symbol = "_Symbol")
|
||
{
|
||
return (int)(dist / SymbolInfoDouble((symbol == "_Symbol" ? _Symbol : symbol), SYMBOL_POINT)); // Calculate and return the distance in points
|
||
}
|
||
|
||
//+--------------------------------------------------------------------+
|
||
//| Function to obtain the ideal lot based on your risk per operation |
|
||
//+--------------------------------------------------------------------+
|
||
// risk_per_operation in USD, not %
|
||
|
||
double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50)
|
||
{
|
||
double VOLUME = 1.0; // Initial volume size
|
||
ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type);
|
||
double price = PriceByOrderType(_Symbol, order_type, DEVIATION, STOP_LIMIT); // Price for the given order type
|
||
double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol
|
||
double max_lote = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
||
double margin = EMPTY_VALUE; // Required margin, initialized as empty
|
||
ResetLastError();
|
||
if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin))
|
||
{
|
||
Print("OrderCalcMargin() failed. Error ", GetLastError());
|
||
return 0; // Exit the function if margin calculation fails
|
||
}
|
||
double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step;
|
||
if(result >= max_lote)
|
||
result = max_lote;
|
||
if(result <= 0)
|
||
{
|
||
PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation);
|
||
return 0;
|
||
}
|
||
PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result);
|
||
return result; // Return the calculated maximum lot size
|
||
}
|
||
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Defines |
|
||
//+------------------------------------------------------------------+
|
||
#define NOT_MAGIC_NUMBER 0 //Not Magic Number
|
||
#define FLAG_CLOSE_ALL_PROFIT 2 //Flag indicating to close only operations with profit
|
||
#define FLAG_CLOSE_ALL_LOSS 4 //Flag indicating to close only operations without profit
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Enumerations |
|
||
//+------------------------------------------------------------------+
|
||
enum ENUM_LOTE_TYPE //lot type
|
||
{
|
||
Dinamico,//Dynamic
|
||
Fijo//Fixed
|
||
};
|
||
|
||
enum ENUM_RISK_CALCULATION_MODE //enumeration to define the types of calculation of the value of maximum profits and losses
|
||
{
|
||
money, //Money
|
||
percentage //Percentage %
|
||
};
|
||
enum ENUM_MODE_RISK_MANAGEMENT //enumeration to define the type of risk management
|
||
{
|
||
propfirm_ftmo, //Prop Firm FTMO
|
||
personal_account // Personal Account
|
||
};
|
||
enum ENUM_APPLIED_PERCENTAGES //enumeration to define the value to which the percentages will be applied
|
||
{
|
||
Balance, //Balance
|
||
ganancianeta,//Net profit
|
||
free_margin, //Free margin
|
||
equity //Equity
|
||
};
|
||
enum ENUM_GET_LOT
|
||
{
|
||
GET_LOT_BY_ONLY_RISK_PER_OPERATION, //Obtain the lot for the risk per operation
|
||
GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION //Obtain and adjust the lot through the risk per operation and stop loss respectively.
|
||
};
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Structures |
|
||
//+------------------------------------------------------------------+
|
||
struct Loss_Profit
|
||
{
|
||
double value; //value
|
||
double assigned_percentage; //percentage to apply
|
||
ENUM_RISK_CALCULATION_MODE mode_calculation_risk; //risk calculation method
|
||
ENUM_APPLIED_PERCENTAGES percentage_applied_to; //percentage applied to
|
||
};
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Class CRisk Management |
|
||
//+------------------------------------------------------------------+
|
||
class CRiskManagemet final
|
||
{
|
||
private:
|
||
//--- CTrade class pointer to be able to work with open positions
|
||
CTrade *trade;
|
||
|
||
//-- Main variables
|
||
double account_profit;
|
||
long StopLoss;
|
||
double lote;
|
||
|
||
//--- General variables
|
||
ENUM_MODE_RISK_MANAGEMENT mode_risk_managemet;
|
||
ulong magic_number;
|
||
|
||
//--- Variables to work with anchoring tests
|
||
double account_balance_propfirm;
|
||
|
||
//--- Variables to store the values of the maximum losses
|
||
double nmlpo;
|
||
|
||
//--- variables that store percentages and enumeration, which will be used for the subsequent calculation of losses
|
||
Loss_Profit mdl, mwl, ml, gmlpo, mdp;
|
||
|
||
//--- Variables to store the profit and the time from which they will be obtained
|
||
double daily_profit, weekly_profit, gross_profit;
|
||
datetime last_weekly_time, last_day_time, init_time;
|
||
|
||
//--- General function to assign values to loss variables
|
||
double GetValorWithApplied(const ENUM_APPLIED_PERCENTAGES applied,const double percentage_);
|
||
|
||
//--- Functions to assign values to internal variables
|
||
void SetMDL() {this.mdl.value = this.mdl.mode_calculation_risk == money ? this.mdl.value : (this.mdl.assigned_percentage > 0 ? GetValorWithApplied(this.mdl.percentage_applied_to,mdl.assigned_percentage) : 0); }
|
||
void SetMWL() {this.mwl.value = this.mwl.mode_calculation_risk == money ? this.mwl.value : (this.mwl.assigned_percentage > 0 ? GetValorWithApplied(this.mwl.percentage_applied_to,mwl.assigned_percentage) : 0); }
|
||
void SetML() {this.ml.value = this.ml.mode_calculation_risk == money ? this.ml.value : (this.ml.assigned_percentage > 0 ? GetValorWithApplied(this.ml.percentage_applied_to,ml.assigned_percentage): 0); }
|
||
void SetGMLPO() {this.gmlpo.value = this.gmlpo.mode_calculation_risk == money ? this.gmlpo.value : (this.gmlpo.assigned_percentage > 0 ? GetValorWithApplied(this.gmlpo.percentage_applied_to,gmlpo.assigned_percentage) : 0); }
|
||
void SetMDP() {this.mdp.value = this.mdp.mode_calculation_risk == money ? this.mdp.value : (this.mdp.assigned_percentage > 0 ? GetValorWithApplied(this.mdp.percentage_applied_to,mdp.assigned_percentage) : 0); }
|
||
void SetNMPLO(double& TLB_new, double tlb) { GetIdealLot(TLB_new,tlb,this.gmlpo.value,this.nmlpo,this.StopLoss); }
|
||
|
||
|
||
public:
|
||
//--- Builder and Destroyer
|
||
CRiskManagemet(ulong magic_number_ = NOT_MAGIC_NUMBER,ENUM_MODE_RISK_MANAGEMENT mode_risk_management_ = personal_account, double account_propfirm_balance=0);
|
||
|
||
~CRiskManagemet();
|
||
|
||
//--- Functions to assign values to variables for subsequent calculation of losses
|
||
void SetPorcentages(double percentage_or_money_mdl, double percentage_or_money_mwl,double percentage_or_money_gmlpo, double percentage_or_money_ml, double percentage_or_money_mdp_); //Obligatorio
|
||
|
||
void SetEnums(ENUM_RISK_CALCULATION_MODE mode_mdl_, ENUM_RISK_CALCULATION_MODE mode_mwl_, ENUM_RISK_CALCULATION_MODE mode_gmlpo_, ENUM_RISK_CALCULATION_MODE mode_ml_,
|
||
ENUM_RISK_CALCULATION_MODE mode_mdp_); //Opcional
|
||
|
||
void SetApplieds(ENUM_APPLIED_PERCENTAGES applied_mdl_, ENUM_APPLIED_PERCENTAGES applied_mwl_, ENUM_APPLIED_PERCENTAGES applied_gmlpo_, ENUM_APPLIED_PERCENTAGES applied_ml_,
|
||
ENUM_APPLIED_PERCENTAGES applied_mdp_); //Opcional
|
||
|
||
//--- Function to set the "StopLoss" variable, in points or distance
|
||
inline void SetStopLoss(double dist_open_sl) { this.StopLoss = DistanceToPoint(dist_open_sl); }
|
||
inline void SetStopLoss(long _sl_point_) { this.StopLoss = _sl_point_; }
|
||
|
||
//--- Events
|
||
void OnNewDay();
|
||
void OnNewWeek();
|
||
|
||
//--- Closing of positions
|
||
void CloseAllPositions(int flags = FLAG_CLOSE_ALL_LOSS | FLAG_CLOSE_ALL_PROFIT);
|
||
|
||
//--- Get the lot
|
||
double GetLote(const ENUM_ORDER_TYPE order_type, const ENUM_GET_LOT mode_get_lot);
|
||
|
||
//--- Obtain the SL by risk per operation
|
||
long GetSL(const ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50);
|
||
|
||
//--- Obtain the value of maximum profits and losses:
|
||
inline double GetML() const { return this.ml.value; }
|
||
inline double GetMWL() const { return this.mwl.value; }
|
||
inline double GetMDL() const { return this.mdl.value; }
|
||
inline double GetGMLPO() const { return this.gmlpo.value; }
|
||
inline double GetNMLPO() const { return this.nmlpo; }
|
||
inline double GetMDP() const { return this.mdp.value; }
|
||
|
||
//--- Obtain only profits:
|
||
inline double GetGrossProfit() const { return this.gross_profit; }
|
||
inline double GetWeeklyProfit() const { return this.weekly_profit; }
|
||
inline double GetDailyProfit() const { return this.daily_profit; }
|
||
};
|
||
|
||
|
||
//+------------------------------------------------------------------------------------------+
|
||
//| Set the percentages to be used if the risk calculation mode was chosen as "percentage". |
|
||
//+------------------------------------------------------------------------------------------+
|
||
//Note: if you have chosen the "money" mode, it is not necessary to set this method.
|
||
void CRiskManagemet::SetPorcentages(double percentage_or_money_mdl,double percentage_or_money_mwl,double percentage_or_money_gmlpo,double percentage_or_money_ml,double percentage_or_money_mdp_)
|
||
{
|
||
this.gmlpo.assigned_percentage = percentage_or_money_gmlpo;
|
||
this.mdl.assigned_percentage = percentage_or_money_mdl;
|
||
this.ml.assigned_percentage = percentage_or_money_ml;
|
||
this.mdp.assigned_percentage = percentage_or_money_mdp_;
|
||
this.mwl.assigned_percentage = percentage_or_money_mwl;
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------------------+
|
||
//| Function to set how losses or gains are calculated, |
|
||
//| by percentage applied to (balance, equity, free margin or net profit) or simply money. |
|
||
//+----------------------------------------------------------------------------------------+
|
||
//Note: This method is mandatory, it must be executed in the OnInit event.
|
||
void CRiskManagemet::SetEnums(ENUM_RISK_CALCULATION_MODE mode_mdl_,ENUM_RISK_CALCULATION_MODE mode_mwl_,ENUM_RISK_CALCULATION_MODE mode_gmlpo_,ENUM_RISK_CALCULATION_MODE mode_ml_,ENUM_RISK_CALCULATION_MODE mode_mdp_)
|
||
{
|
||
this.gmlpo.mode_calculation_risk = mode_gmlpo_;
|
||
this.mdl.mode_calculation_risk = mode_mdl_;
|
||
this.mdp.mode_calculation_risk = mode_mdp_;
|
||
this.ml.mode_calculation_risk = mode_ml_;
|
||
this.mwl.mode_calculation_risk = mode_mwl_;
|
||
//-- En caso se haya escojido el modo dinero, asignamos la variable que guarda el dinero o porcentage alas varialbes correspondientes
|
||
this.gmlpo.value = this.gmlpo.mode_calculation_risk == money ? this.gmlpo.percentage_applied_to : 0;
|
||
this.mdp.value = this.mdp.mode_calculation_risk == money ? this.mdp.percentage_applied_to : 0;
|
||
this.mdl.value = this.mdl.mode_calculation_risk == money ? this.mdl.percentage_applied_to : 0;
|
||
this.ml.value = this.ml.mode_calculation_risk == money ? this.ml.percentage_applied_to : 0;
|
||
this.mwl.value = this.mwl.mode_calculation_risk == money ? this.mwl.percentage_applied_to : 0;
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------------------------------+
|
||
//| Function to set variables that will be used to know which account variable |
|
||
//| (free margin, balance, equity, net profit) will be applied to the percentages chosen by the user. |
|
||
//+----------------------------------------------------------------------------------------------------+
|
||
//Note: if you have chosen the "money" mode, it is not necessary to set this method.
|
||
void CRiskManagemet::SetApplieds(ENUM_APPLIED_PERCENTAGES applied_mdl_,ENUM_APPLIED_PERCENTAGES applied_mwl_,ENUM_APPLIED_PERCENTAGES applied_gmlpo_,ENUM_APPLIED_PERCENTAGES applied_ml_,ENUM_APPLIED_PERCENTAGES applied_mdp_)
|
||
{
|
||
this.gmlpo.percentage_applied_to = applied_gmlpo_;
|
||
this.mdl.percentage_applied_to = applied_mdl_;
|
||
this.mdp.percentage_applied_to = applied_mdp_;
|
||
this.mwl.percentage_applied_to = applied_mwl_;
|
||
this.ml.percentage_applied_to = applied_ml_;
|
||
}
|
||
|
||
//+---------------------------------------------------------------------------+
|
||
//| Function to close all positions opened by the magic number or by the user |
|
||
//+---------------------------------------------------------------------------+
|
||
void CRiskManagemet::CloseAllPositions(int flags = FLAG_CLOSE_ALL_LOSS | FLAG_CLOSE_ALL_PROFIT)
|
||
{
|
||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
{
|
||
ulong position_ticket = PositionGetTicket(i);
|
||
if(!PositionSelectByTicket(position_ticket))
|
||
continue; // If you don't select the position, continue
|
||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||
// Check flags before closing the position
|
||
if((flags & FLAG_CLOSE_ALL_PROFIT) != 0 && profit > 0) // Close only profit positions
|
||
{
|
||
trade.PositionClose(position_ticket);
|
||
}
|
||
else
|
||
if((flags & FLAG_CLOSE_ALL_LOSS) != 0 && profit < 0) // Close only losing positions
|
||
{
|
||
trade.PositionClose(position_ticket);
|
||
}
|
||
}
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------------+
|
||
//| Get the ideal stop loss based on a specified lot and the maximum loss per trade |
|
||
//+----------------------------------------------------------------------------------+
|
||
long CRiskManagemet::GetSL(const ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50)
|
||
{
|
||
double lot;
|
||
return CalculateSL(type,this.gmlpo.value,lot,DEVIATION,STOP_LIMIT);
|
||
}
|
||
|
||
//+-----------------------------------------------------------------------------------------------+
|
||
//| Function to obtain the ideal lot based on the maximum loss per operation and the stop loss |
|
||
//+-----------------------------------------------------------------------------------------------+
|
||
double CRiskManagemet::GetLote(const ENUM_ORDER_TYPE order_type, const ENUM_GET_LOT mode_get_lot)
|
||
{
|
||
if(mode_get_lot == GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION)
|
||
{
|
||
double MaxLote = GetMaxLote(order_type);
|
||
SetNMPLO(this.lote,MaxLote);
|
||
PrintFormat("Maximum loss in case the next operation fails %.2f ", this.nmlpo);
|
||
}
|
||
else
|
||
{
|
||
this.lote = GetLotByRiskPerOperation(this.gmlpo.value,order_type);
|
||
}
|
||
return this.lote;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Constructor |
|
||
//+------------------------------------------------------------------+
|
||
CRiskManagemet::CRiskManagemet(ulong magic_number_ = NOT_MAGIC_NUMBER,ENUM_MODE_RISK_MANAGEMENT mode_risk_management_ = personal_account, double account_propfirm_balance =0)
|
||
{
|
||
if(magic_number_ == NOT_MAGIC_NUMBER)
|
||
{
|
||
Print("| Warning | No magic number has been chosen, taking into account all the magic numbers and the user's trades");
|
||
}
|
||
//---
|
||
this.account_balance_propfirm = account_propfirm_balance ;
|
||
trade = new CTrade();
|
||
this.account_profit = GetNetProfitSince(true,this.magic_number,D'1972.01.01 00:00');
|
||
this.magic_number = magic_number_;
|
||
this.mode_risk_managemet = mode_risk_management_;
|
||
//---
|
||
this.last_day_time = iTime(_Symbol,PERIOD_D1,0);
|
||
this.last_weekly_time = iTime(_Symbol,PERIOD_W1,0);
|
||
this.init_time =magic_number_ != NOT_MAGIC_NUMBER ? TimeCurrent() : D'1972.01.01 00:00';
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Destructor |
|
||
//+------------------------------------------------------------------+
|
||
CRiskManagemet::~CRiskManagemet()
|
||
{
|
||
delete trade;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Function that runs every new day |
|
||
//+------------------------------------------------------------------+
|
||
void CRiskManagemet::OnNewDay(void)
|
||
{
|
||
SetMWL();
|
||
SetMDL();
|
||
SetML();
|
||
SetMDP();
|
||
SetGMLPO();
|
||
this.daily_profit = 0;
|
||
this.last_day_time = iTime(_Symbol,PERIOD_D1,0);
|
||
Print(" New day ");
|
||
Print(StringFormat("%-6s| %s", "Losses", "Loss"));
|
||
Print(StringFormat("%-6s| %.2f", "MDP", this.mdp.value));
|
||
Print(StringFormat("%-6s| %.2f", "MWL", this.mwl.value));
|
||
Print(StringFormat("%-6s| %.2f", "ML", this.ml.value));
|
||
Print(StringFormat("%-6s| %.2f", "MDl", this.mdl.value));
|
||
Print(StringFormat("%-6s| %.2f", "GMLPO", this.gmlpo.value));
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Function that runs every new week |
|
||
//+------------------------------------------------------------------+
|
||
void CRiskManagemet::OnNewWeek(void)
|
||
{
|
||
this.last_weekly_time = iTime(_Symbol,PERIOD_W1,0);
|
||
this.weekly_profit = 0;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Function to obtain the value of a loss or gain based |
|
||
//| on the applied value (balance, free margin, equity, net profit) |
|
||
//+------------------------------------------------------------------+
|
||
double CRiskManagemet::GetValorWithApplied(const ENUM_APPLIED_PERCENTAGES applied,const double percentage_)
|
||
{
|
||
if(this.mode_risk_managemet == propfirm_ftmo && percentage_ != this.mdp.assigned_percentage && percentage_ != this.gmlpo.assigned_percentage)
|
||
return this.account_balance_propfirm * (percentage_/100.0);
|
||
|
||
switch(applied)
|
||
{
|
||
case Balance:
|
||
return NormalizeDouble((percentage_/100.0) * AccountInfoDouble(ACCOUNT_BALANCE),2);
|
||
case ganancianeta:
|
||
{
|
||
if(this.account_profit <= 0)
|
||
{
|
||
PrintFormat("The total profit of the account which is %+.2f is invalid or negative",this.account_profit);
|
||
return 0;
|
||
}
|
||
else
|
||
return NormalizeDouble((percentage_/100.0) * this.account_profit,2);
|
||
}
|
||
case free_margin:
|
||
{
|
||
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
|
||
{
|
||
PrintFormat("free margin of %+.2f is invalid",AccountInfoDouble(ACCOUNT_MARGIN_FREE));
|
||
return 0;
|
||
}
|
||
else
|
||
return NormalizeDouble((percentage_/100.0) * AccountInfoDouble(ACCOUNT_MARGIN_FREE),2);
|
||
}
|
||
case equity:
|
||
return NormalizeDouble((percentage_/100.0) * AccountInfoDouble(ACCOUNT_EQUITY),2);
|
||
default:
|
||
Print("Critical Error | It was not found that: ", EnumToString(applied), " be part of the allowed enumeration");
|
||
}
|
||
return 0;
|
||
}
|
||
|