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

193 lines
7.2 KiB
MQL5

//+------------------------------------------------------------------+
//| MarginProfitMeter.mqh |
//| Copyright (c) 2018-2022, Marketeer |
//| https://www.mql5.com/en/users/marketeer |
//| A set of functions to calculate margin, potential profit/loss, |
//| point value and coversion rates. |
//+------------------------------------------------------------------+
namespace MPM
{
// Analogue of built-in OrderCalcMargin which is not allowed in indicators
bool OrderCalcMargin(const ENUM_ORDER_TYPE action, const string symbol,
double volume, double price, double &margin)
{
double marginInit, marginMain;
MqlTick tick;
// check given parameters
if((action != ORDER_TYPE_BUY && action != ORDER_TYPE_SELL)
|| volume < 0 || price < 0) return false;
// request all properties used in the formulae
if(!SymbolInfoTick(symbol, tick)) return false;
if(!SymbolInfoMarginRate(symbol, action, marginInit, marginMain)) return false;
const double contract = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
long leverage = AccountInfoInteger(ACCOUNT_LEVERAGE);
if(volume == 0) volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
if(price == 0) price = action == ORDER_TYPE_BUY ? tick.ask : tick.bid;
if(margin == DBL_MAX) marginInit = marginMain;
margin = 0;
const ENUM_SYMBOL_CALC_MODE m =
(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE);
switch(m)
{
case SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE:
leverage = 1;
case SYMBOL_CALC_MODE_FOREX:
margin = volume * contract / leverage * marginInit;
break;
case SYMBOL_CALC_MODE_CFD:
margin = volume * contract * price * marginInit;
break;
case SYMBOL_CALC_MODE_CFDINDEX:
margin = volume * contract * price * SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE)
/ SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE) * marginInit;
break;
case SYMBOL_CALC_MODE_CFDLEVERAGE:
margin = volume * contract * price / leverage * marginInit;
break;
case SYMBOL_CALC_MODE_EXCH_STOCKS:
case SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX:
if(price == 0) price = tick.last;
margin = volume * contract * price * marginInit;
break;
case SYMBOL_CALC_MODE_FUTURES:
case SYMBOL_CALC_MODE_EXCH_FUTURES:
case SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS:
margin = volume * SymbolInfoDouble(symbol, SYMBOL_MARGIN_INITIAL) * marginInit;
break;
default:
PrintFormat("Unsupported symbol %s trade mode: %s", symbol, EnumToString(m));
}
string account = AccountInfoString(ACCOUNT_CURRENCY);
string current = SymbolInfoString(symbol, SYMBOL_CURRENCY_MARGIN);
if(current != account)
{
if(!Convert(current, account, action == ORDER_TYPE_SELL, margin)) return false;
}
return true;
}
// Search available symbols for a one built of the 'current' and 'account' currencies
int FindExchangeRate(const string current, const string account, string &result)
{
for(int i = 0; i < SymbolsTotal(true); i++)
{
const string symbol = SymbolName(i, true);
const ENUM_SYMBOL_CALC_MODE m =
(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE);
if(m == SYMBOL_CALC_MODE_FOREX || m == SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE)
{
string base = SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE);
string profit = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT);
if(base == current && profit == account)
{
result = symbol;
return +1;
}
else
if(base == account && profit == current)
{
result = symbol;
return -1;
}
}
}
return 0;
}
// Estimate a rate of specified symbol at a given moment in past
double GetHistoricPrice(const string symbol, const datetime moment, const bool ask)
{
const int offset = iBarShift(symbol, _Period, moment);
// NB: iClose can hold Last price instead of Bid for exchange symbols
// there is no fast way to handle this, only tick history analysis
return iClose(symbol, _Period, offset) +
(ask ? iSpread(symbol, _Period, offset) * SymbolInfoDouble(symbol, SYMBOL_POINT) : 0);
}
// Convert amount of 'current' money into 'account' money
bool Convert(const string current, const string account,
const bool ask, double &margin, const datetime moment = 0)
{
string rate;
int dir = FindExchangeRate(current, account, rate);
if(dir == +1)
{
margin *= moment == 0 ?
SymbolInfoDouble(rate, ask ? SYMBOL_BID : SYMBOL_ASK) :
GetHistoricPrice(rate, moment, ask);
}
else if(dir == -1)
{
margin /= moment == 0 ?
SymbolInfoDouble(rate, ask ? SYMBOL_ASK : SYMBOL_BID) :
GetHistoricPrice(rate, moment, ask);
}
else
{
static bool once = false;
if(!once)
{
Print("Can't convert ", current, " -> ", account);
once = true;
}
}
return true;
}
// Return point value (in account currency) of specific symbol
double PointValue(const string symbol, const bool ask = false, const datetime moment = 0)
{
const double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
const double contract = SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE);
const ENUM_SYMBOL_CALC_MODE m =
(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE);
double result = 0;
switch(m)
{
case SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE:
case SYMBOL_CALC_MODE_FOREX:
case SYMBOL_CALC_MODE_CFD:
case SYMBOL_CALC_MODE_CFDINDEX:
case SYMBOL_CALC_MODE_CFDLEVERAGE:
case SYMBOL_CALC_MODE_EXCH_STOCKS:
case SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX:
result = point * contract;
break;
case SYMBOL_CALC_MODE_FUTURES:
case SYMBOL_CALC_MODE_EXCH_FUTURES:
case SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS:
result = point * SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE)
/ SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
break;
default:
PrintFormat("Unsupported symbol %s trade mode: %s", symbol, EnumToString(m));
}
string account = AccountInfoString(ACCOUNT_CURRENCY);
string current = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT);
if(current != account)
{
if(!Convert(current, account, ask, result, moment)) return 0;
}
return result;
}
};
//+------------------------------------------------------------------+