193 lines
7.2 KiB
MQL5
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;
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+
|