Рецепты MQL5 – Получаем свойства открытой хеджевой позиции
Введение
Относительно недавно в торговом терминале MetaTrader 5 появилась возможность открывать разнонаправленные ордера. Такая система учёта ордеров называется «хеджинг». Наличие этой ордерной системы позволяет легко переносить торговые алгоритмы из MetaTrader 4 в пятую версию терминала, пользуясь всеми преимуществами последнего. Более подробно о хеджинге в MetaTrader 5 можно узнать в материале статьи "В MetaTrader 5 добавлена хеджинговая система учёта позиций".
В данной статье речь пойдёт о свойствах совокупной позиции, работа с которой идёт в системе «хеджинг».
1. Хеджевая позиция, типы
Хеджевая (совокупная) позиция — это рыночная позиция, которая образована несколькими рыночными ордерами. В узком смысле, хеджевая позиция (хедж) включает в себя разнонаправленные ордера (покупки и продажи). Однако предлагаю использовать термин «хедж» и в широком смысле тоже. При этом согласимся с тем, что хедж-позиция будет обобщать и ордера одного направления. Такой подход обусловлен возможностями терминала MetaTrader 5: можем открыть ордера в одном направлении, а можем в разных.
Для классификации совокупной позиции можно использовать несколько способов. Пожалуй, самым популярным является критерий, который различает позиции по типу рыночных ордеров, образующих ту или иную совокупную позицию. Итак, какими ордерами может быть заполнена такая позиция? Ниже в Таблице 1 представлены различные комбинации.
№ | Тип | Описание |
---|---|---|
1 | Hedge buy | Только покупки |
2 | Hedge netting buy | "Чистая" покупка |
3 | Hedge sell | Только продажи |
4 | Hedge netting sell | "Чистая" продажа |
5 | Hedge locked | Лок (полный хедж) |
Таблица 1. Типы хеджа
Кратко поясним указанные разновидности. Если в совокупной позиции есть ордера (в терминах MetaTrader 4) только на покупку или только на продажу, то такую позицию будем считать либо hedge buy, либо hedge sell. Если в позиции есть смешанные ордера (как покупки, так и продажи), то будем смотреть, каких ордеров больше. Если больше ордеров на покупку, то позиция будет hedge netting buy. А если больше ордеров на продажу - hedge netting sell. Причём, если быть более точным, нужно говорить не о количестве ордеров, а об их объёмах. Допустим, что в позиции есть 1 ордер на покупку объёмом 1,25 лота и два ордера на продажу по 0,5 и 0,6 лота соответственно. В итоге позиция будет чистой позицией на покупку (hedge netting buy) в 0,15 лота:
1,25 – (0,5 + 0,6) = 0,15.
Особым видом смешанной позиции является лок, в котором покупки и продажи уравновешивают друг друга по торговому объёму.
Описанные виды хеджа формализуем в следующем перечислении:
//+------------------------------------------------------------------+ //| Hedge type | //+------------------------------------------------------------------+ enum ENUM_HEDGE_TYPE { HEDGE_BUY=0, // buy HEDGE_SELL=1, // sell HEDGE_NETTING_BUY=2, // netting buy HEDGE_NETTING_SELL=3, // netting sell HEDGE_LOCKED=4, // lock };
Если обратиться к системе "неттинг", то там позиция, совокупная по определению, может относится только к одному из двух типов - либо buy, либо sell. Идентификатором служит одно из значений перечисления ENUM_POSITION_TYPE:
1) POSITION_TYPE_BUY;
2) POSITION_TYPE_SELL.
В системе же "хеджинг" получим 5 типов совокупной позиции.
В следующем разделе создадим класс для обработки свойств хеджевой позиции.
2. Класс CHedgePositionInfo
Отмечу, что в Стандартной библиотеке есть класс CPositionInfo, обеспечивающий доступ к свойствам открытой рыночной позиции. В нашем случае этот класс пригодится лишь отчасти, т.к. обрабатываемая им позиция будет представлена в виде отдельного рыночного ордера (в терминах MetaTrader 4). А нам нужен такой класс, который обрабатывает сразу все позиции, образующие совокупную позицию (хедж).
Воспользуемся ООП-средствами и создадим класс CHedgePositionInfo:
//+------------------------------------------------------------------+ //| Class CHedgePositionInfo | //| Purpose: Class for access to a hedge position info. | //| Derives from class CObject. | //+------------------------------------------------------------------+ class CHedgePositionInfo : public CObject { //--- === Data members === --- private: ENUM_HEDGE_TYPE m_type; double m_volume; double m_price; double m_stop_loss; double m_take_profit; ulong m_magic; //--- objects CArrayLong m_tickets; CSymbolInfo m_symbol; CPositionInfo m_pos_info; //--- === Methods === --- public: //--- constructor/destructor void CHedgePositionInfo(void){}; void ~CHedgePositionInfo(void){}; //--- initialization bool Init(const string _symbol,const ulong _magic=0); //--- get methods CSymbolInfo *Symbol(void) {return GetPointer(m_symbol);}; CArrayLong *HedgeTickets(void) {return GetPointer(m_tickets);}; CPositionInfo *PositionInfo(void) {return GetPointer(m_pos_info);}; ulong Magic(void) const {return m_magic;}; //--- fast access methods to the integer hedge properties datetime Time(void); ulong TimeMsc(void); datetime TimeUpdate(void); ulong TimeUpdateMsc(void); ENUM_HEDGE_TYPE HedgeType(void); //--- fast access methods to the double hedge properties double Volume(double &_buy_volume,double &_sell_volume); double PriceOpen(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); double StopLoss(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); double TakeProfit(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); double PriceCurrent(const ENUM_TRADE_TYPE_DIR _dir_type=TRADE_TYPE_ALL); double Commission(const bool _full=false); double Swap(void); double Profit(void); double Margin(void); //--- fast access methods to the string hedge properties string TypeDescription(void); //--- info methods string FormatType(string &_str,const uint _type) const; //--- select bool Select(void); //--- state void StoreState(void); bool CheckState(void); private: //--- calculation methods bool AveragePrice( const SPositionParams &_pos_params, double &_avg_pr, double &_base_volume, double &_quote_volume ); int CheckLoadHistory(ENUM_TIMEFRAMES period,datetime start_date); }; //+------------------------------------------------------------------+
Несколько слов о членах-данные класса.
Во-первых, есть уникальный символ. Т.е. хедж-позиция может включать любые позиции одного символа. За символ отвечает поле m_symbol, представляющее экземпляр класса CSymbolInfo.
Во-вторых, можно в качестве фильтра нужных ордеров задавать магик (m_magic). Таким образом, можно создать позицию, которую обслуживает какой-то один торговый советник. Тогда на одном символе можно создавать несколько хеджей.
Ещё есть динамический массив для учёта ордеров (m_tickets). В него будут попадать тикеты ордеров хедж-позиции.
Функции по получению свойств какой-то одной выбранной позиции (рыночного ордера в терминах MetaTrader 4) возложены на экземпляр класса CPositionInfo (m_pos_info).
Остальные свойства хеджа нужны для оценки его состояния:
- тип (m_type);
- объём (m_volume);
- цена открытия (m_price);
- цена стоп-лосса (m_stop_loss);
- цена тэйк-профита (m_take_profit).
Стоит отметить, что за основу конструирования класса была взята логика класса CPositionInfo. И это вполне естественно. Поэтому в новом классе есть методы, возвращающие целочисленные свойства, double-свойства и пр. Но конечно есть и методы, которые будут специфическими.
2.1 Метод инициализации
Прежде чем воспользоваться возможностями класса, нужно проинициализировать соответствующий экземпляр. Метод проверяет, что, во-первых, советник работает именно в рамках системы "хеджинг", во-вторых выбран нужный символ и также, возможно, задан магик.
//+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ bool CHedgePositionInfo::Init(const string _symbol,const ulong _magic=0) { //--- account margin mode ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); if(margin_mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { Print(__FUNCTION__+": no retail hedging!"); return false; } if(!m_symbol.Name(_symbol)) { Print(__FUNCTION__+": a symbol not selected!"); return false; } ENUM_SYMBOL_CALC_MODE symbol_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(_symbol,SYMBOL_TRADE_CALC_MODE); if(symbol_calc_mode!=SYMBOL_CALC_MODE_FOREX) { Print(__FUNCTION__+": only for Forex mode!"); return false; } m_magic=_magic; //--- return true; } //+------------------------------------------------------------------+
Данный метод является обязательным для последующего использования возможностей хеджевого класса. Отмечу, что в методе проверяется режим расчета маржи. Если он не соответствует "хеджингу", то метод вернёт ложь. Также проверяется способ вычисления стоимости контракта. Работать будем только с Форекс-контрактами.
2.2 Целочисленные свойства
Доступ к целочисленным свойствам осуществляется посредством методов:
- datetime Time(void);
- ulong TimeMsc(void);
- datetime TimeUpdate(void);
- ulong TimeUpdateMsc(void);
- ENUM_HEDGE_TYPE HedgeType(void).
Посмотрим, к примеру, на код метода CHedgePositionInfo::Time():
//+------------------------------------------------------------------+ //| Get the hedge open time | //+------------------------------------------------------------------+ datetime CHedgePositionInfo::Time(void) { datetime hedge_time=WRONG_VALUE; int hedge_pos_num=m_tickets.Total(); //--- if any positions if(hedge_pos_num>0) { //--- find the first opened position for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++) { ulong curr_pos_ticket=m_tickets.At(pos_idx); if(curr_pos_ticket<LONG_MAX) if(m_pos_info.SelectByTicket(curr_pos_ticket)) { datetime curr_pos_time=m_pos_info.Time(); if(curr_pos_time>0) { if(hedge_time==0) hedge_time=curr_pos_time; else { if(curr_pos_time<hedge_time) hedge_time=curr_pos_time; } } } } } //--- return hedge_time; } //+------------------------------------------------------------------+
Чтобы получить время открытия хеджа, а по сути первой позиции хеджа, нужно перебрать все его позиции и найти самую раннюю.
А вот чтобы получить время изменения хеджа, а по сути позиции, которая была изменена последней, нужно немного модифицировать предыдущий метод:
//+------------------------------------------------------------------+ //| Get the hedge update time | //+------------------------------------------------------------------+ datetime CHedgePositionInfo::TimeUpdate(void) { datetime hedge_time_update=0; int hedge_pos_num=m_tickets.Total(); //--- if any positions if(hedge_pos_num>0) { //--- find the first opened position for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++) { ulong curr_pos_ticket=m_tickets.At(pos_idx); if(curr_pos_ticket<LONG_MAX) if(m_pos_info.SelectByTicket(curr_pos_ticket)) { //--- get the current position update time datetime curr_pos_time_update=m_pos_info.TimeUpdate(); if(curr_pos_time_update>0) if(curr_pos_time_update>hedge_time_update) hedge_time_update=curr_pos_time_update; } } } //--- return hedge_time_update; } //+------------------------------------------------------------------+
Метод определения типа хеджа закодирован следующим образом:
//+------------------------------------------------------------------+ //| Get the hedge type | //+------------------------------------------------------------------+ ENUM_HEDGE_TYPE CHedgePositionInfo::HedgeType(void) { ENUM_HEDGE_TYPE curr_hedge_type=WRONG_VALUE; int hedge_pos_num=m_tickets.Total(); //--- if any positions if(hedge_pos_num>0) { //--- get the volumes double total_vol,buy_volume,sell_volume; buy_volume=sell_volume=0.; total_vol=this.Volume(buy_volume,sell_volume); //--- define a hedge type if(buy_volume>0. && sell_volume>0.) { if(buy_volume>sell_volume) curr_hedge_type=HEDGE_NETTING_BUY; else if(buy_volume<sell_volume) curr_hedge_type=HEDGE_NETTING_SELL; else curr_hedge_type=HEDGE_LOCKED; } else if(buy_volume>0. && sell_volume==0.) curr_hedge_type=HEDGE_BUY; else if(buy_volume==0. && sell_volume>0.) curr_hedge_type=HEDGE_SELL; } //--- return curr_hedge_type; }; //+------------------------------------------------------------------+
Тип хеджа зависит от разницы объёмов покупок и продаж. Сначала проверяем, действительно ли хедж является таковым в узком смысле. Если объёмы покупок и продаж равны, то хедж полный. Если есть неравенство, то мы имеем дело с частичным хеджем.
Затем проверяем, не представлен ли хедж либо только покупками, либо только продажами.
2.3 Double-свойства
Доступ к double-свойствам осуществляется посредством методов:
- double Volume(double &_buy_volume,double &_sell_volume);
- double PriceOpen(void);
- double StopLoss(void);
- double TakeProfit(void);
- double PriceCurrent(void);
- double Commission(void);
- double Swap(void);
- double Profit(void);
- double Margin(void).
Легко заметить, что метод определения объёма хеджа имеет параметры в виде ссылок. Такая реализация позволяет сразу получить как величину объёма самого хеджа, так и его составляющих (покупок и продаж).
//+------------------------------------------------------------------+ //| Get the hedge volume | //+------------------------------------------------------------------+ double CHedgePositionInfo::Volume(double &_buy_volume,double &_sell_volume) { double total_vol=0.; int hedge_pos_num=m_tickets.Total(); //--- if any positions if(hedge_pos_num>0) { _buy_volume=_sell_volume=0.; //--- get the buy\sell volumes for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++) { ulong curr_pos_ticket=m_tickets.At(pos_idx); if(curr_pos_ticket<LONG_MAX) if(m_pos_info.SelectByTicket(curr_pos_ticket)) { ENUM_POSITION_TYPE curr_pos_type=m_pos_info.PositionType(); double curr_pos_vol=m_pos_info.Volume(); if(curr_pos_vol>0.) { //--- for a buy position if(curr_pos_type==POSITION_TYPE_BUY) _buy_volume+=curr_pos_vol; //--- else for a sell position else if(curr_pos_type==POSITION_TYPE_SELL) _sell_volume+=curr_pos_vol; } } } total_vol=_buy_volume-_sell_volume; } //--- return total_vol; } //+------------------------------------------------------------------+
Для работы с ценовыми свойствами был создан вспомогательный метод CHedgePositionInfo::AveragePrice(). Приведу блок кода, где он считает среднюю цену хеджа в зависимости от типа ценового уровня:
//--- if the hedge volumes calculated if(hedge_base_volume!=0. && hedge_quote_volume!=0.) { _avg_pr=fabs(hedge_quote_volume/hedge_base_volume); _base_volume=hedge_base_volume; _quote_volume=hedge_quote_volume; return true; }
При этом используется стоимостной подход - отношение итоговой стоимости хеджа в валюте котировки к итоговой стоимости хеджа в базовой валюте.
Позже посмотрим на конкретный расчёт при работе с примером.
Методы получения свопа и профита суммируют соответствующие показатели каждой позиции хеджа, а затем возвращают итоговое значение. Метод получения комиссии анализирует сделки, которые участвовали в открытии позиции. Причём можно выбрать через параметр, как рассчитывать размер комиссии. Если хотим получить его только по сделкам входа, то оставляем параметр по умолчанию. А если нужно подсчитать комиссию за вход и выход, то параметр должен быть равен true. Отмечу, что последний способ носит примерный характер по нескольким причинам. Во-первых, мы можем закрыть выбранные позиции встречными. Тогда появятся сделки типа DEAL_ENTRY_OUT_BY. А за них комиссия не взимается. А во-вторых, если валюта счёта не совпадает с базовой валютой, то при изменении курсов стоимость входа и выхода могут отличаться.
//+------------------------------------------------------------------+ //| Get the hedge commission | //+------------------------------------------------------------------+ double CHedgePositionInfo::Commission(const bool _full=false) { double hedge_commission=0.; int hedge_pos_num=m_tickets.Total(); //--- if any positions if(hedge_pos_num>0) for(int pos_idx=0;pos_idx<hedge_pos_num;pos_idx++) { ulong curr_pos_ticket=m_tickets.At(pos_idx); if(curr_pos_ticket<LONG_MAX) if(m_pos_info.SelectByTicket(curr_pos_ticket)) { long curr_pos_id=m_pos_info.Identifier(); if(curr_pos_id>0) //--- retrieve the history of deals associated with the selected position if(HistorySelectByPosition(curr_pos_id)) { CDealInfo curr_deal; int deals_num=HistoryDealsTotal(); for(int deal_idx=0;deal_idx<deals_num;deal_idx++) if(curr_deal.SelectByIndex(deal_idx)) { ENUM_DEAL_ENTRY curr_deal_entry=curr_deal.Entry(); if(curr_deal_entry==DEAL_ENTRY_IN) { double curr_deal_commission=NormalizeDouble(curr_deal.Commission(),2); if(curr_deal_commission!=0.) { double fac=1.; if(_full) fac=2.; hedge_commission+=(fac*curr_deal_commission); } } } } } } //--- return hedge_commission; } //+------------------------------------------------------------------+
Отмечу, что в классе есть метод CHedgePositionInfo::Margin(), позволяющий определить величину залога для хеджевой позиции. На самом деле этот метод оказался самым сложным для программирования. По-хорошему, можно посвятить целую статью тому, как правильно определять размер залоговых средств для открытых позиций и отложенных ордеров.
2.3.1 Залог для хеджевой позиции
Как указывает разработчик, при наличии разнонаправленных позиций существует 2 способа расчёта маржи, которые определяются брокером. Первый способ основан на базовом расчёте, а второй реализован по наибольшей стороне.
Признаюсь, что я не сталкивался со вторым способом подсчёта. Тем не менее запрограммируем и его. Но сначала предлагаю рассмотреть первый способ, который будет иметь более сложный алгоритм, включающий в себя расчёт маржи:
- Для неперекрытого объема;
- Для перекрытого объема (если указан размер хеджированной маржи);
- Для отложенных ордеров.
Маржа в данной статье рассчитывается для модели Retail Forex, Futures. Расчёт маржи для отложенных ордеров не рассматривается.
Для полноценного расчёта маржи для хеджевой позиции нужна будет информация по следующим параметрам:
- Валюта депозита. Как правило, счёта номинированы в долларах США (USD), евро (EUR), фунтах (GBP), франках (CHF).
- Валюта маржи. Как правило, это базовая валюта символа. Например, для пары EURUSD такой будет евро (EUR), а для кросса AUDNZD — австралийский доллар (AUD).
- Размер плеча.
Кроме того замечу, что в строке баланса на вкладке «Торговля» терминала значение маржи будет указано в валюте депозита. Поэтому результатом расчёта должно стать значение маржи именно в валюте депозита.
А так как валюта маржи может не совпадать с валютой депозита, то есть несколько вариантов алгоритма расчёта:
- Когда валюта депозита присутствует в символе хеджевой позиции в виде базовой валюты. Допустим, торгуете USDCHF на долларовом счёте.
- Когда валюта депозита присутствует в символе хеджевой позиции в виде котируемой валюты. Допустим, торгуете EUR USD на долларовом счёте.
- Когда валюты депозита нет в символе хеджевой позиции. Допустим, торгуете AUDNZD на долларовом счёте.
Первый вариант будет самым простым для расчёта, а последний – самым сложным. Займёмся бухгалтерией и рассмотрим примеры для каждого варианта.
Первый вариант
Пусть есть долларовый счёт (депозит), на котором открыто 5 позиций по символу USDCHF (Рис.1).
Рис.1 Рыночные позиции по паре USDCHF
Базовые параметры:
Валюта счёта - USD.
Валюта маржи - USD.
Размер плеча - 1:100.
Есть 3 позиции на покупку. Общий объём позиций равен 5,55 лота, что в стоимостном выражении составляет $555 000. И ещё есть 2 позиции на продажу. Общий объём позиций равен 7,5 лота, что в стоимостном выражении составляет $750 000.
А) расчёт для неперекрытого объёма
Неперекрытый объём равен 1,95 лота или $195 000. Относится он к продажам, т.к. продали больше чем купили. Хотя в данном случае это совсем неважно – покупки или продажи, ведь не нужно расчитывать средневзвешенную цену.
Маржа от этой суммы берётся с учётом плеча:
$195 000 / 100 = $1 950.
Б) расчёт для перекрытого объёма
Перекрытый объём равен 5,55 лота или $555 000.
Маржа от этой суммы берётся с учётом плеча:
$555 000 / 100 = $5 550.
Тогда валовая маржа рассчитывается как сумма маржи для неперекрытого объёма и маржи для перекрытого объёма:
$1 950 + $5 550 = $7 500.
Именно это значение мы и видим в торговом терминале для показателя "Маржа".
Второй вариант
Пусть есть долларовый счёт (депозит), на котором открыто 5 позиций по символу EURUSD (Рис.2).
Рис.2 Рыночные позиции по паре EURUSD
Базовые параметры:
- Валюта счёта - USD.
- Валюта маржи - EUR.
- Размер плеча - 1:300.
Есть 3 позиции на покупку. Общий объём позиций равен 5,55 лота, что в стоимостном выражении составляет €555 000 или $645 617.20. Средневзвешенная цена покупок равна $1.163274.
Есть 2 позиции на продажу. Общий объём позиций равен 7,50 лота, что в стоимостном выражении составляет €750 000 или $872 409. Средневзвешенная цена продаж равна $1.163212.
Все позиции представлены в Таблице 2.
Type | Volume | Price | Value, $ |
---|---|---|---|
buy | 1.75 | 1.16329 | 203 575.75 |
buy | 2.55 | 1.16329 | 296 638.95 |
buy | 1.25 | 1.16322 | 145 402.50 |
sell | 3.00 | 1.16323 | 348 969.00 |
sell | 4.50 | 1.16320 | 523 440.00 |
Total | 13.05 | 1.1632385 | 1 518 026.20 |
Таблица.2 Рыночные позиции по паре EURUSD
Всего есть 5 позиций. Общий объём позиций равен 13,05 лота, что в стоимостном выражении составляет €1 305 000 или $1 518 026.20. Средневзвешенная цена позиций равна $1.16324.
А) расчёт для неперекрытого объёма
Неперекрытый объём равен 1,95 лота или €195 000. Относится он к продажам, т.к. продали больше чем купили. Поэтому для определения стоимости этого объёма берём средневзвешенную цену продаж:
$1.163212 * €195 000 = $226 826.34.
Маржа от этой суммы берётся с учётом плеча:
$226 826.34 / 300 = $756.09.
Б) расчёт для перекрытого объёма
Перекрытый объём равен 5,55 лота или €555 000. Для определения стоимости этого объёма берём средневзвешенную цену всех позиций:
$1.1632385 * €555 000 = $645 597.35.
Маржа от этой суммы берётся с учётом плеча:
$645 597.35 / 300 = $2 151,99.
Тогда, по идее, вся валовая маржа должна составить:
$756.09 + $2 151.99 = $2 908.08.
Однако в терминале мы видим значение $1 832.08.
Дело в том, что для перекрытого объёма учитывается значение параметра "Хеджированная маржа" из спецификации инструмента. Если она меньше размера контракта, то получим некоторый мультипликатор. В данном случае спецификация для заданного параметра задаёт значение 50000. Тогда:
Стоимость перекрытого объёма = $1.1632385 * €555 000 / (100 000 / 50 000) = $322 798,67.
Маржа для перекрытого объёма = $322 798,67 / 300 = $1 076.00.
В сумме: $756.09 + $1 076.00 = $1 832.08. Именно это значение соответствует терминальному.
Третий вариант
Пусть есть долларовый счёт (депозит), на котором открыто 5 позиций по символу AUDNZD (Рис.3).
Рис.3 Рыночные позиции по кроссу AUDNZD
Базовые параметры:
- Валюта счёта - USD.
- Валюта маржи - AUD.
- Размер плеча - 1:300.
Есть 3 позиции на покупку. Общий объём позиций равен 5,55 лота, что в стоимостном выражении составляет A$555 000 или $400 442.35. Средневзвешенная цена покупок равна $0.7215178.
Есть 2 позиции на продажу. Общий объём позиций равен 7,50 лота, что в стоимостном выражении составляет A$750 000 или $541 035.00. Средневзвешенная цена продаж равна $0.72138.
Все позиции представлены в Таблице 3.
Type | Volume | Price | Value, $ |
---|---|---|---|
buy | 1.75 | 0.72152 | 126 266.00 |
buy | 2.55 | 0.72152 | 183 987.60 |
buy | 1.25 | 0.72151 | 90 188.75 |
sell | 3.00 | 0.72144 | 216 432.00 |
sell | 4.50 | 0.72134 | 324 603.00 |
Total | 13.05 | 0.72144 | 941 477.35 |
Таблица.3 Рыночные позиции по кроссу AUDNZD
Легко заметить, что в столбце Price представлены цены открытия позиций не по самому символу AUDNZD, а по символу AUDUSD. Сделано это для того, чтобы сразу оценить торгуемый объём в валюте счёта. При этом возникает необходимость обращаться к истории тиков и котировок пары, в которой представлены валюта счёта и валюта маржи. Поэтому рассчитанные значения могут незначительно отличаться от фактических.
Всего есть 5 позиций. Общий объём позиций равен 13,05 лота, что в стоимостном выражении составляет A$1 305 000 или $941 477.35. Средневзвешенная цена позиций равна $0.72144.
А) расчёт для неперекрытого объёма
Неперекрытый объём равен 1,95 лота или A$195 000. Относится он к продажам, т.к. продали больше чем купили. Поэтому для определения стоимости этого объёма берём средневзвешенную цену продаж:
$0.72138 * A$195 000 = $140 669.10.
Маржа от этой суммы берётся с учётом плеча:
$140 669.10 / 300 = $468.90.
Б) расчёт для перекрытого объёма
Перекрытый объём равен 5,55 лота или A$555 000. Для определения стоимости этого объёма берём средневзвешенную цену всех позиций с учётом значения параметра "Хеджированная маржа":
$0.72144 * A$555 000 / (100 000 / 50 000) = $200 199.21.
Маржа от этой суммы берётся с учётом плеча:
$200 199.21/ 300 = $667.33.
В сумме: $468.90 + $667.33 = $1 136.23. Проверяем согласно Рис.3: оно совпадает с терминальным.
2.4 Прочие свойства
В классе ещё есть методы, которые работают с состоянием хеджа: StoreState() и CheckState(). Так же как и с обычной позицией, оно определяется значениями типа, объёма, цены открытия, цены стоп-лосса и тэйк-профита.
Единственный метод текстового свойства TypeDescription() возвращает тип хеджа в виде строки.
Особо стоит отметить метод выбора хеджевой позиции Select(). Код метода представлен ниже:
//+------------------------------------------------------------------+ //| Selects hedge positions | //+------------------------------------------------------------------+ bool CHedgePositionInfo::Select(void) { string hedge_symbol=m_symbol.Name(); //--- clear all positions m_tickets.Shutdown(); //--- collect positions int pos_num=PositionsTotal(); for(int pos_idx=0;pos_idx<pos_num;pos_idx++) if(m_pos_info.SelectByIndex(pos_idx)) { string curr_pos_symbol=m_pos_info.Symbol(); //--- select by symbol if(!StringCompare(hedge_symbol,curr_pos_symbol)) { //--- if to select by magic bool is_the_same_magic=true; if(m_magic>0) { long curr_pos_magic=m_pos_info.Magic(); if(m_magic!=curr_pos_magic) is_the_same_magic=false; } if(is_the_same_magic) { ulong curr_pos_ticket=m_pos_info.Ticket(); if(curr_pos_ticket>0) if(!m_tickets.Add(curr_pos_ticket)) { PrintFormat(__FUNCTION__+": failed to add #%d ticket!",curr_pos_ticket); return false; } } } } //--- return m_tickets.Total()>0; } //+------------------------------------------------------------------+
Основная задача метода - обновить тикеты позиций, входящих в состав хеджа. В него попадают рыночные позиции, если их символ совпадает с символом самого хеджа. Также можно добавить такой фильтр селекции, как магик.
3. Примеры
Итак, мы создали класс, который работает со свойствами хеджевой позиции. В данном разделе предлагаю поработать с практическими примерами. Начнём с простого скрипта.
3.1 Тестовый скрипт
Для учебных целей был создан скрипт Test_hedge_properties.mq5, который выводит в журнал на вкладке "Эксперты" информацию о свойствах хеджа.
В первом варианте расчёта залога у нас было 5 позиций по символу USDCHF (Рис.1). Запустим скрипт и получим в журнале такую информацию:
2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) ---== Hedge properties==--- 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Symbol: USDCHF 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Positions total = 5 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) 1) #293972991 buy 1.75 USDCHF 0.97160000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) 2) #293974150 buy 2.55 USDCHF 0.97142000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) 3) #293974889 sell 3.00 USDCHF 0.97157000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) 4) #293975329 sell 4.50 USDCHF 0.97164000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) 5) #293976289 buy 1.25 USDCHF 0.97205000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Magic: 0 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Time: 2018.08.29 17:15:44 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Time in msc: 1535562944628 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Update time: 2018.08.29 17:20:35 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Update time in msc: 1535563235034 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Type: HEDGE_NETTING_SELL 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Type description: hedge netting sell 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Volume: -1.95 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Buy volume: 5.55 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Sell volume: 7.50 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Open price: 0.97159 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Sl-price: -1.00000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Tp-price: -1.00000 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Current price: 0.96956 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Commission: 0.00 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Swap: -35.79 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Profit: 409.77 2018.09.03 18:51:37.078 Test_hedge_properties (AUDNZD,H1) Margin: 7500.00
Во втором варианте обрабатывали свойства позиций по EURUSD (Рис.2). После запуска скрипта получили такую информацию:
2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) ---== Hedge properties==--- 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Symbol: EURUSD 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Positions total = 5 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) 1) #119213986 buy 1.75 EURUSD 1.16329000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) 2) #119214003 buy 2.55 EURUSD 1.16329000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) 3) #119214004 buy 1.25 EURUSD 1.16322000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) 4) #119214011 sell 3.00 EURUSD 1.16323000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) 5) #119214021 sell 4.50 EURUSD 1.16320000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Magic: 0 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Time: 2018.08.31 16:38:10 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Time in msc: 1535733490531 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Update time: 2018.08.31 16:38:49 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Update time in msc: 1535733529678 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Type: HEDGE_NETTING_SELL 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Type description: hedge netting sell 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Volume: -1.95 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Buy volume: 5.55 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Sell volume: 7.50 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Open price: 1.16303 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Sl-price: -1.00000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Tp-price: -1.00000 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Current price: 1.16198 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Commission: 0.00 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Swap: -37.20 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Profit: 206.60 2018.09.03 18:55:09.469 Test_hedge_properties (AUDNZD,H1) Margin: 1832.08
В третьем варианте работа шла с позициями по символу AUDNZD (Рис.3). В журнал скрипт вывел такую информацию:
2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) ---== Hedge properties==--- 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Symbol: AUDNZD 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Positions total = 5 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) 1) #119214062 buy 1.75 AUDNZD 1.08781000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) 2) #119214068 buy 2.55 AUDNZD 1.08783000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) 3) #119214071 buy 1.25 AUDNZD 1.08785000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) 4) #119214083 sell 3.00 AUDNZD 1.08773000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) 5) #119214092 sell 4.50 AUDNZD 1.08757000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Magic: 0 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Time: 2018.08.31 16:39:41 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Time in msc: 1535733581113 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Update time: 2018.08.31 16:40:07 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Update time in msc: 1535733607241 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Type: HEDGE_NETTING_SELL 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Type description: hedge netting sell 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Volume: -1.95 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Buy volume: 5.55 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Sell volume: 7.50 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Open price: 1.08708 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Sl-price: -1.00000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Tp-price: -1.00000 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Current price: 1.09314 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Commission: 0.00 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Swap: -21.06 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Profit: -779.45 2018.09.03 18:47:25.369 Test_hedge_properties (EURUSD,H1) Margin: 1136.23
Код скрипта Test_hedge_properties.mq5 можно будет скачать из архива файлов.
3.1 Панель свойств хеджа
Теперь усложним задачу. С помощью Стандартной библиотеки напишем советник HedgePropertiesEA.mq5, который выводит на график панель, отображающую свойства выбранной хеджевой позиции.
Для этих целей создадим класс CHedgeDialog, который будет являться потомком стандартного класса CAppDialog. С помощью последнего избавимся от необходимости программировать типичные задачи, например: минимизация и восстановление окна панели, обработка изменений в элементах панели и т.д.
//+------------------------------------------------------------------+ //| Class CHedgeDialog | //| Purpose: Class for displaying a hedge position info. | //| Derives from class CAppDialog. | //+------------------------------------------------------------------+ class CHedgeDialog : private CAppDialog { //--- === Data members === --- private: CArrayString m_symbols_arr; //--- controls CLabel m_labels[FIELDS_NUM+1]; CEdit m_edits[FIELDS_NUM]; CComboBox m_combo; bool m_to_refresh; //--- === Methods === --- public: //--- constructor/destructor void CHedgeDialog(void) {}; void ~CHedgeDialog(void) {}; //--- initialization bool Init(void); void Deinit(const int _reason); //--- processing void OnChartEvent(const int _id, const long &_lparam, const double &_dparam, const string &_sparam); void OnTradeEvent(void); //--- private: int HedgeSymbols(void); void RefreshPanel(void); }; //+------------------------------------------------------------------+
Экземпляр класса в коде советника будет вызываться и обрабатывать события инициализации, деинициализации, события графика и события, связанные с торговыми транзакциями.
Панель свойств хеджевой позиции представлена на Рис.4.
Рис.4 Панель свойств хеджевой позиции
Одним из основных методов является CHedgeDialog::RefreshPanel(). Это, так сказать, "рабочая лошадка". При необходимости он обновляет информационные поля панели. Некоторую сложность при кодировании и тестировании вызвала ситуация, когда изменялось число хеджей. В таком случае нужно изменить уникальные символы в выпадающем списке и не попасть в бесконечный цикл вызовов обработчика OnChartEvent(). Для этого был использован предел для последовательных вызовов обработчика протяжённостью в 1 сек.
//--- check the limit for refreshing if(!m_to_refresh) { uint last_cnt=GetTickCount(); static uint prev_cnt=0; uint msc_elapsed=last_cnt-prev_cnt; prev_cnt=last_cnt; if(msc_elapsed>1000) m_to_refresh=true; else return; }
Полный код советника HedgePropertiesEA.mq5 представлен в архиве файлов.
Заключение
Торговый терминал MetaTrader 5 на данном этапе является не только мультирыночным, но и позволяет применять различные системы учёта позиций. Такие возможности существенно расширяют инструментарий для реализации и формализации торговых идей.
Надеюсь, что данная статья вызовет интерес у тех, кто желает заняться переводом своих стратегий из MetaTrader 4 в MetaTrader 5.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
...Нет, судил по коду. Откройте позицию, а затем закройте ее часть. По Вашему коду комиссия текущей позиции не изменится.
Более того, удвоение - это благое намерение, которое создает проблему. Дело в том, что Вам нужно, чтобы После закрытия всех позиций Balance стал равен Equity + PositionCommission. Но тогда никакого удвоения быть не должно, т.к. MT5 на открытии позиций уже в балансе отразил комиссию открытия.
Да, не изменится. Цель была показать максимальную величину комиссии, которую платим в общем за текущий хедж. И, причём, часть уже заплатили, а вторую заплатим позже. Но есть нюансы.
К примеру, есть ещё такой тип сделки как DEAL_ENTRY_OUT_BY. Там вообще нет комиссии, по крайней мере у моего брокера. Ещё комиссия может меняться (вход и выход могут неодинаково стоить), если валюта счёта не совпадает с базовой валютой. Так что мой пример далеко не идеален...
А почему Вы решили, что мне это нужно? Я просто показал один из способов расчёта таких накладных расходов как комиссия.
Ну и при подсчете комиссии очень дорого проводить нормализацию на каждом шаге цикла. Важно при Оптимизации.
Ну разве что для оптимизации. Ну так никто же не мешает внести свои изменения и учесть их в коде.
Да, не изменится. Цель была показать максимальную величину комиссии, которую платим в общем за текущий хедж. И, причём, часть уже заплатили, а вторую заплатим позже. Но есть нюансы.
К примеру, есть ещё такой тип сделки как DEAL_ENTRY_OUT_BY. Там вообще нет комиссии, по крайней мере у моего брокера.
CloseBy не облагается комиссией по определению.
Ещё комиссия может меняться (вход и выход могут неодинаково стоить), если валюта счёта не совпадает с базовой валютой. Так что мой пример далеко не идеален...
А почему Вы решили, что мне это нужно? Я просто показал один из способов расчёта таких накладных расходов как комиссия.
Потому что на языке того же MT4 Equity = Balance + Profit + Swap + Commission. Это самая распространенная логика. Но у Вас, конечно, может быть своя.
Ну разве что для оптимизации. Ну так никто же не мешает внести свои изменения и учесть их в коде.
Разработчики распинаются, чтобы сделать Оптимизатор быстрее. А в Статьях допускается медленный код. Выглядит странно.
Потому что на языке того же MT4 Equity = Balance + Profit + Swap + Commission. Это самая распространенная логика. Но у Вас, конечно, может быть своя.
Не поверите, я так же считаю относительно эквити :-))
Сделаю выбор для метода комиссии, как считать. Внесу изменения в код...
...Разработчики распинаются, чтобы сделать Оптимизатор быстрее. А в Статьях допускается медленный код. Выглядит странно.
Как бы это сказать языком дипломатов... Вы всё ещё кипятите? - Я давно тестирую всё в облаке и не парюсь, что где-то есть лишние нормировки и нет многострочных макросов...
давно тестирую всё в облаке и не парюсь, что где-то есть лишние нормировки и нет многострочных макросов...
Даже простейшую алгоритмическую оптимизацию заменять мощью железа - это, видимо, давно сформировавшийся тренд. Так не могу.
Даже простейшую алгоритмическую оптимизацию заменять мощью железа - это, видимо, давно сформировавшийся тренд. Так не могу.
Поколение гигагерц и гигабайт.