MQL5Book/Include/LotMarginExposure.mqh
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

150 lines
5.6 KiB
MQL5

//+------------------------------------------------------------------+
//| LotMarginExposure.mqh |
//| Copyright (c) 2021-2022, Marketeer |
//| https://www.mql5.com/en/users/marketeer |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Lot Exposure Margin Level Risk |
//+------------------------------------------------------------------+
namespace LEMLR
{
// Container for all calculated margin characteristics per symbol
struct SymbolLotExposureRisk
{
double lot; // requested lot
int atrPointsNormalized; // normalized by tick size
double atrValue; // money value for 1 lot
double lotFromExposureRaw; // non-normalized (can be less than mininal lot)
double lotFromExposure; // normalized
double lotFromRiskOfStopLossRaw; // non-normalized (can be less than mininal lot)
double lotFromRiskOfStopLoss; // normalized
double exposureFromLot; // per 'lot'
double marginLevelFromLot; // per 'lot'
int lotDigits; // digits in normalized lots
};
bool IsOfKindBuy(const ENUM_ORDER_TYPE type)
{
return (type & 1) == 0;
}
bool IsOfKindSell(const ENUM_ORDER_TYPE type)
{
return (type & 1) == 1;
}
double GetCurrentPrice(const string symbol, const ENUM_ORDER_TYPE type)
{
return SymbolInfoDouble(symbol, IsOfKindBuy(type) ? SYMBOL_ASK : SYMBOL_BID);
}
// Main function to calculate all marginal characteristics for given symbol and money
bool Estimate(const ENUM_ORDER_TYPE type, const string symbol, const double lot,
const double price, const double exposure,
const double riskLevel, const int riskPoints, const ENUM_TIMEFRAMES riskPeriod,
double money, SymbolLotExposureRisk &r)
{
double lot1margin;
if(!OrderCalcMargin(type, symbol, 1.0,
price == 0 ? GetCurrentPrice(symbol, type) : price,
lot1margin))
{
Print("OrderCalcMargin ", symbol, " failed: ", _LastError);
return false;
}
if(lot1margin == 0)
{
Print("Margin ", symbol, " is zero, ", _LastError);
return false;
}
const double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
const int pointsInTick = (int)(SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE) / SymbolInfoDouble(symbol, SYMBOL_POINT));
const double pointValue = tickValue / pointsInTick;
if(pointValue == 0)
{
Print(symbol, " is not available (probably yet), ", _LastError);
return false;
}
const int atrPoints = (riskPoints > 0) ? riskPoints :
(int)(((MathMax(iHigh(symbol, riskPeriod, 1), iHigh(symbol, riskPeriod, 0))
- MathMin(iLow(symbol, riskPeriod, 1), iLow(symbol, riskPeriod, 0)))
/ SymbolInfoDouble(symbol, SYMBOL_POINT)));
r.atrPointsNormalized = atrPoints / pointsInTick * pointsInTick; // rounding
if(r.atrPointsNormalized == 0)
{
Print(symbol, " ATR on " , EnumToString(riskPeriod), " is not available (probably yet), ", _LastError);
return false;
}
r.atrValue = r.atrPointsNormalized * pointValue;
double usedMargin = 0;
if(money == 0)
{
money = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
usedMargin = AccountInfoDouble(ACCOUNT_MARGIN);
}
r.lotFromExposureRaw = money * exposure / 100.0 / lot1margin;
r.lotFromExposure = NormalizeLot(symbol, r.lotFromExposureRaw);
r.lotFromRiskOfStopLossRaw = money * riskLevel / 100.0 / (pointValue * r.atrPointsNormalized);
r.lotFromRiskOfStopLoss = NormalizeLot(symbol, r.lotFromRiskOfStopLossRaw);
r.lot = lot <= 0 ? SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) : lot;
double margin = r.lot * lot1margin;
#ifdef DebugLogging
{
Print((float)r.lot, " ", symbol, "=", margin, " ", lot1margin, " ",
EnumToString((ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE)),
" ", (float)pointValue);
}
#endif
if(lot < 0)
{
margin = r.lotFromRiskOfStopLossRaw * lot1margin;
}
r.exposureFromLot = (margin + usedMargin) / money * 100.0;
r.marginLevelFromLot = margin > 0 ? money / (margin + usedMargin) * 100.0 : 0;
r.lotDigits = (int)MathLog10(1.0 / SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));
return true;
}
// Normalize lot in double according to symbol specs
double NormalizeLot(const string symbol, const double lot)
{
const double stepLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(stepLot == 0)
{
Print("LOTSTEP IS ZERO");
return 0;
}
const double newLotsRounded = MathFloor(lot / stepLot) * stepLot;
const double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
if(minLot == 0)
{
Print("MINLOT IS ZERO ", symbol);
return 0;
}
if(newLotsRounded < minLot)
{
return 0;
}
const double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
if(newLotsRounded > maxLot) return maxLot;
return newLotsRounded;
}
};
//+------------------------------------------------------------------+