
Функции для управления капиталом в экспертах
Введение
Язык MQL5 предоставляет возможность получения огромного количества информации о текущем состоянии терминала, mql5-программы, а также о финансовом инструменте и торговом счете. Для организации функций управлений капиталом нам потребуется изучить свойства из двух последних перечисленных разделов, а также познакомиться со следующими функциями:
- SymbolInfoInteger()
- SymbolInfoDouble()
- SymbolInfoString()
- AccountInfoInteger()
- AccountInfoDouble()
- AccountInfoString()
Хотя речь в статье будет идти в основном об использовании функций в экспертах, на самом деле все описанное можно также применять в индикаторах и скриптах.
Получение информации о состоянии счета
Две первые важные характеристики торгового счета - это баланс (Balance) и собственные средства (Equity). Для получения этих значений используем функцию AccountInfoDouble():
double balance=AccountInfoDouble(ACCOUNT_BALANCE); double equity=AccountInfoDouble(ACCOUNT_EQUITY);
Следующее, что нас интересует, это размер залоговых средств под открытые позиции и совокупная плавающая прибыль или убыток на счете по всем открытым позициям.
double margin=AccountInfoDouble(ACCOUNT_MARGIN); double float_profit=AccountInfoDouble(ACCOUNT_PROFIT);
Для того чтобы можно было открывать новые позиции или наращивать уже существующие, необходимы свободные средства, не участвующие в залоге.
double free_margin=AccountInfoDouble(ACCOUNT_FREEMARGIN);
Тут необходимо отметить, что вышеперечисленные значения выражены в денежном выражении.
Денежные значения, возвращаемые функцией AccountInfoDouble(), выражаются в валюте депозита. Чтобы узнать валюту депозита, используйте функцию AccountInfoString().
string account_currency=AccountInfoString(ACCOUNT_CURRENCY);
Уровень собственных средств
Счет имеет еще одну важную характеристику - уровень, на котором наступает событие Stop Out (принудительное закрытие позиции при нехватке собственных средств на поддержание открытых позиций). Чтобы получить это значение, вновь используем функцию AccountInfoDouble():
double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
Функция возвращает только само значение, но не поясняет, в каких единицах это значение выражается. Существуют два режима указания уровня Stop Out: в процентах и в денежном выражении. Чтобы выяснить это, используем AccountInfoInteger():
//--- получим валюту счета string account_currency=AccountInfoString(ACCOUNT_CURRENCY); //--- уровень, при котором наступает Stop Out double stopout_level=AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); //--- режим указания Stop Out ENUM_ACCOUNT_STOPOUT_MODE so_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); if(so_mode==ACCOUNT_STOPOUT_MODE_PERCENT) PrintFormat("Уровень Stop Out задается в процентах %.2f%%",stopout_level); else PrintFormat("Уровень Stop Out задается в денежном выражении и составляет %.2f %s",stopout_level,account_currency);
Дополнительные сведения о счете
Часто в расчетах требуется знать размер предоставляемого на торговом счете плеча. Получить эту информацию можно с помощью функции AccountInfoInteger():
int leverage=(int)AccountInfoInteger(ACCOUNT_LEVERAGE);
Для того чтобы случайно не запустить торговать неотлаженного эксперта на реальном счете, необходимо узнать тип счета.
ENUM_ACCOUNT_TRADE_MODE mode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE); switch(mode) { case ACCOUNT_TRADE_MODE_DEMO: Comment("Счет demo"); break; case ACCOUNT_TRADE_MODE_CONTEST: Comment(com,"Счет конкурсный"); break; case ACCOUNT_TRADE_MODE_REAL: Comment(com,"Счет реальный"); break; default: Comment(com,"Счет неизвестного типа"); }
Не на всяком счете можно торговать, например, на конкурсных счетах нельзя проводить торговые операции до начала соревнований. Эту информацию также получим функцией AccountInfoInteger():
bool trade_allowed=(bool)AccountInfoInteger(ACCOUNT_TRADE_ALLOWED); if(trade_allowed) Print("Торговля разрешена"); else Print(com,"Торговля запрещена");
Если даже торговля на данном счете разрешена, то это еще не означает, что эксперт имеет право торговать. Чтобы проверить, разрешено ли торговать эксперту, пишем:
if(trade_allowed) { bool trade_expert=(bool)AccountInfoInteger(ACCOUNT_TRADE_EXPERT); if(trade_expert) Print("Экспертам также разрешено торговать"); else Print("Но экспертам торговать запрещено");
Рассмотренные примеры можно найти в приложенном советнике Account_Info.mq5. Они могут быть использованы в mql5-программах любой сложности.
Информация об инструменте
Каждый финансовый инструмент имеет свое описание и размещен по некоторому пути, который этот инструмент характеризует. Если мы в терминале откроем окно свойств для EURUSD, то увидим примерно такую картину:
В данном случае описание для EURUSD такое - "EURUSD, Euro vs US Dollar". Чтобы получить эту информацию, воспользуемся функцией SymbolInfoString():
string symbol=SymbolInfoString(_Symbol,SYMBOL_DESCRIPTION); Print("Символ: "+symbol); string symbol_path=SymbolInfoString(_Symbol,SYMBOL_PATH); Print("Путь: "+symbol_path);
Чтобы узнать размер стандартного контракта, используйте функцию SymbolInfoDouble():
double lot_size=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE); Print("Стандартный контракт: "+DoubleToString(lot_size,2));
Для форексных инструментов характерно то, что одна валюта покупается, а другая продается. Контракт указывается в той валюте, которую необходимо купить. Эта валюта называется базовой, и получить ее можно функцией SymbolInfoString():
string base_currency=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE); Print("Валюта базовая: "+base_currency);
Изменения цены инструмента приводят к изменению стоимости купленного актива и, соответственно, колебаниям незафиксированной прибыли по открытой позиции (прибыль может быть и отрицательной, если позиция убыточна). Таким образом, изменения цены влекут за собой изменения прибыли, выраженной в определенной валюте. Эта валюта называется валютой котировки. Для валютной пары EURUSD базовой валютой обычно является Евро, а валютой котировки - американский доллар. Получить валюту котировки также можно с помощью функции SymbolInfoString():
string profit_currency=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_PROFIT); Print("Валюта котировки: "+profit_currency);
Для открытия позиции по инструменту требуются средства, эти средства также выражаются в определенной валюте. Эта валюта называется валютой маржи или залога. Для форексных инструментов обычно валюта маржи и базовая валюта совпадают. Получить значение валюты залога можно функцией SymbolInfoString():
string margin_currency=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_MARGIN); Print("Валюта залога: "+margin_currency);
Все описанные функции приведены в коде эксперта Symbol_Info.mq5. На представленном ниже рисунке показан вывод информации по символу EURUSD с помощью функции Comment().
Вычисление размера залога
Наиболее востребованная для трейдера информация о финансовом инструменте - это размер денежных средств, необходимых для открытия позиции по нему. Не зная того, сколько денег необходимо для покупки или продажи заданного количества лотов, нельзя реализовать в эксперте систему управления капиталом. Кроме того, контроль над состоянием счета также становится затруднителен.
Если у вас возникнут сложности с пониманием дальнейшего изложения, рекомендую почитать статью Азбука торговли валютами. Описанные в ней объяснения применимы и в этой статье.
Нам необходимо вычислить размер маржи в валюте депозита, то есть пересчитать залог из валюты залога в валюту депозита, разделив полученное значение на размер предоставленного для этого счета плеча. Напишем для этого функцию GetMarginForOpening():
//+------------------------------------------------------------------+ //| возвращает размер средств, необходимых для открытия позиции | //+------------------------------------------------------------------+ double GetMarginForOpening(double lot,string symbol,ENUM_POSITION_TYPE direction) { double answer=0; //--- ... //--- вернем результат - размер средств, необходимых для открытия позиции в указанном объеме return(answer); }
где:
- lot - объем открываемой позиции;
- symbol - имя финансового инструмента;
- предполагаемое направление позиции.
Итак, мы имеем следующую информацию для вычисления размера маржи (денежные средства под залог открытой позиции):
- валюта депозита
- валюта залога
- валюта котировки (для кроссовых
валютных пар она может понадобиться)
- размер контракта
Запишем это на языке MQL5:
//--- получим размер контракта double lot_size=SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE); //--- получим валюту счета string account_currency=AccountInfoString(ACCOUNT_CURRENCY); //--- валюта, в которой берутся залоговые средства string margin_currency=SymbolInfoString(symbol,SYMBOL_CURRENCY_MARGIN); //--- валюта котировки string profit_currency=SymbolInfoString(symbol,SYMBOL_CURRENCY_PROFIT); //--- валюта для расчета string calc_currency=""; //--- обратная котировка - true, прямая - false bool mode;
Переменная mode влияет на то, как мы будем вычислять размер контракта в валюте депозита. Рассмотрим все на примерах, везде в дальнейшем предполагается, что валютой депозита является доллар США.
Исторически валютные пары принято делить на три
категории:
- прямая валютная пара - курс американского доллара к определенной валюте. Примеры: USDCHF, USDCAD, USDJPY, USDSEK;
- обратная валютная пара - курс определенной валюты к американскому доллару. Примеры: EURUSD, GBPUSD, AUDUSD, NZDUSD;
- кроссовая валютная пара - валютная пара, в которой не участвует
американский доллар. Примеры: AUDCAD, EURJPY, EURCAD.
1. EURUSD - обратная валютная пара
Мы будем называть обратными валютными пары те, у которых валютой котировки является валюта счета. В наших примерах валютой счета принят американский доллар, поэтому наша классификация валютных пар будет совпадать с общепринятой классификацией. Но если ваш торговый счет учитывается в любой другой валюте (не USD), то это будет не так. В таком случае принимайте во внимание именно валюту счета, чтобы понимать все дальнейшие объяснения.
Размер контракта для EURUSD - 100 000 евро. Нам необходимо выразить 100 000 евро в валюте депозита - долларах США. Для этого нужно знать курс, по которому можно пересчитать евро в доллары. Введем понятие расчетная валюта, то есть валюта, необходимая для конвертации валюты залога в валюту депозита.
//--- валюта для расчета string calc_currency="";
К счастью, валютная пара EURUSD как раз и отображает курс евро к доллару, а следовательно, для этого случая символ EURUSD, для которого нужно рассчитать размер залога, как раз и является валютой расчета:
//--- если валюта котировки символа и валюта депозита одинаковы if(profit_currency==account_currency) { calc_currency=symbol; mode=true; }
Мы установили значение mode равным true, это означает, что для перевода евро в доллары (валюта залога конвертируется в валюту депозита) мы будем умножать текущий курс EURUSD на размер контракта. Если mode=false, то мы делили бы размер контракта на курс валюты расчета. Для получения текущих цен по инструменту служит функция SymbolInfoTick().
//--- валюта для расчетов известна, теперь получим последние цены по ней MqlTick tick; SymbolInfoTick(calc_currency,tick);
Эта функция помещает текущие цены и время последнего обновления цен в переменную, имеющую тип специально для этого предназначенной структуры MqlTick.
Поэтому достаточно получить последнюю цену по этому символу, умножить ее на размер контракта и умножить на количество лотов. Только какую цену расчетов взять, если есть цена покупки и цена продажи по этому инструменту? Логичным будет, если мы покупаем, то цена для расчетов равна цене Ask, а для продажи нужно будет брать цену Bid.
//--- теперь все есть для вычисления, double calc_price; //--- считаем для покупки if(direction==POSITION_TYPE_BUY) { //--- обратная котировка if(mode) { //--- считаем по цене покупки для обратной котировки calc_price=tick.ask; answer=lot*lot_size*calc_price; } } //--- считаем для продажи if(direction==POSITION_TYPE_SELL) { //--- обратная котировка if(mode) { //--- считаем по цене продажи для обратной котировки calc_price=tick.bid; answer=lot*lot_size*calc_price; } }
Таким образом, в нашем примере для символа EURUSD валютой залога является Евро, размер контракта составляет 100 000, последняя цена Ask=1.2500. Валюта счета - доллар США, а расчетной валютой является сама же валютная пара EURUSD. Умножаем 100 000 на 1.2500 и получаем 125 000 долларов США - именно столько стоит стандартный контракт для покупки 1 лота EURUSD, если цена Ask=1.2500.
Можно сделать вывод: если валюта котировки равна валюте счета, то для получения стоимости одного лота в валюте счета, мы просто умножаем размер контракта на соответствующую цену Ask или Bid в зависимости от предполагаемого направления позиции.
margin=lots*lot_size*rate/leverage;
2. USDCHF - прямая валютная пара
Валюта залога и валюта счета для USDCHF совпадают - доллар США. Такие валютные пары, у которых валюта залога и валюта счета одинаковы, мы будем называть прямыми. Размер контракта - 100 000. Это самый простой случай, просто возвращаем произведение.
//--- если базовая валюта символа и валюта депозита одинаковы if(margin_currency==account_currency) { calc_currency=symbol; //--- просто вернем значение контракта, умноженное на количество лотов return(lot*lot_size); }
Если валюта залога совпадает с валютой счета, то значение залога в валюте счета равно произведению стандартного контракта на количество лотов (контрактов), деленному на размер плеча.
margin=lots*lot_size/leverage;
3. CADCHF - кроссовая валютная пара
Валютная пара CADCHF взята только для примера, вместо него может быть любая другая, у которой ни валюта залога, ни валюта котировки не совпадают с валютой счета. Такие валютные пары называют кроссовыми, потому что для вычисления маржи и прибыли по ним требуется знать курс какой-то другой валютной пары, которая пересекается с этой по одной из валют.
Обычно под кроссовой валютной парой подразумеваются те валютные пары, в котировку которых не входит американский доллар. Мы же под кроссовой парой будем иметь в виду пары, у которых в котировку не входит валюта счета. Так, если валюта счета Евро, то пара GBPUSD будет для нас кроссовой, так как валюта залога у нее английский фунт, а валюта котировки - американский доллар. В этом случае чтобы вычислить маржу, нам придется выразить фунты (GBP) евро (EUR).
Но мы продолжим рассмотрение примера, когда в качестве символа выступает валютная пара CADCHF. Валюта залога канадский доллар (CAD) не совпадает с американским долларом (USD). Валюта котировки швейцарский франк, также не совпадает с американским долларом.
Мы только можем сказать, что залог под открытие позиции в 1 лот составляет 100 000 канадских долларов. Наша задача - пересчитать залог в валюту счета, в доллары США. Для этого нам необходимо найти валютную пару, чей курс содержит доллар США и валюту залога - CAD. Всего имеется два потенциальных варианта:
- CADUSD
- USDCAD
Итак, для CADCHF имеем исходные данные:
margin_currency=CAD (канадский доллар) profit_currency=CHF (швейцарский франк)
Мы не знаем заранее, которая из валютных пар существует в терминале, и с точки зрения языка MQL5 ни один из вариантов не является предпочтительным. Поэтому напишем функцию GetSymbolByCurrencies(), которая по заданному набору валют выдаст нам первую подходящую для расчетов валютную пару.
//+------------------------------------------------------------------+ //| возвращает символ с заданными валютами залога и котировки | //+------------------------------------------------------------------+ string GetSymbolByCurrencies(string margin_currency,string profit_currency) { //--- переберем в цикле все символы, которые представлены в окне "Обзор рынка" for(int s=0;s<SymbolsTotal(true);s++) { //--- получим имя символа по номеру в списке "Обзор рынка" string symbolname=SymbolName(s,true); //--- получим валюту залога string m_cur=SymbolInfoString(symbolname,SYMBOL_CURRENCY_MARGIN); //--- получим валюту котировки (в чем измеряется прибыль при изменении цены) string p_cur=SymbolInfoString(symbolname,SYMBOL_CURRENCY_PROFIT); //--- если символ подошел по обеим заданным валютам, вернем имя символа if(m_cur==margin_currency && p_cur==profit_currency) return(symbolname); } return(NULL); }
Как видно из кода, мы начинаем перебор всех символов, доступных в окне "Обзор рынка" (функция SymbolsTotal() с параметром true даст нам это количество). Для получения имени каждого символа по номеру в списке "Обзор рынка" используем функцию SymbolName() опять же с параметром true! Если указать параметр false, то будут перебираться все символы, представленные на торговом сервере, а это обычно больше, чем выбрано в терминале.
Далее по имени символа получаем валюты залога и котировки и сравниваем с теми, что были переданы в функцию GetSymbolByCurrencies(). В случае успеха возвращаем имя символа, работа функции завершена успешно и досрочно. Если цикл пройден до конца, и мы дошли до последней строчки функции, значит, ничего не подошло, символ не найден - возвращаем NULL.
Теперь, когда мы можем получить с помощью функции GetSymbolByCurrencies() валюту расчетов для кроссовой валютной пары, сделаем две попытки: в первой попытки поищем символ, у которого валютой залога является margin_currency (валюта залога CADCHF - CAD), а валютой котировки является валюта счета (USD). То есть, мы ищем что-то вроде пары CADUSD.
//--- если до сих пор валюта для расчета не определена //--- значит перед нами кроссовая валюта if(calc_currency="") { calc_currency=GetSymbolByCurrencies(margin_currency,account_currency); mode=true; //--- если полученное значение равно NULL, значит, такой символ не найден if(calc_currency==NULL) { //--- попробуем наоборот calc_currency=GetSymbolByCurrencies(account_currency,margin_currency); mode=false; } }
Если попытка не удалась, попробуем найти другой вариант: ищем символ, у которого валютой залога является account_currency (USD), а валютой котировки является margin_currency (валюта залога для CADCHF - CAD). Мы ищем что-то похожее на USDCAD.
Теперь, когда мы нашли валютную пару для расчета, она может быть одной из двух вариантов - прямой или обратной. Переменная mode у нас принимает значение true для обратной валютной пары. Если же у нас прямая валютная пара, значение равно false. Для значения true мы умножаем на курс валютной пары, для false - делим на курс валютной пары, чтобы получить значение залога для стандартного контракта в валюте счета.
Приведем окончательный расчет размера залога в валюте счета для найденной валюты расчетов. Он подходит для обоих вариантов - прямой и обратной валютных пар.
//--- валюта для расчетов известна,теперь получим последние цены по ней MqlTick tick; SymbolInfoTick(calc_currency,tick); //--- теперь все есть для вычисления, double calc_price; //--- считаем для покупки if(direction==POSITION_TYPE_BUY) { //--- обратная котировка if(mode) { //--- считаем по цене покупки для обратной котировки calc_price=tick.ask; answer=lot*lot_size*calc_price; } //--- прямая котировка else { //--- считаем по цене продажи для прямой котировки calc_price=tick.bid; answer=lot*lot_size/calc_price; } } //--- считаем для продажи if(direction==POSITION_TYPE_SELL) { //--- обратная котировка if(mode) { //--- считаем по цене продажи для обратной котировки calc_price=tick.bid; answer=lot*lot_size*calc_price; } //--- прямая котировка else { //--- считаем по цене покупки для прямой котировки calc_price=tick.ask; answer=lot*lot_size/calc_price; } }
Вернем полученный результат
//--- вернем результат - размер средств в валюте счета, необходимых для открытия позиции в указанном объеме return(answer);
Функция GetMarginForOpening() на этом заканчивает свою работу. Осталось сделать последнее - разделить полученное значение на размер предоставленного плеча - и тогда мы получим значение маржи под открытие позиции с указанным объемом в предполагаемом направлении. Имейте в виду, что для символов, представляющих собой обратную или кроссовую пару, значение маржи будет изменяться с каждым тиком.
Приведем часть кода эксперта SymbolInfo_Advanced.mq5, полный код прилагается в виде файла.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- строковая переменная для "сборки" комментария string com="\r\n"; StringAdd(com,Symbol()); StringAdd(com,"\r\n"); //--- размер стандартного контракта double lot_size=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE); //--- валюта залога string margin_currency=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_MARGIN); StringAdd(com,StringFormat("Cтандартный контракт: %.2f %s",lot_size,margin_currency)); StringAdd(com,"\r\n"); //--- предоставляемое плечо int leverage=(int)AccountInfoInteger(ACCOUNT_LEVERAGE); StringAdd(com,StringFormat("Плечо: 1/%d",leverage)); StringAdd(com,"\r\n"); //--- вычислим стоимость контракта в валюте депозита StringAdd(com,"Залог для открытия позиции в 1 лот составляет "); //--- вычислим маржу с учетом предоставленного плеча double margin=GetMarginForOpening(1,Symbol(),POSITION_TYPE_BUY)/leverage; StringAdd(com,DoubleToString(margin,2)); StringAdd(com," "+AccountInfoString(ACCOUNT_CURRENCY)); Comment(com); }
и результат его работы на графике.
Заключение
Представленные примеры показывают, как легко и просто получить информацию о наиболее важных характеристиках торгового счета и о свойствах финансовых инструментов.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
В общем случае размер маржи можно получить с полмощью функции OrderCalcMargin()
Я вкурсе, мне докладывали :)
Хотелось бы разобраться откуда чего берётся и куда чего девается. Так сказать детальный анализ.
Я вкурсе, мне докладывали :)
Хотелось бы разобраться откуда чего берётся и куда чего девается. Так сказать детальный анализ.
Есть еще ENUM_SYMBOL_CALC_MODE
Спасибо Рашид, вы как всегда на высоте, зрите в корень, так сказать видите чего пользователи недопонимают.
это то что нужно, в описании ENUM_SYMBOL_CALC_MODE расписаны все формулы.
Подскажите ,что такое Percentage.Также где его посмотреть.
SYMBOL_CALC_MODE_CFD
CFD mode – расчет залога и прибыли для CFD
Margin: Lots *ContractSize*MarketPrice*Percentage/100
Profit: (close_price-open_price)*Contract_Size*Lots
По IBM выдает результат 2 (то что выше из таблицы) - торговля без плеча.
Далее непонятно ,а значит интересно:
MQ #IBM.Цена акции естественно однозначна,вычисление идет без плеча ( тип 2 ) .
Liteforex #IBM.Вычисление также без плеча (по типу 2 )
Размеры контрактов одинаковы,вычисления одинаковы.Маржа разная.Остается,что влияет этот Percentage.
Далее..Терминал значит выдает тип 2 -торговля без плеча.Тем не менее вот что написано в спецификации :
**** Плечо для всех контрактов на разницу (CFD) фиксировано и равно 1:20
Иcходя из этого мы имеем 19902 / 20 =995 $ . Значит в это й формуле Percentage и есть плечо.