150 行
5.6 KiB
MQL5
150 行
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;
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|