forked from rosh/MQL5Book
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;
| |
}
| |
};
| |
//+------------------------------------------------------------------+
|