English 中文 Español Deutsch 日本語 Português
Универсальный торговый эксперт: Доступ к свойствам инструмента (часть 8)

Универсальный торговый эксперт: Доступ к свойствам инструмента (часть 8)

MetaTrader 5Примеры | 31 мая 2017, 15:44
4 770 1
Vasiliy Sokolov
Vasiliy Sokolov

Введение

Прошло некоторое время с выхода последней части статьи, посвященной торговому движку CStrategy. Это время понадобилось для осмысления того пути развития, который прошел CStrategy от небольшой вспомогательной торговой библиотеки до полнофункционального торгового комплекса, включающего в себя большинство часто используемых инструментов для построения полноценной торговой стратегии. По прошествии этого времени появилось новое понимание того, в какую сторону необходимо развивать CStrategy дальше. Практическое применение CStrategy в течение этого времени выявило также несколько недостатков, свойственных последним версиям движка. Исправление этих недостатков вылилось в написание новых статей серии "Универсальный торговый эксперт". В данной, восьмой части статьи, будет освещена работа с торговыми инструментами через объектно-ориентированный класс CStrategy. 


Обзор работы с рабочим инструментом в предыдущих версиях CStrategy

Торговое окружение эксперта объемно и многообразно. Это разнообразная информация о счете, данные котировок, функции по работе с временем и, конечно же, информация о торговых инструментах, доступная в терминале. Большинство этой информации сосредоточено в функциях по работе с торговыми инструментами: получение текущих котировок и работа с свойствами рабочего инструмента. Как правило, все торговые эксперты очень активно работают с информацией о цене. Они рассчитывают на основе последних данных некий ценовой паттерн или торговый сигнал и заключают сделку на его основе. Для правильного формирования торгового приказа они также используют информацию о свойствах текущего рабочего символа: например, это минимальный объем заключаемой сделки или уровень freeze level — диапазон от текущей цены, внутри которого запрещена установка отложенных ордеров.

Ясно, что данная информация должна быть легкодоступной и быть всегда "под рукой". Однако так ли это было в прошлых редакциях CStrategy? Давайте разберемся с этим, сделав небольшой экскурс в историю: опишем, как происходила работа с торговым символом раньше. В третьей части статьи была предложена система доступа к котировкам через привычный индексатор []. В класс CStrategy включались несколько вспомогательных классов COpen, CHigh, CLow, CClose, CVolume, CTime, каждый из которых возвращал соответствующее значение по запрашиваемому индексу. Это позволяло в коде эксперта достаточно удобно получать информацию о текущем символе. Например, чтобы узнать цену закрытия текущего бара, достаточно было написать:

...
double close = Close[0];
...

Однако доступа к ценам в формате OHLC оказалось недостаточно, и класс CStrategy пришлось снабдить дополнительными методами Ask(), Bid(), Last(). Вскоре и этих методов было мало. Потребовалось добавить еще несколько методов вроде FreezeLevel(), получающих основную информацию о текущем инструменте. Объем базового класса CStrategy стал расти большими темпами. Обилие доступных методов внутри CStrategy начало приводить к путанице. Но основные трудности начались, когда с помощью CStrategy была сделана попытка создать эксперт, торгующий на нескольких инструментах одновременно. Формально CStrategy — мультиинструментальный движок. Это значит, что с его помощью можно создать как несколько экспертов, торгующих на разных инструментах независимо друг от друга, так и одного эксперта, торгующего на двух и более инструментах. Однако последнее оказалось выполнить не так просто, т.к. в этом случае потребовалось на лету переконфигурировать классы таймсерий, выставляя тот или иной рабочий символ поочередно:

string symbol1 = "EURUSD";
string symbol2 = "GBPUSD";
Close.Symbol(symbol1);
double close_eurusd = Close[0];
Close.Symbol(symbol2);
double close_gbpusd = Close[0];
...

Все эти трудности привели к пониманию того, что информация об рабочем инструменте слишком объемна и разнообразна, чтобы ее мог реализовать непосредственно CStrategy. Сам класс CStrategy уже выполняет трудоемкую и сложную работу по организации последовательности торговых действий, и наделение его дополнительным функционалом приводило к снижению степени управляемости кодом. Вместо этого решено было выделить методы для работы с инструментом в отдельный класс CSymbol.


Первое знакомство с объектом WS и классом CSymbol

Теперь вместо разрозненных методов вроде Ask(), Bid(), Last и специальных классов вроде CHigh и CLow, торговой стратегии на базе CStrategy доступен специальный объект WS, созданный на основе класса CSymbol. Этот класс, принадлежащий набору библиотек CStrategy, имеет ряд методов, делающих его схожим со стандартным классом СSymbolInfo. Однако это другой класс, работающий не только со свойствами инструмента, но и позволяющий получать его котировки, включая информацию о лимитных заявках (стакан цен). Название объекта WS — аббревиатура от "Work Symbol", и он доступен непосредственно в коде стратегии. Такое короткое название выбрано не случайно. В процессе работы требуется очень часто обращаться к тому или иному свойству инструмента, поэтому двухсимвольная аббревиатура позволяет коду оставаться компактным и выразительным. 

Как уже было сказано в предыдущих частях статьи, прежде чем передать управление эксперту, торговый движок CStrategy проводит ряд инициализаций объектов внутреннего окружения. Запоминается имя рабочего инструмента и таймфрейм, создаются классы отслеживающие поступление новых событий (по умолчанию это новый тик и новый бар). Устанавливается логирование, выставляются флаги режимов работы. В этот же момент инициализируется объект WS класса CSymbol. Его внутреннее устройство достаточно просто. Он содержит два внутренних поля: символ инструмента и его таймфрейм, а также специальные объекты для доступа к котировкам инструмента. Сам объект WS инициируется методом InitSeries. Зная рабочий символ и таймфрейм эксперта, инициировать его не составляет труда:

CStrategy::CStrategy(void)
{
   WS.InitSeries(ExpertSymbol(), Timeframe());
}

После того, как объект инициализирован, с его помощью можно получить то или иное свойство инструмента. Например, чтобы получить максимальную цену текущего бара, достаточно написать следующее:

double max = WS.High[0];

Объект WS обладает дополнительным рядом свойств, делающим его использование в непосредственных расчетах удобным и самодостаточным средством. Разберем распространенный случай, когда требуется поставить BuyStop ордер чуть выше цены максимума предыдущего бара. Допустим, если мы торгуем на EURUSD и хотим, чтобы наш стоп-ордер был выставлен на расстоянии трех пятизначных пунктов от максимума предыдущего бара, нам нужно написать следующий код:

void CMovingAverage::InitBuy(const MarketEvent &event)
{
   ...
   Trade.BuyStop(1.0, WS.High[1] + WS.StepToPrice(3), WS.Name());
   ...
}

Так всего в одной строчке кода мы уместили большой объем действий:

  • Получили экстремум предыдущего бара (WS.High[1]);
  • Значение одного пункта умножили на три, получив тем самым ценовой зазор в три пункта (WS.StepToPrice(3));
  • Прибавили к цене экстремума полученный ценовой зазор (WS.High[1] + WS.StepToPrice(3));
  • Отправили торговый приказ BuyStop, уровнем срабатывания которого стала получившаяся цена, указав в качестве символа имя текущего инструмента (WS.Name()).

Метод StepToPrice может показаться весьма отличным от системы наименований, принятой в MetaTrader. В других торговых платформах под прайсстепом (price step) принято понимать минимальное изменение цены. Его эквивалент в MetaTrader называется SYMBOL_TRADE_TICK_SIZE. Это название можно легко спутать с размером или стоимостью одного тика SYMBOL_TRADE_TICK_VALUE, поэтому в CSymbol используется другое название для этого параметра. Однако большинство других названий методов CSymbol совпадает с системными модификаторами и методами MQL5, но, как показывает пример с StepToPrice, они не всегда идентичны. Ведь главная цель CSymbol — дать простой и интуитивно понятный набор методов для получения полной информации о торговом инструменте.


Структура класса CSymbol. Сравнительная таблица методов

Торговый инструмент в MetaTrader обладает большим набором свойств. Прежде всего, все свойства можно условно разделить на целочисленные, вещественные и строковые. К целочисленным свойствам относятся логические свойства (bool), системные модификаторы в виде перечислений (enum), дата и время (datetime) и собственно целочисленные свойства (int и long). К вещественным свойствам относятся разнообразные дробные значения (double). Наконец, к строковым свойствам (string) относятся свойства, возвращающие строки: название инструмента, строковое описание символа и т.п. Наконец, для инструмента доступен ряд свойств, специфичных для определенного рыночного сегмента. Так, для рынка ФОРТС доступны дополнительные свойства текущей торговой сессии. Для рынков опционов доступны свои уникальные свойства. Класс CSymbol определяет свойства текущей торговой сессии ФОРТС в дополнительном внутреннем классе SessionInfo. Остальные же свойства не разделяются по типам и присутствуют "как есть" в виде одноименных методов.

Более того, в классе CSymbol содержится дополнительные коллекции, делающие возможным обращение к котировкам инструмента. Так, для доступа к сериям OHLCV публично определены классы COpen, CHigh, CLow, CClose, CVolume, а для доступа к стакану цен используется специальный класс CMarketWatch, подробное описание которого доступно в отдельной статье: "Рецепты MQL5 - пишем свой стакан цен". Помимо одноименных методов и классов-индексаторов, наподобие CClose, класс CSymbol содержит несколько методов, не имеющих очевидных аналогов в стандартных функциях MQL5 или классе SymbolInfo. Опишем их более подробно.

Available — данный метод возвращает true, если инструмент с заданным названием существует в терминале. Если такого инструмента нет, возвращается false.

IndexByTime — возвращает индекс бара, соответствующий указанному времени. Например, переменной index в следующем коде будет присвоено значение 1:

int index = WS.IndexByTime(WS.Time[1]);
// index = 1;

Этот метод удобно использовать, если нам известно время и мы хотим получить номер бара, соответствующий этому времени. Предположим, что торговый эксперт должен закрыть позицию после того, как он удерживал ее в течении BarsHold баров. Тогда код, делающий это, мог бы выглядеть так:

//+------------------------------------------------------------------+
//| Сопровождение длинной позиции по скользящей средней Moving       |
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   int bar_open = WS.IndexByTime(pos.TimeOpen());
   if(bar_open >= BarsHold)
      pos.CloseAtMarket("Exit by time hold");
}

StepToPrice — значение минимального изменения цены, выраженное в пунктах инструмента. Подробнее о предназначении этого метода было написано выше.

Полный перечень методов CSymbol представлен в сводной таблице ниже. В поле "Описание" дано краткое описание метода. В большинстве случаев оно совпадает с официальным описанием аналогичного свойства инструмента в соответствующем разделе документации, однако для некоторых методов дано более удачное, на мой взгляд, описание их возможностей.

Поле "Тип" возвращаемого значения содержит, соответственно тип, который возвращает тот или иной метод или коллекция.

Поле "Функция или системный идентификатор MQL5" содержит название системного идентификатора или функции MQL5, выполняющей аналогичные функции. Если указывается системная функция, то в конце ее названия ставятся круглые скобки, например, CopyOpen() или MarketBookGet(). Под системным модификатором имеется в виду один из трех модификаторов, который необходимо указать при вызове функций SymbolInfoInteger, SymbolInfoDouble или SymbolInfoString. Данный модификатор должен принадлежать одному из трех системных соответствующих перечислений: ENUM_SYMBOL_INFO_INTEGER,  ENUM_SYMBOL_INFO_DOUBLE или ENUM_SYMBOL_INFO_STRING. Например, если в колонке "Функция или системный идентификатор MQL5" указан модификатор SYMBOL_TRADE_STOPS_LEVEL, это означает, что для получения данного свойства необходимо вызвать функцию SymbolInfoInteger:

int stop_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);

Колонка "Название метода CSymbol" содержит собственно название метода, отвечающего за возврат того или иного свойства. Например, чтобы получить день недели начисления тройного свопа, необходимо вызывать метод:

ENUM_DAY_OF_WEEK day = WS.DayOfSwap3x();

Итак, таблица методов:

Описание Тип возвращаемого значения Функция или системный идентификатор MQL5 Название метода CSymbol
 ДОСТУП К ИСТОРИЧЕСКИМ КОТИРОВКАМ ИНСТРУМЕНТА      
   Получение цены открытия по заданному индексу бара, с предустановленным таймфреймом инструмента  double  CopyOpen()  Open[]
   Получение цены максимума по заданному индексу бара, с предустановленным таймфреймом инструмента  double  CopyHigh()  High[]
   Получение цены минимума по заданному индексу бара, с предустановленным таймфреймом инструмента  double  CopyLow()  Low[]
   Получение цены закрытия по заданному индексу бара, с предустановленным таймфреймом инструмента  double  CopyClose()  Close[]
   Получение объема, соответствующего бару с заданным индексом  double  CopyVolume()  Volume[]
   Получение свойств стакана цен данного инструмента, доступ к котировкам второго уровня  MqlBookInfo  MarketBookGet()  MarketBook
 ЦЕЛОЧИСЛЕННЫЕ СВОЙСТВА ИНСТРУМЕНТА      
   Признак того, что данный инструмент присутствует в терминале  bool  Нет аналогов  Available
   Количество баров по данному символу и таймфрейму  int  Bars()   BarsTotal
   Таймфрейм инструмента  ENUM_TIMEFRAMES  Period()  Period
   Признак того, что символ выбран в Market Watch  bool  SYMBOL_SELECT  SelectInMarketWatch
   Признак плавающего спреда  bool  SYMBOL_SPREAD_FLOAT  SpreadFloat
   Размер спреда в пунктах  int  SYMBOL_SPREAD  Spread
   Минимальный отступ в пунктах от текущей цены закрытия для установки Stop ордеров  int  SYMBOL_TRADE_STOPS_LEVEL  StopLevel
   Диапазон заморозки торговых операций (в пунктах)  int  SYMBOL_TRADE_FREEZE_LEVEL  FreezeLevel
   Флаги разрешенных режимов истечения ордера  int  SYMBOL_EXPIRATION_MODE  FlagsExpirationOrders
   Флаги разрешенных режимов исполнения ордера  int  SYMBOL_FILLING_MODE  FlagsExecutionOrders
   Флаги разрешенных типов ордеров  int  SYMBOL_ORDER_MODE  FlagsAllowedOrders
   Возвращает индекс бара, время открытия которого соответствует переданному аргументу  int  Нет аналогов  IndexByTime
   Способ вычисления стоимости контракта  ENUM_SYMBOL_CALC_MODE  SYMBOL_TRADE_CALC_MODE  CalcContractType
   Тип исполнения ордеров  ENUM_SYMBOL_TRADE_MODE  SYMBOL_TRADE_MODE  ExecuteOrderType
   Режим заключения сделок  ENUM_SYMBOL_TRADE_EXECUTION  SYMBOL_TRADE_EXEMODE  ExecuteDealsType
   Модель расчета свопа  ENUM_SYMBOL_SWAP_MODE  SYMBOL_SWAP_MODE  CalcSwapMode
   День недели для начисления тройного свопа  ENUM_DAY_OF_WEEK  SYMBOL_SWAP_ROLLOVER3DAYS  DayOfSwap3x
   Тип опциона  ENUM_SYMBOL_OPTION_MODE  SYMBOL_OPTION_MODE  OptionType
   Право опциона (Call/Put)  ENUM_SYMBOL_OPTION_RIGHT  SYMBOL_OPTION_RIGHT  OptionRight
   Время последней котировки  datetime  SYMBOL_TIME  TimeOfLastQuote
   Дата начала торгов по инструменту (обычно используется для фьючерсов)  datetime  SYMBOL_START_TIME  StartDate
   Дата окончания торгов по инструменту (обычно используется для фьючерсов)  datetime  SYMBOL_EXPIRATION_TIME  ExpirationDate
СВОЙСТВА ТЕКУЩЕЙ ТОРГОВОЙ СЕССИИ ФЬЮЧЕРСНЫХ ИНСТРУМЕНТОВ MOEX

 
   Количество сделок в текущей сессии  long  SYMBOL_SESSION_DEALS  SymbolInfo.DealsTotal
   Общее число ордеров на покупку в текущий момент  long  SYMBOL_SESSION_BUY_ORDERS  SymbolInfo.BuyOrdersTotal
   Общее число ордеров на продажу в текущий момент  long  SYMBOL_SESSION_SELL_ORDERS  SymbolInfo.SellOrdersTotal
   Максимальный объем за текущую торговую сессию  long  SYMBOL_VOLUMEHIGH  SymbolInfo.HighVolume
   Минимальный объем за текущую торговую сессию  long  SYMBOL_VOLUMELOW  SymbolInfo.LowVolume
   Максимальный Bid за день  double  SYMBOL_BIDHIGH  SymbolInfo.BidHigh
   Максимальный Ask за день  double  SYMBOL_ASKHIGH  SymbolInfo.AskHigh
   Минимальный Bid за день  double  SYMBOL_BIDLOW  SymbolInfo.BidLow
   Минимальный Ask за день  double  SYMBOL_ASKLOW  SymbolInfo.AskLow
   Максимальный Last за день  double  SYMBOL_LASTHIGH  SymbolInfo.LastHigh
   Минимальный Last за день  double  SYMBOL_LASTLOW  SymbolInfo.LastLow
   Cуммарный объём сделок в текущую сессию  double  SYMBOL_SESSION_VOLUME  SymbolInfo.VolumeTotal
   Cуммарный оборот в текущую сессию  double  SYMBOL_SESSION_TURNOVER  SymbolInfo.TurnoverTotal
   Cуммарный объём открытых позиций  double  SYMBOL_SESSION_INTEREST  SymbolInfo.OpenInterestTotal
   Общий объём ордеров на покупку в текущий момент  double  SYMBOL_SESSION_BUY_ORDERS_VOLUME  SymbolInfo.BuyOrdersVolume
   Общий объём ордеров на продажу в текущий момент  double  SYMBOL_SESSION_SELL_ORDERS_VOLUME  SymbolInfo.SellOrdersVolume
   Цена открытия сессии  double  SYMBOL_SESSION_OPEN  SymbolInfo.PriceSessionOpen
   Цена закрытия сессии  double  SYMBOL_SESSION_CLOSE  SymbolInfo.PriceSessionClose
   Средневзвешенная цена сессии  double  SYMBOL_SESSION_AW  SymbolInfo.PriceSessionAverage
   Цена поставки на текущую сессию  double  SYMBOL_SESSION_PRICE_SETTLEMENT  SymbolInfo.PriceSettlement
   Максимально допустимое значение цены на сессию  double  SYMBOL_SESSION_PRICE_LIMIT_MAX  SymbolInfo.PriceLimitMax
   Минимально допустимое значение цены на сессию  double  SYMBOL_SESSION_PRICE_LIMIT_MIN  SymbolInfo.PriceLimitMin
ВЕЩЕСТВЕННЫЕ СВОЙСТВА ИНСТРУМЕНТА       
   Ask — лучшее предложение на покупку  double  SYMBOL_ASK  Ask
   Bid — лучшее предложение на продажу  double  SYMBOL_BID  Bid
   Цена, по которой совершена последняя сделка  double  SYMBOL_LAST  Last
   Значение минимального изменения цены, умноженное на переданное количество шагов цены  double  Нет аналогов  StepToPrice
   Значение одного пункта (тика)  double  SYMBOL_POINT  PriceStep
   Стоимость одного пункта (тика), выраженная в валюте депозита  double  SYMBOL_TRADE_TICK_VALUE  TickValue
   Цена исполнения опциона  double  SYMBOL_OPTION_STRIKE  OptionStrike
   Размер торгового контракта  double  SYMBOL_TRADE_CONTRACT_SIZE  ContractSize
   Минимальный объем для заключения сделки  double  SYMBOL_VOLUME_MIN  VolumeContractMin
   Максимальный объем для заключения сделки  double  SYMBOL_VOLUME_MAX  VolumeContractMax
   Минимальный шаг изменения объема для заключения сделки  double  SYMBOL_VOLUME_STEP  VolumeContractStep
   Максимально допустимый для данного символа совокупный объем открытой позиции и отложенных ордеров в одном направлении (покупка или продажа)  double  SYMBOL_VOLUME_LIMIT  VolumeContractLimit
   Значение свопа, зачисляемого при удержании длиной позиции объемом в один контракт  double  SYMBOL_SWAP_LONG  SwapLong
   Значение свопа, зачисляемого при удержании короткой позиции объемом в один контракт  double  SYMBOL_SWAP_SHORT  SwapShort
   Маржа, необходимая для открытия позиции размером в один лот  double  SYMBOL_MARGIN_INITIAL  MarginInit
   Маржа, необходимая для поддержания одного лота открытой позиции  double  SYMBOL_MARGIN_MAINTENANCE  MarginMaintenance
   Маржа, необходимая для удержания одного лота перекрытой позиции  double  SYMBOL_MARGIN_HEDGED  MarginHedged
 СТРОКОВЫЕ СВОЙСТВА ИНСТРУМЕНТА      
   Имя инструмента  string  Symbol()  Name
   Имя базового актива для производного инструмента  string  SYMBOL_BASIS  NameBasisSymbol
   Базовая валюта инструмента  string  SYMBOL_CURRENCY_BASE  NameBasisCurrency
   Валюта прибыли  string  SYMBOL_CURRENCY_PROFIT  NameCurrencyProfit
   Валюта, в которой вычисляются залоговые средства  string  SYMBOL_CURRENCY_MARGIN  NameCurrencyMargin
   Источник текущей котировки  string  SYMBOL_BANK  NameBank
   Строковое описание символа  string  SYMBOL_DESCRIPTION  Description
   Имя торгового символа в системе международных идентификационных кодов ценных бумаг — ISIN  string  SYMBOL_ISIN  NameISIN
   Путь в дереве символов  string  SYMBOL_PATH  SymbolPath


Использование нескольких инструментов одновременно

Так как CSymbol является обычным классом, то можно создать неограниченное количество объектов этого класса внутри своего эксперта. Объект WS — лишь только один из этих объектов, созданный заранее самим движком CStrategy и указывающий на текущий рабочий символ и таймфрейм эксперта. Самому эксперту ничто не мешает создать дополнительный символ, предоставляющий доступ к любому другому инструменту. Предположим, что наш эксперт торгует на срочной секции Московской биржи и одновременно отслеживает два инструмента — Si и Brent. Тогда в его коде можно разместить два объекта CSymbol, назвав их соответственно Si и Brent:

//+------------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2017, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/ru/users/c-4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#include <Strategy\Strategy.mqh>

//+------------------------------------------------------------------+
//| Шаблон стратегии, работающий с двумя инструментами одновременно  |
//+------------------------------------------------------------------+
class CIntRate : public CStrategy
  {
   CSymbol           Si;         // Рубль-доллар
   CSymbol           Brent;      // Нефть
public:
   virtual void      OnEvent(const MarketEvent& event);
   virtual bool      OnInit();
  };
//+------------------------------------------------------------------+
//| Инициализирует инструменты нефть и рубль                         |
//+------------------------------------------------------------------+
bool CIntRate::OnInit(void)
  {
   Si.InitSeries("Si Splice", Timeframe());
   Brent.InitSeries("BR Splice", Timeframe());
   return true;
  }

//+------------------------------------------------------------------+
//| Стоимость нефти, выраженная в рублях                             |
//+------------------------------------------------------------------+
void CIntRate::OnEvent(const MarketEvent &event)
  {
   double brent_in_rub = Brent.Last()*Si.Last()/Si.ContractSize();
  }

//+------------------------------------------------------------------+

Данный код эксперта получает последние цены текущих фьючерсов нефти и рубля и вычисляет известную формулу стоимости нефти, выраженную в рублях. Так как один контракт фьючерса Si равен 1000$, то приходится дополнительно делить результат на размер одного контракта. Однако благодаря тому, что все свойства инструмента собраны в едином классе CSymbol, это достаточно простая операция. Остальной код тоже прост и выразителен. Главное — не забыть проинициализировать объекты Si и Brent в момент запуска эксперта, в методе OnInit.


Построение профиля процентной ставки с помощью CSymbol

Последний пример использования CSymbol, который мы разберем, будет чуть более сложным, но и более интересным. Известно, что фьючерсные контракты торгуются с некоторым контанго по отношению к базовому активу. Это связано с тем, что цена на товар в будущем обычно дороже, чем текущая цена. По сути, такая разница определяет рыночную процентную ставку на тот или иной товар или актив. Расмотрим пример с фьючерсом рубль/доллар. Его текущая цена на момент написания этих строк составляла 56.2875 рублей за 1 доллар, а цена ближайшего фьючерсного контракта по Si-6.17 была 56682 рублей за 1000$ или 56,682 рублей за 1 доллар. Т.е. разница между ценой сейчас и ценой через 30 дней (на 16.05.2017 Si-6.17 истекает ровно через 30 дней) составляет 0,395 рубля, или 39,5 копеек. Т.е. за 1 месяц, по мнению рынка, рубль обесценится на 39,5 копеек, что составит 0.7% от текущей его стоимости. Нетрудно подсчитать, что за 12 месяцев инфляция, по мнению рынка, составит 8,42%. Но это уровень инфляции, рассчитанный только для ближайшего фьючерса. Если вместо Si-6.17 подставить Si-9.17 то инфляция будет ниже и составит около 7.8% годовых. Таким образом, сравнив все фьючерсы Si с базовой ценой актива, мы сможем получить профиль процентной ставки. Этот профиль будет выглядеть в виде таблицы, показывающей ожидания инвесторов в зависимости от времени. Например мы узнаем процентную ставку на ближайшие 30, 100, 200, 300, 400 и 500 дней.

Чтобы рассчитать все эти значения, нам придется активно использовать различные свойства инструментов, а также манипулировать самим списком этих инструментов. Вот как выглядит задача для расчета профиля процентной ставки по пунктам.

  1. Эксперт загружается на любом фьючерсном инструменте. Он анализирует название инструмента и загружает все фьючерсы, связанные с ним.
  2. Каждый загруженный фьючерс представляет объект CSymbol, который размещается в списке инструментов.
  3. В момент прихода нового тика эксперт перебирает коллекцию инструментов. Для каждого инструмента находится его базовый символ.
  4. Производится расчет разницы между ценой выбранного инструмента и ценой его базового символа. Эта разница конвертируется в проценты, которые затем преобразуются в годовой эквивалент, для чего учитывается оставшееся время обращения фьючерсного контракта.
  5. Получившаяся разница выводится на панель в виде строки таблицы. Каждая строка имеет вид: "Название фьючерса — Количество дней до экспирации — процентная ставка".

Как видно из описания, алгоритм получается в действительности не так прост, как может показаться. Однако благодаря движку CStrategy и объекту CSymbol мы существенно снизим вычислительную сложность для эксперта. Наш код будет выполнен в виде эксперта, однако сам эксперт не будет совершать каких-либо торговых действий. Вместо этого он будет просто выводить значение процентных ставок на панель. Итак, рассмотрим получившийся код:

//+------------------------------------------------------------------+
//|                                                EventListener.mqh |
//|           Copyright 2017, Vasiliy Sokolov, St-Petersburg, Russia |
//|                                https://www.mql5.com/ru/users/c-4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#include <Strategy\Strategy.mqh>
#include <Arrays\ArrayObj.mqh>
#include "Panel.mqh"

//+------------------------------------------------------------------+
//| Профиль процентной ставки                                        |
//+------------------------------------------------------------------+
class CIntRate : public CStrategy
  {
   CArrayObj         Symbols;    // Список символов
   CPercentPanel     Panel;      // Панель отрисовки процентных ставок
   double            BaseRate(CSymbol* fut);
public:
   virtual void      OnEvent(const MarketEvent& event);
   virtual bool      OnInit();
  };
//+-------------------------------------------------------------------+
//| Добавляет требуемые фьючерсы для расчета профиля процентной ставки|
//+-------------------------------------------------------------------+
bool CIntRate::OnInit(void)
  {
   string basis = WS.NameBasisSymbol();
   for(int i = 0; i < SymbolsTotal(false); i++)
   {
      string name = SymbolName(i, false);
      int index = StringFind(name, basis, 0);
      if(index != 0)
         continue;
      CSymbol* Fut = new CSymbol(name, Timeframe());
      if(Fut.ExpirationDate() == 0 || Fut.ExpirationDate() < TimeCurrent())
      {
         delete Fut;
         continue;
      }
      string text = "Add new symbol " + Fut.Name() + " in symbols list";
      CMessage* msg = new CMessage(MESSAGE_INFO, __FUNCTION__, text);
      Log.AddMessage(msg);
      Symbols.Add(Fut);
   }
   string text = "Total add symbols " + (string)Symbols.Total();
   CMessage* msg = new CMessage(MESSAGE_INFO, __FUNCTION__, text);
   Log.AddMessage(msg);
   if(Symbols.Total() > 0)
   {
      Panel.Show();
   }
   return true;
  }

//+------------------------------------------------------------------+
//| Расчитывает профиль и выводит его в таблицу                      |
//+------------------------------------------------------------------+
void CIntRate::OnEvent(const MarketEvent &event)
  {
   double sec_one_day = 60*60*24;   //86 400
   for(int i = 0; i < Symbols.Total(); i++)
   {
      CSymbol* Fut = Symbols.At(i);
      double brate = BaseRate(Fut);
      double days = (Fut.ExpirationDate()-TimeCurrent())/sec_one_day;
      if(Fut.Last() == 0.0)
         continue;
      double per = (Fut.Last() - brate)/brate*100.0;
      double per_in_year = per/days*365;
      Panel.SetLine(i, Fut.NameBasisSymbol() + " " + DoubleToString(days, 0) + " Days:", DoubleToString(per_in_year, 2)+"%");
   }

  }
//+------------------------------------------------------------------+
//| Возвращает спот-котировку фьючерса                               |
//+------------------------------------------------------------------+
double CIntRate::BaseRate(CSymbol* fut)
{
   string name = fut.NameBasisSymbol();
   if(StringFind(name, "Si", 0) == 0)
      return SymbolInfoDouble("USDRUB_TOD", SYMBOL_LAST)*fut.ContractSize();
   return SymbolInfoDouble(name, SYMBOL_LAST)*fut.ContractSize();
}
//+------------------------------------------------------------------+

Основной функционал заложен в метод OnInit. Он получает имя базового символа через WS.NameBasisSymbol() и через перебор всех инструментов находит все фьючерсы, соответствующие данному базовому символу.  Каждый такой фьючерс преобразуется в объект CSymbol и размещается в списке символов CArrayObj. Однако предварительно делается проверка, является ли данный фьючерс действующим, и если его время экспирации находится в будущем, значит, это именно тот фьючерс, который нам нужен.

В методе OnEvent делается собственно расчет процентной ставки для каждого фьючерса, размещенного в коллекции  Symbols. Рассчитывается количество дней до экспирации, дельта между фьючерсом и ценой спота. Разница цен преобразуется в процент, а уже процент нормируется в соответствии с годовой доходностью. Получившееся значение заносится в таблицу Panel (метод SetLine).

Сама таблица достаточно просто устроена и базируется на том же комплекте графических классов, что и панель CStrategy, появляющаяся вместе с экспертом. Код графической компоненты эксперта представлен ниже:

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                 Copyright 2017, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Panel\ElChart.mqh>

class CPercentPanel : public CElChart
{
private:
   CArrayObj  m_fields;
   CArrayObj  m_values;
public:
   
   CPercentPanel(void);
   void SetLine(int index, string field, string value);
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPercentPanel::CPercentPanel(void) : CElChart(OBJ_RECTANGLE_LABEL)
{
   Width(200);
   Height(200);
}
//+------------------------------------------------------------------+
//| Устанавливает линию                                              |
//+------------------------------------------------------------------+
void CPercentPanel::SetLine(int index,string field,string value)
{
   if(m_fields.Total() <= index)
   {
      CElChart* sfield = new CElChart(OBJ_LABEL);
      sfield.XCoord(XCoord()+10);
      sfield.YCoord(YCoord()+21*index+10);
      sfield.Text(field);
      m_fields.Add(sfield);
      m_elements.Add(sfield);
      
      CElChart* svalue = new CElChart(OBJ_LABEL);
      svalue.YCoord(YCoord()+21*index+10);
      svalue.XCoord(XCoord()+132);
      svalue.Text(value);
      svalue.TextColor(clrGreen);
      m_values.Add(svalue);
      m_elements.Add(svalue);
      if(IsShowed())
      {
         sfield.Show();
         svalue.Show();
      }
      Height(m_fields.Total()*20 + m_fields.Total()*2 + 10);
   }
   else
   {
      CElChart* el = m_fields.At(index);
      el.Text(field);
      el = m_values.At(index);
      el.Text(value);
   }
   ChartRedraw();
}

После того, как данный эксперт будет скомпилирован и запущен на одном из графиков фьючерсного контракта Si, должна появиться такая таблица:

Профиль процентной ставки Рубль/Доллар в виде таблицы

Как видно из таблицы, процентные ставки практически на всех временных горизонтах равны и составляют чуть более 7% годовых. Чуть большую процентную ставку показывает ближайший фьючерс. 

Важное замечание: прежде чем запускать эксперта на графике, убедитесь, что котировки по всем требуемым фьючерсам доступны и были ранее загружены. В противном случае результат может быть неопределенным.


Заключение

Мы рассмотрели новый класс CSymbol в составе классов CStrategy. Благодаря этому классу, стало легче работать с торговыми инструментами, получая то или иное их свойство. Благодаря CSymbol мы построили достаточно интересный и нетривиальный индикатор профиля процентной ставки.  Этот пример очень показательный. Множество свойств инструментов были легко получены из объектов CSymbol, а сам расчет оказался не столь сложным. Эксперт одновременно манипулировал шестью инструментами, однако код от этого не стал больше. Благодаря тому, что CStrategy наследуется от CObject, экземпляры этого класса легко размещать в стандартных коллекциях, делая обработку данных на его основе масштабируемой и универсальной. Кроме того, CStrategy делегировал CSymbol несвойственный ему функционал, благодаря чему сам класс CStrategy стал более легковесным и управляемым.


Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
igorbel
igorbel | 15 июн. 2017 в 22:20

Почему у вас в торговых методах открытия позиции нет SL/TP? Имею в виду публичные методы TradeControl Buy/Sell/BuyLimit...

Паттерны, доступные при торговле корзинами валют. Часть III Паттерны, доступные при торговле корзинами валют. Часть III
Это заключительная статья, посвященная паттернам, возникающим при торговле корзинами валютных пар. Рассмотрены трендовые объединенные индикаторы и применение обычных графических построений.
Методы сортировки и их визуализация с помощью MQL5 Методы сортировки и их визуализация с помощью MQL5
Для работы с графикой в MQL5 создана специальная библиотека Graphic.mqh. В статье описан пример ее практического применения и поясняется сама суть сортировок. По каждой сортировке существует как минимум отдельная статья, а по ряду из них уже опубликованы целые исследования, поэтому здесь описывается лишь общая идея.
Кроссплатфоменный торговый советник: Менеджер ордеров Кроссплатфоменный торговый советник: Менеджер ордеров
В статье обсуждается создание менеджера ордеров для кроссплатформенного торгового советника. Менеджер ордеров отвечает за открытие и закрытие экспертом ордеров или позиций, а также за ведение независимой записи о них, и будет доступен для обеих версий терминала.
Пример индикатора, строящего линии поддержки и сопротивления Пример индикатора, строящего линии поддержки и сопротивления
В статье приводится пример реализации индикатора для построения линий поддержки и сопротивления на основе формализованных условий. Вы сможете не только применить индикатор, но и поймете, насколько просто это реализовать. Теперь вы сможете сами сформулировать те условия для построения линий, которые посчитаете нужными, немного изменив код индикатора под свои нужды.