2026-07-18 15:53:04 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| MoneyRiskBase.mqh |
|
|
|
|
|
//| AnimateDread |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "..\Expert\ExpertMoneyCustom.mqh"
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Class CMoneyRiskBase. |
|
|
|
|
|
//| Shared risk-based lot-sizing core for every money-management |
|
|
|
|
|
//| strategy that sizes a trade off a fixed account-risk percentage |
|
|
|
|
|
//| (CMoneyFixedRisk, CMoneyIntelligent) - CalculatePotentialLoss()/ |
|
|
|
|
|
//| CheckOpenLong()/CheckOpenShort()/CalculateLotSize() used to be |
|
|
|
|
|
//| duplicated near-verbatim across both. AdjustRiskAmount()/ |
|
2026-07-18 17:39:58 -04:00
|
|
|
//| AdjustLotSize() are the two points a subclass can diverge at |
|
|
|
|
|
//| (CMoneyIntelligent overrides AdjustRiskAmount() for its Kelly- |
|
|
|
|
|
//| criterion, confidence-and-reward:risk-based sizing) - override |
|
|
|
|
|
//| only those, everything else here is shared as-is. |
|
2026-07-18 15:53:04 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CMoneyRiskBase : public CExpertMoneyCustom
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
virtual double CheckOpenLong(double price, double sl);
|
|
|
|
|
virtual double CheckOpenShort(double price, double sl);
|
|
|
|
|
virtual double CheckClose(CPositionInfo *position) { return(0.0); }
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
double CalculatePotentialLoss(ENUM_ORDER_TYPE orderType, double price, double sl);
|
|
|
|
|
double CalculateLotSize(double loss);
|
2026-07-18 17:39:58 -04:00
|
|
|
//--- Hook for CMoneyIntelligent's Kelly-criterion risk% scaling (see its own
|
|
|
|
|
//--- AdjustRiskAmount() override for the rationale); default no-op keeps CMoneyFixedRisk's
|
|
|
|
|
//--- behavior exactly as it was before this base class existed.
|
2026-07-18 15:53:04 -04:00
|
|
|
virtual double AdjustRiskAmount(double riskAmount) { return riskAmount; }
|
2026-07-18 17:39:58 -04:00
|
|
|
//--- Reserved extension point for a future lot-size-level adjustment (e.g. equity-curve-
|
|
|
|
|
//--- based scaling); no current subclass overrides this - default is a no-op.
|
2026-07-18 15:53:04 -04:00
|
|
|
virtual double AdjustLotSize(double lot) { return lot; }
|
|
|
|
|
};
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Getting lot size for open long position. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CMoneyRiskBase::CheckOpenLong(double price, double sl)
|
|
|
|
|
{
|
|
|
|
|
if(m_symbol == NULL)
|
|
|
|
|
return 0.0;
|
|
|
|
|
double loss = CalculatePotentialLoss(ORDER_TYPE_BUY, price, sl);
|
|
|
|
|
if(loss <= 0.0)
|
|
|
|
|
return m_symbol.LotsMin();
|
|
|
|
|
double lot = AdjustLotSize(CalculateLotSize(loss));
|
|
|
|
|
string description;
|
|
|
|
|
// Adjust the lot size within allowed bounds and ensure sufficient margin
|
|
|
|
|
if(CheckAndCorrectVolumeValue(lot, description) && CheckAndAdjustMoneyForTrade(m_symbol.Name(), lot, ORDER_TYPE_BUY))
|
|
|
|
|
return lot;
|
|
|
|
|
else
|
|
|
|
|
return 0.0; // Handle as needed or inform the user
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Getting lot size for open short position. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CMoneyRiskBase::CheckOpenShort(double price, double sl)
|
|
|
|
|
{
|
|
|
|
|
if(m_symbol == NULL)
|
|
|
|
|
return 0.0;
|
|
|
|
|
double loss = CalculatePotentialLoss(ORDER_TYPE_SELL, price, sl);
|
|
|
|
|
if(loss <= 0.0)
|
|
|
|
|
return m_symbol.LotsMin();
|
|
|
|
|
double lot = AdjustLotSize(CalculateLotSize(loss));
|
|
|
|
|
string description;
|
|
|
|
|
// Adjust the lot size within allowed bounds and ensure sufficient margin
|
|
|
|
|
if(CheckAndCorrectVolumeValue(lot, description) && CheckAndAdjustMoneyForTrade(m_symbol.Name(), lot, ORDER_TYPE_SELL))
|
|
|
|
|
return lot;
|
|
|
|
|
else
|
|
|
|
|
return 0.0; // Handle as needed or inform the user
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Calculate potential loss |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CMoneyRiskBase::CalculatePotentialLoss(ENUM_ORDER_TYPE orderType, double price, double sl)
|
|
|
|
|
{
|
|
|
|
|
if(price == 0.0)
|
|
|
|
|
price = (orderType == ORDER_TYPE_BUY) ? m_symbol.Ask() : m_symbol.Bid();
|
|
|
|
|
return -m_account.OrderProfitCheck(m_symbol.Name(), orderType, 1.0, price, sl);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Calculate the lot size based on potential loss and account balance|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CMoneyRiskBase::CalculateLotSize(double loss)
|
|
|
|
|
{
|
|
|
|
|
double riskAmount = AdjustRiskAmount(m_account.Balance() * m_percent / 100.0);
|
|
|
|
|
double stepvol = m_symbol.LotsStep();
|
|
|
|
|
return MathFloor(riskAmount / loss / stepvol) * stepvol;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|