English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Как использовать торговые классы Стандартной библиотеки при написании советника

Как использовать торговые классы Стандартной библиотеки при написании советника

MetaTrader 5Торговые системы | 10 ноября 2010, 10:32
29 536 26
Samuel Olowoyo
Samuel Olowoyo

Введение

Новый язык MQL5 поставляется с множеством классов, цель которых - упросить трейдерам и программистам разработку советников, индикаторов и скриптов на MQL5. Классы Стандартной библиотеки находятся в каталоге \MQL5\Include, расположенном в папке клиентского терминала.

Библиотеки классов разделены на несколько категорий:

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

Если вы уже знакомы с классами и тем, как их можно использовать, добро пожаловать в мир возможностей, предлагаемых новым языком MQL5. Если вы совсем новичок в MQL5, рекомендую для начала прочитать статьи Пошаговое руководство по написанию MQL5-советников для начинающих, Написание советника в MQL5 с использованием объектно-ориентированного подхода или любые другие вводные статьи. Есть множество статей, в которых можно получить необходимые знания.


1. Торговые классы

Каталог \MQL5\Include\Trade содержит различные классы, предназначенные для облегчения жизни трейдеров, пишущих советников для себя, и разработчиков, которые не должны снова "изобретать колесо" при разработке  советников.

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

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

1.1 Класс CAccountInfo

Класс CAccountInfo упрощает пользователю доступ ко всем свойствам текущего активного торгового счета в клиентском терминале. Для того чтобы лучше в нем разобраться, посмотрим на основные члены-функции класса, которые, возможно, понадобятся в нашем советнике.

Перед использованием класса мы должны создать объект класса, поэтому для использования класса CAccountInfo, мы должны создать объект этого класса.

Назовем его myaccount:

//--- Объявление объекта класса CAccountInfo
CAccountInfo myaccount;

Помните, что для создания объекта класса нужно объявить имя класса, а затем имя, которое вы хотите дать объекту. Теперь мы можем использовать наш объект myaccount для доступа к открытым членам-функциям класса CAccountInfo.

Метод
Описание
Пример использования
myaccount.Login()

Используется для получения номера текущего активного счета

// возвращает номер счета, например, 7770
long accountno = myaccount.Login();
myaccount.TradeModeDescription()Используется для получения строкового описания режима торговли текущего активного счета терминала
// Возвращает "Demo trading account", 
// "Real trading account" или "Contest trading account"
string  acc_trading_mode = myaccount.TradeModeDescription(); 
myaccount.Leverage()

Используется для получения размера предоставленного кредитного плеча текущего активного счета терминала

// возвращает размер предоставленного кредитного плеча
long acc_leverage = myaccount.Leverage(); 
myaccount.TradeAllowed() 

Используется для проверки возможности торговли. Если торговля запрещена, проведение торговых операций невозможно.

if (myaccount.TradeAllowed())
{
  // торговля разрешена
}
else
{
  // торговля запрещена
}
myaccount.TradeExpert()Используется для проверки возможности автоматизированной торговли при помощи советников.
if (myaccount.TradeExpert())
{
  // торговля советниками разрешена
}
else
{
  // торговля советниками запрещена
}
myaccount.Balance()Используется для получения значения баланса текущего активного счета.
// получить баланс счета в валюте депозита
double acc_balance =  myaccount.Balance(); 
myaccount.Profit() 

Используется для получения значения текущей прибыли активного счета.

// получить прибыль счета в валюте депозита
double acc_profit =  myaccount.Profit(); 
myaccount.FreeMargin() 

Используется для получения величины свободной маржи активного счета.

// получить значение свободной маржи текущего счета
double acc_free_margin =  myaccount.FreeMargin();
myaccount.Currency()  

Используется для получения валюты депозита для активного счета терминала.

string acc_currency = myaccount.Currency();
myaccount.OrderProfitCheck(
const string symbol,
ENUM_ORDER_TYPE
  trade_operation,
double
volume,
double
price_open, double price_close)
Вычисляет размер прибыли для текущего счета на основании переданных параметров. Она предназначена для предварительной оценки результата торговой операции. Значение возвращается в валюте счета.
double op_profit=myaccount.OrderProfitCheck(_Symbol, ORDER_TYPE_BUY,
                                            1.0,1.2950,1.3235);
Print("Размер прибыли для сделки по покупке EURUSD",
      "по 1.2950 и продаже по 1.3235: ",op_profit);
myaccount.MarginCheck(const string symbol, ENUM_ORDER_TYPE trade_operation,
double
  volume,
double price)
Возвращает размер маржи, необходимой для совершения торговой операции. У функции три входных параметра: символ (валютная пара), тип операции, торговый объем и цена. Эта функция очень важна при размещении торговых ордеров.

// цена зависит от типа операции - в нашем случае покупка
double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); 

double margin_req=myaccount.MarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
myaccount.FreeMarginCheck(const string symbol,ENUM_ORDER_TYPE trade_operation,
double volume,
double price)
Делает то же, что и предыдущая, отличие в том, что возвращается величина маржи, которая останется после совершения торговой операции.
double acс_fm=myaccount.FreeMarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
myaccount.MaxLotCheck(const string symbol,ENUM_ORDER_TYPE trade_operation,double price)Возвращает максимально возможный объем торговой операции.
double max_lot=myaccount.MaxLotCheck(_Symbol,ORDER_TYPE_BUY,price);

1.2 Класс СSymbolInfo

Класс CSymbolInfo упрощает доступ к свойствам текущего символа.

Для использования класса необходимо создать экземпляр класса (объект), в данном случае мы назовем его mysymbol.

// объект класса CSymbolInfo
CSymbolInfo mysymbol;

Давайте посмотрим на основные функции данного класса, которые могут нам понадобиться при написании нашего советника.

 Метод ОписаниеПример использования
mysymbol.Name(string name)

Используется для установки наименования символа для объекта класса. Она использует имя символа, заданное как входной параметр.

// Установка наименования символа
// для нашего объекта класса CSymbolInfo
mysymbol.Name(_Symbol);
mysymbol.Refresh() Используется для обновления всех данных по символу. При указании нового наименования символа для класса, она вызывается автоматически.
mysymbol.Refresh();
mysymbol.RefreshRates()Используется для обновления котировок. При установке нового символа вызывается автоматически.
// Получаем последние цены котировок, используя функцию 
// объекта класса CSymbolInfo 
if (!mysymbol.RefreshRates())
{
   // ошибка получения последних котировок
}
mysymbol.IsSynchronized()    

Используется для проверки синхронизации символа в терминале с данными на торговом сервере. Возвращает true, если данные синхронизированы или false, если не синхронизированы.

// проверка синхронизации данных по символу
if (!mysymbol.IsSynchronized())
{
  // ошибка! Данные по символу 
  // не синхронизированы с сервером
}
mysymbol.VolumeHigh() 

Используется для получения максимального дневного объема для установленного символа.

long max_vol = mysymbol.VolumeHigh();
mysymbol.VolumeLow() 

Используется для получения минимального дневного объема для установленного символа.

long min_vol = mysymbol.VolumeLow();
mysymbol.Time() 

Используется для получения времени последней котировки по установленному символу.

datetime qtime = mysymbol.Time();
mysymbol.Spread()Используется для получения величины текущего спреда (в пунктах) по установленному символу.
int spread = mysymbol.Spread();
mysymbol.StopsLevel()Используется для получения минимального уровня (в пунктах) от текущей цены, для которой может быть установлен Stop Loss для установленного символа. Очень полезная функция для использования при реализации при трейлинга позиций (ордеров).
int stp_level = mysymbol.StopsLevel();
mysymbol.FreezeLevel()Используется для получения расстояния (в пунктах) заморозки торговых операций по установленному символу.
int frz_level = mysymbol.FreezeLevel();
mysymbol.Bid()  

Используется для получения текущей цены Bid для установленного символа.

double bid =  mysymbol.Bid();
mysymbol.BidHigh()Используется для получения максимальной цены Bid за день для установленного символа.
double max_bid = mysymbol.BidHigh();
mysymbol.BidLow()Используется для получения минимальной цены Bid за день для установленного символа.
double min_bid = mysymbol.BidLow();
msymbol.Ask()Используется для получения текущей цены Ask для установленного символа.
double ask =  mysymbol.Ask();
mysymbol.AskHigh()Используется для получения максимальной цены Ask за день для установленного символа.
double max_ask = mysymbol.AskHigh();
mysymbol.AskLow()Используется для получения минимальной цены Ask за день для установленного символа.
double min_ask = mysymbol.AskLow();
mysymbol.CurrencyBase()Используется для получения наименования базовой валюты символа.
// для USDJPY или USDCAD возвращает "USD"
string base_currency = mysymbol.CurrencyBase(); 
mysymbol.ContractSize()Используется для получения величины размера торгового контракта для установленного символа.
double cont_size =  mysymbol.ContractSize();
mysymbol.Digits()
Используется для получения количества цифр после запятой для установленного символа.
int s_digits = mysymbol.Digits();
mysymbol.Point() Используется для получения величины пункта для установленного символа.
double s_point =  mysymbol.Point();
mysymbol.LotsMin()Используется для получения минимального объема сделки для установленного символа.
double min_lot =  mysymbol.LotsMin();
mysymbol.LotsMax()Используется для получения максимального объема сделки для установленного символа.
double max_lot =  mysymbol.LotsMax();
mysymbol.LotsStep()Используется для получения минимального шага изменения объема для установленного символа.
double lot_step = mysymbol.LotsStep();
mysymbol.NormalizePrice(double price) Используется для получения нормализованной цены для установленного символа.
// A normalized current Ask price
double n_price = mysymbol.NormalizePrice(mysymbol.Ask()); 
mysymbol.Select()

Используется для получения флага наличия символа в "Обзоре Рынка".


if (mysymbol.Select())
{
  // Символ успешно выбран
}
else
{
  // Символ не может быть выбран
}
mysymbol.Select(bool select)Используется для добавления или удаления символа из "Обзора Рынка". Следует отметить, что функция вернет false при попытке удаления символа, по которому открыт график или позиция.
if (!mysymbol.Select())
{
  // символ не выбран, выбираем
   mysymbol.Select(true);
}
else
{
 // Символ уже выбран, 
 // удаляем его из "Обзора рынка"
    mysymbol.Select(false);
}
mysymbol.MarginInitial() Используется для получения маржи, необходимой для открытия одного лота по данному символу.
double init_margin = mysymbol.MarginInitial(); 
mysymbol.TradeMode()Используется для получения текущего типа исполнения ордеров, разрешенного для данного символа.
if (mysymbol.TradeMode() == SYMBOL_TRADE_MODE_FULL)
{
 // Нет ограничений на торговые операции по символу
}
mysymbol.TradeModeDescription()Используется для получения описания текущего типа исполнения ордеров, разрешенного для данного символа.
Print("Текущий режим торговли по символу: ",
       mysymbol.TradeModeDescription());


1.3 Класс CHistoryOrderInfo

Класс CHistoryOrderInfo - является классом для упрощенного доступа к свойствам ордера из истории.

Создав объект класса, мы можем использовать его функции для решения своих задач. Назовем объект данного класса myhistory.

// Объект класса CHistoryOrderInfo
CHistoryOrderInfo myhistory;

Рассмотрим некоторые из основных функций этого класса.

При использовании этого класса для получения свойств ордеров в истории сначала нам нужно получить общее количество ордеров в истории, а затем передать тикет ордера объекту нашего класса myhistory.

//--- Получить все исторические ордера за указанный временной период
if (HistorySelect(0,TimeCurrent()))  // получаем все исторические ордера
{
//--- получаем общее количество ордеров в истории
int tot_hist_orders = HistoryOrdersTotal(); 

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

ulong h_ticket; // Тикет ордера

for (int j=0; j<tot_hist_orders; j++)
{
  h_ticket = HistoryOrderGetTicket(j);
  if (h_ticket>0)
  {
    // Первое, что мы делаем - получаем тикет ордера для работы
Метод
 ОписаниеПример использования
myhistory.Ticket(ulong ticket)Используется для выбора ордера для дальнейшей работы.
myhistory.Ticket(h_ticket);
myhistory.Ticket()
Получает тикет ордера.
ulong o_ticket = myhistory.Ticket();
myhistory.TimeSetup()Используется для получения время установки ордера.
datetime os_time =  myhistory.TimeSetup();
myhistory.OrderType()Используется для получения типа ордера (ORDER_TYPE_BUY, и т.д.).
if (myhistory.OrderType() == ORDER_TYPE_BUY)
{
// это ордер на покупку
}
myhistory.State()

Используется для получения текущего состояния ордера (отменен, принят, отклонен, размещен и т.п.).

if(myhistory.State() == ORDER_STATE_REJECTED)
{
// ордер отклонен, он не был размещен
}
myhistory.TimeDone()             
Используется для получения времени исполнения или снятия ордера.
datetime ot_done =  myhistory.TimeDone();
myhistory.Magic()Используется для получения идентификатора эксперта, выставившего ордер.
long o_magic = myhistory.Magic();
myhistory.PositionId()Используется для получения идентификатора позиции, в которой участвовал ордер.
long o_posid = myhistory.PositionId();
myhistory.PriceOpen()Используется для получения цены открытия ордера.
double o_price = myhistory.PriceOpen();
myhistory.Symbol()Используется для получения свойств символа (валютной пары) ордера.
string o_symbol = myhistory.symbol();
      

Отметим, что мы использовали эти функции в цикле для всех ордеров в истории.

1.4 Класс COrderInfo

Класс СOrderInfo является классом, обеспечивающим легкий доступ ко всем свойствам отложенного ордера.

После того, как создан объект класса, мы можем использовать его функции. Использование этого класса примерно такое же, как и класса CHistoryOrderInfo, рассмотренного выше. Создадим объект класса и назовем его myorder.

// Объект класса COrderInfo
COrderInfo myorder;

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

//Выбрать все исторические ордера за указанный временной период
if (HistorySelect(0,TimeCurrent()))   // все ордера
   {    
     // получаем общее количество ордеров
     int o_total = OrdersTotal();
   }

Теперь пройдемся в цикле по всем ордерам и получим их свойства, используя созданный нами объект myorder.

for (int j=0; j<o_total; j++)
{
 // сначала выбираем ордер при помощи функции Select класса COrderInfо, затем если все нормально, работаем с ним

Метод
Описание
Пример использования
myorder.Select(unlong ticket)
Используется для выбора ордера для дальнейшего доступа по указанному тикету.
if (myorder.Select(OrderGetTicket(j)) 
{
// ордер был успешно выбран,
// теперь мы можем с ним работать
}
myorder.TicketИспользуется для получения тикета предварительно выбранного ордера.
ulong o_ticket = myorder.Ticket();
myorder.TimeSetup()Используется для получения времени установки ордера.
datetime o_setup = myorder.TimeSetup();
myorder.OrderType()Используется для получения типа ордера (ORDER_TYPE_BUY_STOP и т.п.).
if (myorder.OrderType() == ORDER_TYPE_BUY_LIMIT)
{
// это ордер Buy Limit order, и т.д.
}
myorder.State()Используется для получения состояния ордера (отменен, принят, отклонен, размещен и т.п.).
if (myorder.State() ==ORDER_STATE_STARTED)
{
// ордер был проверен 
// и скоро может быть проверен брокером
}
myorder.TimeDone()Используется для получения времени исполнения или снятия ордера.
datetime ot_done = myorder.TimeDone();
myorder.Magic()Используется для получения идентификатора эксперта, выставившего ордер.
long o_magic =  myorder.Magic();
myorder.PositionId()Используется для получения идентификатора позиции, в которой участвовал ордер.
long o_posid = myorder.PositionId();
myorder.PriceOpen()Используется для получения цены открытия ордера.
double o_price = myorder.PriceOpen();
myorder.StopLoss()Используется для получения цены Stop loss ордера.
double s_loss = myorder.StopLoss();
myorder.TakeProfit()Используется для получения цены Take Profit ордера.
double t_profit = myorder.TakeProfit();
myorder.PriceCurrent()Используется для получения текущей цены по символу ордера.
double cur_price =  myorder.PriceCurrent();
myorder.Symbol()Используется для получения наименования символа ордера.
string o_symbol = myorder.Symbol();
myorder.StoreState()Используется для сохранения и хранения параметров ордера.
myorder.StoreState();
myorder.CheckState()Используется для сравнения текущих параметров ордера с сохраненными.
if (myorder.CheckState() == true)
{
// статус или параметры нашего ордера изменились
}


1.5 Класс CDealInfo

Класс CDealInfo предоставляет доступ к свойствам и информации о сделках в истории.

Создав объект класса, мы будем его использовать для получения информации о сделках в истории, подобно тому, как при работе с классом CHistoryOrderInfo. Поэтому, первое, что мы хотим сделать - создать объект класса, назовем его mydeal.

// Объект класса CDealInfo
CDealInfo mydeal; 

Мы начнем с получения общего количества сделок в истории.

if (HistorySelect(0,TimeCurrent()))
{
 // Получить общее количество сделок в истории
 int tot_deals = HistoryDealsTotal();
}

Далее мы пройдемся по всей истории доступных сделок и, используя объект нашего класса, получим детали каждой из сделок в истории.

ulong d_ticket; // deal ticket
for (int j=0; j<tot_deals; j++)
    {
     d_ticket = HistoryDealGetTicket(j);
     if (d_ticket>0)  
     {
Метод
 ОписаниеПример использования
mydeal.Ticket(ulong ticket)Используется для выбора сделки по тикету для дальнейшей работы с ней.
mydeal.Ticket(d_ticket);
mydeal.Ticket()Используется для получения тикета текущей выбранной сделки.
long dealticket=mydeal.Ticket();
mydeal.Order()Используется для получения тикета ордера, на основании которого выполнена сделка.
long deal_order_no =  mydeal.Order();
mydeal.Time() Используется для получения времени совершения сделки.
datetime d_time = mydeal.Time();
mydeal.DealType()Используется для получения типа сделки (DEAL_TYPE_BUY, DEAL_TYPE_SELL и т.п.).
if (mydeal.DealType() == DEAL_TYPE_BUY)
{
// Сделка типа BUY
}
 mydeal.Entry() Используется для получения направления сделки (DEAL_ENTRY_IN, DEAL_ENTRY_OUT и т.п.).
if (mydeal.Entry() == DEAL_ENTRY_IN)
{
// Направление сделки IN (открытие)
}
 mydeal.Magic() Используется для получения идентификатора эксперта (магический номер), совершившего сделку.
long d_magic = mydeal.Magic();
 mydeal.PositionId()Используется для получения уникального идентификатора позиции, в которой участвовала сделка.
long d_post_id = mydeal.PositionId();
 mydeal.Price()Используется для получения цены, по которой была выполнена сделка.
double d_price = mydeal.Price();
 mydeal.Volume()Используется для получения объема сделки (в лотах).
double d_vol = mydeal.Volume();
 mydeal.Symbol()Используется для получения символа сделки.
string d_symbol = mydeal.Symbol();


1.6 Класс CPositionInfo

Класс CPositionInfo является классом для упрощенного доступа к свойствам текущей позиции.

Перед использованием методов класса для получения свойств позиции, объект класса нужно создать. Создадим объект этого класса и назовем его myposition.

// Объект класса CPositionInfo
CPositionInfo myposition;

Теперь мы будем использовать этот объект для получения параметров открытой позиции. Начнем с запроса общего количества открытых позиций:

int pos_total = PositionsTotal();

Теперь время пройтись по всем открытым позициям и получить их параметры.

for (int j=0; j<pos_total; j++)
{
Метод
 ОписаниеПример использования
myposition.Select(
const string symbol)
Используется для выбора позиции по символу для дальнейшей работы.
if (myposition.Select(PositionGetSymbol(j)))
{
 // позиция по символу успешно выбрана, теперь 
 // мы сможем работать с позицией по данному символу
}
или
// если работаем с текущим символом графика
if (myposition.Select(_Symbol)) 
{
 // символ успешно выбран, теперь мы сможем работать
 // с текущей открытой позицией по этому символу
 }
myposition.Time()Используется для получения времени открытия позиции.
datetime pos_time = myposition.Time();
myposition.PositionType() Используется для получения типа открытой позиции.
if (myposition.PositionType() == POSITION_TYPE_BUY)
{
// Это длинная (buy) позиция
}
myposition.Magic()Используется для получения идентификатора эксперта, открывшего позицию.
long pos_magic = myposition.Magic();
 myposition.Volume()Используется для получения объема открытой позиции (в лотах).
// объем позиции
double pos_vol = myposition.Volume();
 myposition.PriceOpen()Используется для получения цены, по которой позиция была открыта.
double pos_op_price = myposition.PriceOpen();
 myposition.StopLoss()Используется для получения цены Stop Loss открытой позиции.
double pos_stoploss = myposition.StopLoss();
 myposition.TakeProfit()Используется для получения цены Take Profit открытой позиции.
double pos_takeprofit = myposition.TakeProfit();
 myposition.StoreState()Используется для сохранения параметров позиции.
// сохраняет текущее состояние позиции
myposition.StoreState();
myposition.CheckState()Используется для проверки текущих параметров позиции с сохраненными.
if (!myposition.CheckState())
{
  // состояние позиции не изменилось
}
 myposition.Symbol()Используется для получения имени символа позиции.
string pos_symbol = myposition.Symbol();


1.7 Класс CTrade

Класс CTrade предоставляет легкий доступ к торговым функциям.

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

//Объект класса CTrade
CTrade mytrade;

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

 МетодОписание
 Пример использования
mytrade.SetExpertMagicNumber(
ulong magic)
Используется для установки идентификатора эксперта.
ulong Magic_No=12345;
mytrade.SetExpertMagicNumber(Magic_No); 
mytrade.SetDeviationInPoints(
ulong deviation)
Эта функция также используется для установки значения допустимого проскальзывания (в пипсах), которое будет использоваться в торговле.
ulong Deviation=20;
mytrade.SetDeviationInPoints(Deviation); 
mytrade.OrderOpen(
const
string symbol,
ENUM_ORDER_TYPE
order_type,
double
volume,
double limit_price,
double price, double sl, double tp,
ENUM_ORDER_TYPE_TIME type_time,

datetime
expiration,
const
string comment="")
Используется для размещения отложенного ордера. Для использования этой функции, сначала нужно подготовить обязательные параметры и передать их в функцию.
//задаем входные параметры
double Lots = 0.1;
double SL = 0;
double TP = 0;
// последняя цена Bid, полученная 
// с использованием объекта класса CSymbolInfo
double Oprice = mysymbol.Bid()-_Point*550
// разместим отложенный ордер (SellStop)
mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELLSTOP,Lots,Oprice,
                  Oprice,SL,TP,ORDER_TIME_GTC,0);
mytrade.OrderModify(
ulong
ticket,double price,
double
sl,double tp,

ENUM_ORDER_TYPE_TIME
type_time,
datetime expiration)
Используется для модификации существующего отложенного ордера.
// Пройдемся по всем ордерам в истории и возьмем все отложенные 
// ордеры (как показано в разделе описания класса COrderInfo). 
// Для получения цен текущих цен Bid/Ask используется
// объект класса CSymbolInfo
int Stoploss = 400;
int Takeprofit = 550;
for(int j=0; j<OrdersTotal(); j++)
{
  ulong o_ticket = OrderGetTicket(j);
  if(o_ticket != 0)
  {
   // Stoploss должен быть объявлен выше 
   double SL = mysymbol.Bid() – Stoploss*_Point;    
   // Takeprofit должен быть объявлен выше 
   double TP = mysymbol.Bid() + Takeprofit*_Point;
   // последняя цена Bid, полученная 
   // при помощи объекта класса CSymbolInfo
   double Oprice = mysymbol.Bid()-_Point*550;
   // модифицируем отложенный Sell Stop ордер
   mytrade.OrderModify(o_ticket,Oprice,SL,TP,ORDER_TIME_GTC,0);
  }
}
mytrade.OrderDelete(
ulong ticket)
Используется для удаления отложенного ордера.
// Выбрать все ордера в истории 
// и получить общее количество отложенных ордеров 
int o_total=OrdersTotal();
for(int j=o_total-1; j>=0; j--)
{
   ulong o_ticket = OrderGetTicket(j);
   if(o_ticket != 0)
   {
    // удалить отложенный ордер Sell Stop
    mytrade.OrderDelete(o_ticket);
   }
}
mytrade.PositionOpen(
const  string symbol,
ENUM_ORDER_TYPE
order_type,
double volume,

double
price,double sl,double tp,
const string comment="")
Эта функция используется для открытия позиций на покупку (BUY) или продажу (SELL). Для использования этой функции все обязательные параметры должны быть предварительно подготовлены и переданы в эту функцию.
// Задаем входные параметры и используем объект 
// класса CSymbolInfo для получения текущих Bid/Ask цен
double Lots = 0.1;
// Stoploss должен быть объявлен выше 
double SL = mysymbol.Ask() – Stoploss*_Point;
//Takeprofit должен быть объявлен выше 
double TP = mysymbol.Ask() + Takeprofit*_Point; 
// последняя цена ask, полученная с помошью 
// объекта класса CSymbolInfo
double Oprice = mysymbol.Ask();
// открыть позицию на покупку
mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lots,
                     Oprice,SL,TP,"Test Buy");
 mytrade.PositionModify(
const string symbol,
double
sl,double tp)
Эта функция используется для изменения значений цен StopLoss и TakeProfit для существующей открытой позиции.
if (myposition.Select(_Symbol))
{
  int newStoploss = 250;
  int newTakeprofit = 500;
  double SL = mysymbol.Ask() – newStoploss*_Point;    
  double TP = mysymbol.Ask() + newTakeprofit*_Point;  
  // модифицируем открытую позицию по этому символу
  mytrade.PositionModify(_Symbol,SL,TP);
}
mytrade.PositionClose(
const string symbol,
ulong deviation=ULONG_MAX)
Используется для закрытия существующей открытой позиции.
if (myposition.Select(_Symbol))
{
 // закрыть открытую позицию по этому символу
 // величина проскальзывания была установлена ранее 
 mytrade.PositionClose(_Symbol);
}
mytrade.Buy(double volume,const string symbol=NULL,
double
price=0.0,double sl=0.0,double tp=0.0,
const string comment="")
Используется для размещения ордера на покупку.
Торговый объем
volume (в лотах) является обязательным параметром.
Значения цен
tp (take profit) и sl (stop loss) могут быть установлены позднее модификацией открытой позиции, для покупки используется текущая цена Ask.
double Lots = 0.1;
// задаем цену Stop Loss 
double SL = mysymbol.Ask() – Stoploss*_Point;  
// задаем Take profit
double TP = mysymbol.Ask() +Takeprofit*_Point; 
// получаем последнюю цену Ask через объект класса CSymbolInfo
double Oprice = mysymbol.Ask();  
// покупаем
mytrade.Buy(Lots,NULL,Oprice,SL,TP,"Buy Trade");
// можно это сделать и по-другому:
mytrade.Buy(Lots,NULL,0.0,0.0,0.0,"Buy Trade"); 
// модифицировать позицию будем позднее
mytrade.Sell(double volume,const string symbol=NULL,
double
price=0.0,double sl=0.0,double tp=0.0,
const string comment="")
Используется для размещения ордера на продажу.
Торговый объем volume (в лотах) является обязательным параметром. Значения цен tp (take profit) и sl (stop loss) могут быть установлены позднее модификацией открытой позиции, для покупки используется текущая цена Bid.
double Lots = 0.1;
// цена Stoploss
double SL = mysymbol.Bid() + Stoploss*_Point;  
// цена Take profit
double TP = mysymbol.Bid() - Takeprofit*_Point;  
// получаем последнюю цену Bid при помощи CSymbolInfo
double Oprice = mysymbol.Bid();  
// продаем
mytrade.Sell(Lots,NULL,Oprice,SL,TP,"Sell Trade");
// или по-другому:
mytrade.Sell(Lots,NULL,0.0,0.0,0.0,"Sell Trade");
// модифицировать позицию будем позднее
mytrade.BuyStop(double volume,double price,
const string symbol=NULL,double sl=0.0,
double tp=0.0, ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,
datetime
expiration=0,const string comment="")
Используется для размещения отложенного ордера BuyStop.
 double Lot = 0.1;
 //Buy price = bar 1 High + 2 pip + spread
 int sprd=mysymbol.Spread();
//--- цена покупки
 double bprice =mrate[1].high + 2*_Point + sprd*_Point;
//--- нормализуем
 double mprice = NormalizeDouble(bprice,_Digits);             
 //--- Stop Loss   
 double stloss = NormalizeDouble(bprice - STP*_Point,_Digits);
 //--- Take Profit
 double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits);
 //--- размещаем ордер BuyStop
 mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit);  
mytrade.SellStop(double volume,double price,
const string symbol=NULL,double sl=0.0,double tp=0.0,
ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,
datetime
expiration=0, const string  comment="")
Используется для  размещения отложенного ордера SellStop. 
double Lot = 0.1;
//--- цена продажи  = bar 1 Low - 2 pip 
double sprice=mrate[1].low-2*_Point;                        
//--- нормализуем
double slprice=NormalizeDouble(sprice,_Digits);             
//--- Stop Loss
double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);   
//--- Stop Loss
//--- Take Profit
double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits); 
//--- размещаем ордер SellStop
mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit);
mytrade.BuyLimit(double volume,double price,
const string symbol=NULL,double sl=0.0,double tp=0.0,
ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,
datetime
expiration=0,const string  comment="")
Используется для  размещения отложенного ордера BuyLimit.
//--- цена покупки = bar 1 Open  - 5 pip + spread
double Lot = 0.1;
/--- спред по символу
int sprd=mysymbol.Spread();                                   
double bprice = mrate[1].open - 5*_Point + sprd*_Point;       
//--- цена BuyLimit
double mprice=NormalizeDouble(bprice,_Digits);                
//--- размещаем ордер BuyLimit
mytrade.BuyLimit(Lot,mprice,_Symbol); 
mytrade.SellLimit (double volume,double price,
const string symbol=NULL,double sl=0.0,
double tp=0.0,
ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,
datetime
expiration=0,
const string comment="")
Используется для  размещения отложенного ордера SellLimit.
//--- цена ордера Sell Limit = bar 1 Open  + 5 pip 
double Lot = 0.1;
//--- цена SellLimit
double sprice = mrate[1].open + 5*_Point;
//--- нормализуем
double slprice=NormalizeDouble(sprice,_Digits);              
// размещаем SellLimit ордер
mytrade.SellLimit(Lot,slprice,_Symbol); 
 Методы анализа результатов выполнения
  
 mytrade.ResultRetcode()
Используется для получения кода результата торговой операции.
// торговая операция только что завершилась, получим код
int return_code = mytrade.ResultRetcode();
 mytrade.ResultRetcodeDescription()Используется для получения описания результата выполнения торговой операции в виде строки.
string ret_message =  ResultRetcodeDescription();
// покажем его
Alert("Код ошибки - " , mytrade.ResultRetcode() ,
      "сообщение об ошибке - ", ret_message);
 mytrade.ResultDeal()Используется для получения тикета сделки как результат запроса на совершение торговой операции.
long dl_ticket = mytrade.ResultDeal();
 mytrade.ResultOrder()Используется для получения тикета ордера как результат запроса на размещение рыночного ордера.
long o_ticket = mytrade.ResultOrder();
 mytrade.ResultVolume()Используется для получения объема (в лотах) как результат запроса торговой операции.
double o_volume = mytrade.ResultVolume();
mytrade.ResultPrice()Используется для получения цены, подтвержденной брокером.
double r_price = mytrade.ResultPrice();
mytrade.ResultBid()Используется для получения текущей рыночной цены Bid.
double rq_bid = mytrade.ResultBid;
 mytrade.ResultAsk()Используется для получения текущей рыночной цены Ask.
double rq_ask = mytrade.ResultAsk;
 mytrade.PrintRequest()
 
mytrade.PrintResult()
Эти функции могут быть использованы для вывода в Журнал параметров и результатов запросов на совершение торговых операций соответственно.
// после проведения торговой операции
// выводит параметры торгового запроса
mytrade.PrintRequest();
// выводит результаты выполнения торгового запроса
mytrade.PrintResult(); 
 Анализ результатов запроса
  
 mytrade.RequestAction()

Получает тип торговой операции.

// определяем тип торговой операции
// использованный в предыдущем запросе
if (mytrade.RequestAction() == TRADE_ACTION_DEAL)
{
  // это рыночный ордер с немедленным исполнением
}
else if (mytrade.RequestAction() == TRADE_ACTION_PENDING)
{
  // это отложенный ордер
}  
 mytrade.RequestMagic()

Получает идентификатор эксперта.

ulong mag_no = mytrade. RequestMagic();
 mytrade.RequestOrder()

Получает тикет ордера. В основном он используется для модификации отложенных ордеров.

ulong po_ticket =  mytrade.RequestOrder();
 mytrade.RequestSymbol()Используется для получения символа, использованной в последнем запросе.
string symb = mytrade.RequestSymbol(); 
 mytrade.RequestVolume()Используется для получения объема (в лотах), использованной в последнем запросе.
double Lot = mytrade.RequestVolume();  
 mytrade.RequestPrice()Используется для получения цены, использованной в последнем запросе.
double oprice = mytrade.RequestPrice();   
 mytrade.RequestStopLimit()Используется для получения цены Stop Limit, использованной в последнем запросе.
double limitprice = mytrade.RequestStopLimit(); 
 mytrade.RequestSL()Используется для получения цены Stop Loss, использованой в последнем запросе.
double sloss = mytrade.RequestSL();  
 mytrade.RequestTP()Используется для получения цены Take Profit, использованной в последнем запросе.
double tprofit = mytrade.RequestTP();   
 mytrade.RequestDeviation()Используется для получения значения Deviation (проскальзывание в пунктах), использованной в последнем запросе.
ulong dev = mytrade.RequestDeviation();  
 mytrade.RequestType()Используется для получения типа ордера, использованного в последнем запросе.
if (mytrade.RequestType() == ORDER_TYPE_BUY)
 {
  // market order Buy was placed in the last request.
 mytrade.RequestTypeDescription()Используется для получения описания ордера, использованного в последнем запросе.
Print("Описание ордера, использованное в предыдущем запросе:",
      mytrade.RequestTypeDescription());  
 mytrade.RequestActionDescription()Используется для получения описания типа торговой операции, использованной в последнем запросе.
Print("Тип торговой операции :", 
      mytrade.RequestTypeDescription());
 mytrade.RequestTypeFillingDescription()Используется для получения описания типа исполнения ордера, использованного в последнем запросе.
Print("Тип исполнения ордера: ",
      RequestTypeFillingDescription()); 
 

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

 //--- открываем позицию на покупку и проверяем результат
 if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
 //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit)) //--- запрос выполнен или ордер размещен
    {
     Alert("Покупка по цене: ", mytrade.ResultPrice() , ", объем:",mytrade.ResultVolume(),
          " была успешно выполнена, Ticket#:",mytrade.ResultDeal(),"!!");
     mytrade.PrintResult();
     }
 else
    {
     Alert("Запрос на покупку объема:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
            ", tp:",mytrade.RequestTP(), ", по цене:", mytrade.RequestPrice(),
          " не может быть выполнен -ошибка:",mytrade.ResultRetcodeDescription());
     mytrade.PrintRequest();
     return;
     }

В этом коде в случае ошибки мы выводим значения некоторых из параметров, отправленных в нашем запросе. Например, в случае, если мы неправильно указали цену Stop Loss, мы получим ошибку "Invalid Stops" и выведем значение Stop Loss при помощи функции  mytrade.RequestSL(). Это позволит нам найти причину неправильной установки цены Stop Loss.

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

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


2. Использование торговых классов

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

    • Советник будет проверять условия покупки и продажи, и при выполнении определенных условий, будет помещать ордера на покупку или продажу в зависимости от условий;
    • Если есть открытая позиция, и цена продолжает идти в нашем направлении, мы будем изменять значения уровней Take Profit и Stop Loss позиции. В случае, если цена идет против нас и наш уровень прибыли не достигнут, мы закроем позицию.
    • Наш советник будет использоваться на дневном графике на одной из следующих валютных пар – GBPUSD, AUDUSD, EURUSD и т.д.

    2.1 Пишем советник

    Для начала вызовем Мастер MQL5 и выберем Советник:

    Рисунок 1. Создание нового советника при помощи Мастера MQL5

    Рисунок 1. Создание нового документа при помощи Мастера MQL5

    Напишем имя советника и нажмем кнопку "Готово". Входные параметры советника мы зададим позже вручную.

    Рисунок 2. Указание имени советника

    Рисунок 2. Указание имени советника

    Созданный документ должен выглядеть следующим образом:

    The expert code skeleton

    Сразу же после строки, начинающейся с "#property version...", мы включим торговые классы, которые собираемся использовать.

    //+------------------------------------------------------------------+
    //|  Включаемые файлы с классами, которые будут использованы         |
    //+------------------------------------------------------------------+
    //--- Класс CTrade
    #include <Trade\Trade.mqh>
    //--- Класс CPositionInfo
    #include <Trade\PositionInfo.mqh>
    //--- Класс CAccountInfо
    #include <Trade\AccountInfo.mqh>
    //--- Класс CSymbolInfo
    #include <Trade\SymbolInfo.mqh>
    

    Затем зададим наши входные параметры:

    //+------------------------------------------------------------------+
    //|  Входные параметры                                               |
    //+------------------------------------------------------------------+
    input int      StopLoss=100;     // Stop Loss
    input int      TakeProfit=270 // Take Profit
    input int      ADX_Period=15;    // Период ADX
    input int      MA_Period=15;     // Период Moving Average
    input ulong    EA_Magic=99977;   // Magic Number советника
    input double   Adx_Min=24.0;     // Минимальное значение ADX
    input double   Lot=0.1;          // Количество лотов для торговли
    input ulong    dev=100;          // Проскальзывание 
    input long     Trail_point=32;   // Количество пунктов для увеличения TP/SL
    input int      Min_Bars = 20;    // Минимальное количество баров, требуемое эксперту для торговли
    input double   TradePct = 25;    // Процент свободной торговой маржи счета

    Также укажем другие параметры, которые будут использованы в коде советника:

    //+------------------------------------------------------------------+
    //|  Другие полезные параметры                                       |
    //+------------------------------------------------------------------+
    int adxHandle;            // хэндл нашего индикатора ADX
    int maHandle;             // хэндл нашего индикатора Moving Average
    double plsDI[],minDI[],adxVal[]; // Динамические массивы для хранения значений +DI, -DI и ADX каждого бара
    double maVal[];           // Динамический массив для хранения значений индикатора Moving Average для каждого бара
    double p_close;           // Переменная для хранения текущих значений цены закрытия бара
    int STP, TKP;             // Будут использоваться для Stop Loss, Take Profit 
    double TPC;                // Будет использован для контроля маржи

    Теперь создадим объекты каждого из классов, которые мы включили:

    //+------------------------------------------------------------------+
    //|  Создание объектов классов                                       |
    //+------------------------------------------------------------------+
    //--- Объект класса CTrade
    CTrade mytrade;
    //--- Объект класса CPositionInfo
    CPositionInfo myposition;
    //--- Объект класса CAccountInfo
    CAccountInfo myaccount;
    //--- Объект класса CSymbolInfo
    CSymbolInfo mysymbol;

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

    После того, как функции заданы, мы просто будем вызывать их из соответствующих разделов функций OnInit() и OnTick().

    2.1.1 Функция checkTrading

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

    //+------------------------------------------------------------------+
    //|  Проверяет, может ли советник совершать торговые операции        |
    //+------------------------------------------------------------------+
    bool checkTrading()
    {
      bool can_trade = false;
      // проверка того, синхронизирован ли терминал с сервером и т.д.
      if (myaccount.TradeAllowed() && myaccount.TradeExpert() && mysymbol.IsSynchronized())
      {
        // есть ли у нас достаточное количество баров?
        int mbars = Bars(_Symbol,_Period);
        if(mbars >Min_Bars)
        {
          can_trade = true;
        }
      }
      return(can_trade);
    }

    Мы объявили переменную can_trade типа bool и установили ее в false. Мы использовали объект класса CAccountInfo для проверки разрешения торговли и возможности автоматизированной торговли на активном счете. Мы также используем объект CSymbolInfo для проверки синхронизации клиентского терминала с торговым сервером.

    При удовлетворении всех этих условий мы проверяем общее количество текущих баров - оно должно быть больше, чем минимальное количество баров, требуемое нашему советнику для торговли. Если эта функция возвращает true, наш советник будет производить торговые операции, в противном случае, наш советник не будет торговать до тех пор, пока не станут удовлетворяться условия, проверяемые в данной функции.

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

    2.1.2 Функция ConfirmMargin

    //+------------------------------------------------------------------+
    //|  Подтверждает, что маржи достаточно для открытия ордера          |
    //+------------------------------------------------------------------+
    bool ConfirmMargin(ENUM_ORDER_TYPE otype)
      {
       bool confirm = false;
       double lot_price = myaccount.MarginCheck(_Symbol,otype,Lot); //цена лота/кол-во требуемой маржи
       double act_f_mag = myaccount.FreeMargin();                  //кол-во свободной маржи счета
       // проверяет, достаточно ли количества маржи
       if(MathFloor(act_f_mag*TPC)>MathFloor(lot_price))
         {
          confirm =true;
         }
        return(confirm);
      }

    Мы используем объект класса CAccountInfo для проверки того, достаточно ли маржи для торговли лотом, указанным в наших настройках при торговле некоторым указанным процентом свободной маржи нашего счета.

    Если требуемый процент свободной маржи больше, чем маржа, требуемая для помещения ордера, то функция возвращает true, в противном случае она возвращает false. Поэтому, мы хотим размещать ордер, если функция возвратила true. В качестве параметра функции используется тип ордера.

    2.1.3. Функция checkBuy

    //+------------------------------------------------------------------+
    //|  Проверяет условия на покупку                                    |
    //+------------------------------------------------------------------+
    bool checkBuy()
    {
      bool dobuy = false;
      if ((maVal[0]>maVal[1]) && (maVal[1]>maVal[2]) &&(p_close > maVal[1]))
      {
        // MA растет и цена закрытия предыдущего бара выше MA
        if ((adxVal[1]>Adx_Min)&& (plsDI[1]>minDI[1]))
        {
          // значение ADX больше, чем минимально требуемое, и +DI больше, чем -DI
          dobuy = true;
        }
      }
      return(dobuy);
    }

    Мы решили обернуть условия для покупки в данную функцию, никакие функции объектов класса здесь не использовались.  Здесь мы не использовали классы.  Мы проверяем условие того, что индикатор Moving Average (скользящая средняя) возрастает, а цена закрытия предыдущего бара больше, чем значение индикатора Moving Average.

    Также нам требуется соблюдение условия того, что значение индикатора ADX больше, чем минимально требуемое (заданное во входных параметрах) и при этом значение DI+ больше, чем DI-. Мы хотим, чтобы при выполнении всех этих условий наш советник размещал заказ на покупку.

    2.1.4 Функция checkSell

    //+------------------------------------------------------------------+
    //|  Проверяет условия на продажу                                    |
    //+------------------------------------------------------------------+
    bool checkSell()
    {
      bool dosell = false;
      if ((maVal[0]<maVal[1]) && (maVal[1]<maVal[2]) &&(p_close < maVal[1]))
      {
        // MA падает и цена закрытия предыдущего бара ниже MA
        if ((adxVal[1]>Adx_Min)&& (minDI[1]>plsDI[1]))
        {
          // значение ADX больше, чем минимально требуемое, и -DI больше, чем +DI
          dosell = true;
        }
      }
      return(dosell);
    }

    Эта функция производит проверку условий, которые противоположны тем, что заданы в функции CheckBuy. Здесь мы также не использовали никаких функций из объектов классов. Функция проверяет факт убывания индикатора Moving Average, при этом цена закрытия предыдущего бара должна быть меньше, чем значение индикатора Moving Average для предыдущего бара. Значение индикатора ADX должно быть больше, чем минимально установленное, при этом значение -DI должно быть больше, чем значение +DI.

    Нам нужно, чтобы при выполнении всех этих условий наш советник размещал ордера на продажу.

    2.1.5 Функция checkClosePos

    //+------------------------------------------------------------------+
    //|  Проверяет, нужно ли закрывать открытую позицию                  |
    //+------------------------------------------------------------------+
    bool checkClosePos(string ptype, double Closeprice)
    {
       bool mark = false;
       if (ptype=="BUY")
       {
          // нужно ли закрывать эту позицию?
         if (Closeprice < maVal[1])  // цена закрытия предыдущего бара ниже MA
          {
             mark = true;
          }
       }
       if (ptype=="SELL")
       {
          // нужно ли закрывать эту позицию?
          if (Closeprice > maVal[1]) // цена закрытия предыдущего бара выше MA
          {
             mark = true;
          }
       }
       return(mark);
    }

    Эта функция используется для проверки того, стоит ли закрывать открытую позицию. Данная функция (в зависимости от направления позиции, указанной в строковой переменной ptype) сравнивает цену закрытия предыдущего бара (Closeprice) со значением индикатора Moving Average. 

    Если удовлетворяется любое из условий, функция возвращает true, и мы ожидаем, что наш советник закроет позицию. У функции два входных параметра - тип ордера (в данном случае в виде строки "BUY" или "SELL") и цена закрытия предыдущего бара.

    2.1.6 Функция ClosePosition

    //+------------------------------------------------------------------+
    //|  Проверяет и если нужно, закрывает открытую позицию              |
    //+------------------------------------------------------------------+
    bool ClosePosition(string ptype,double clp)
      {
       bool marker=false;
         
          if(myposition.Select(_Symbol)==true)
            {
             if(myposition.Magic()==EA_Magic && myposition.Symbol()==_Symbol)
               {
                //--- проверяем, нужно ли закрывать позицию
                if(checkClosePos(ptype,clp)==true)
                  {
                   //--- закрываем позицию и проверяем успешность ее закрытия
                   if(mytrade.PositionClose(_Symbol)) //--- запрос успешно выполнен 
                     {
                      Alert("Открытая позиция была успешно закрыта!!");
                      marker=true;
                     }
                   else
                     {
                      Alert("Запрос на закрытие позиции не выполнен - ошибка: ",
                           mytrade.ResultRetcodeDescription());
                     }
                  }
               }
            }
          return(marker);
         }

    В этой функции используется рассмотренная выше функция checkClosePos, она использует объекты myposition  и mytrade классов CPositionInfo и CTrade. Для проверки наличия открытых позиций, а также того, что эта позиция была открыта нашим советником на текущем символе, используется объект класса CPositionInfo. Если такая позиция есть, то при помощи функции checkClosePos проверяются условия ее закрытия.

    Если функция checkClosePos возвратила true, то для закрытия позиции и анализа результата будет использоваться объект класса CTrade. Если позиция была успешно закрыта, функция возвращает true, в противном случае она вернет false.

    Функция ClosePosition имеет два входных параметра: направление позиции "BUY" или "SELL" и цена закрытия предыдущего бара.

    2.1.7 Функция CheckModify

    //+------------------------------------------------------------------+
    //|  Проверяет условия модификации открытой позиции                  |
    //+------------------------------------------------------------------+
    bool CheckModify(string otype,double cprc)
    {
       bool check=false;
       if (otype=="BUY")
       {
          if ((maVal[2]<maVal[1]) && (maVal[1]<maVal[0]) && (cprc>maVal[1]) && (adxVal[1]>Adx_Min))
          {
             check=true;
          }
       }
       else if (otype=="SELL")
       {
          if ((maVal[2]>maVal[1]) && (maVal[1]>maVal[0]) && (cprc<maVal[1]) && (adxVal[1]>Adx_Min))
          {
             check=true;
          }
       }
       return(check);
    } 

    Данная функция используется для проверки условий для модификации открытой позиции. В качестве параметров используется строка типа ордера ("BUY" или "SELL") и цена закрытия предыдущего бара.

    Функция проверяет, продолжает ли скользящая средняя (Moving Average) расти и при этом цена закрытия предыдущего бара остается выше ее, при этом значение индикатора ADX больше минимального необходимого (для позиции "BUY"), или продолжает уменьшаться, и при этом цена закрытия предыдущего бара ниже, чем значение скользящей средней (для позиции "SELL").  При выполнении условий наш советник будет рассматривать возможность модификации позиции в зависимости от ее типа.

    Функция имеет два аргумента (тип позиции "BUY"/"SELL" и цена закрытия предыдущего бара).

    2.1.8 Функция Modify

    //+------------------------------------------------------------------+
    //| Изменяет параметры открытой позиции                              |
    //+------------------------------------------------------------------+
       void Modify(string ptype,double stpl,double tkpf)
         {
          double ntp,nsl,pbid,pask;            //--- новые значения Stop Loss, Take Profit, цен Bid и Ask
          long tsp=Trail_point;
          if(_Digits==5 || _Digits==3) tsp=tsp*10;   //--- для 3 и 5-значных котировок
          long stplevel= mysymbol.StopsLevel();    //--- уровень Stops Level
          if(tsp<stplevel) tsp=stplevel;           //--- Trail point должна быть меньше чем Stops Level
          if(ptype=="BUY")
            {
             pbid=mysymbol.Bid();           //--- текущая цена Bid
             if(tkpf-pbid<=stplevel*_Point)
               {
                //--- расстояние до takeprofit меньше или равно Stops level? 
                //--- увеличиваем takeprofit
                ntp = pbid + tsp*_Point;
                nsl = pbid - tsp*_Point;
               }
             else
               {
                //--- расстояние до takeprofit выше, чем уровень Stops level?
                  //---takeprofit не меняем
                ntp = tkpf;
                nsl = pbid - tsp*_Point;
               }
            }
          else //--- это продажа (SELL)
            {
             pask=mysymbol.Ask();            //--- текущая цена Ask
             if(pask-tkpf<=stplevel*_Point)
               {
                ntp = pask - tsp*_Point;
                nsl = pask + tsp*_Point;
               }
             else
               {
                ntp = tkpf;
                nsl = pask + tsp*_Point;
               }
            }
          //--- модифицируем параметры и проверяем результат
          if(mytrade.PositionModify(_Symbol,nsl,ntp)) //--- запрос успешно выполнен 
            {
             Alert("Параметры открытой позиции были успешно модифицированы!!");
             return;
            }
          else
            {
             Alert("Запрос изменения параметров позиции не выполнен - ошибка: ",
                   mytrade.ResultRetcodeDescription());
             return;
            }
    
         }

    Функция Modify использует функцию checkModify. Она использует объекты классов CSymbolInfo и CTrade.  Сначала мы объявили четыре переменные типа double для новых значений take profit, stop loss, а также цен bid и ask. Затем объявили новую переменную tsp для хранения значения Trail_point, установленной в разделе входных параметров советника.

    Мы также учли возможность работы с 3/5-значными котировками, для этого используется переменная tsp для хранения значения пунктов трейлинга. Далее нужно получить информацию об допустимых уровнях Stop Level, для этого используется объект классса CSymbolInfo. Если величина трейлинга меньше, чем уровень Stop Level, то будет использоваться значение Stop Level.

    В зависимости от типа позиции, для получения текущих цен Bid и Ask мы используем объект класса CSymbolInfo. Если расстояние между текущей цено Bid/Ask и начальной ценой take profit меньше или равно stop level, то мы решаем скорректировать цены stop loss и take profit, иначе изменяем только цену stop loss. Затем мы используем объект класса CTrade для изменения уровней Stop loss и take profit позиции. Советник также выводит сообщение в зависимости от результата выполнения торгового запроса.

    Мы закончили с определением некоторых пользовательских функций, которые облегчат нашу работу.

    Теперь пойдем вперед к разделам кода советника.

    2.1.9 Функция OnInit

    //--- устанавливаем наименование символа для объекта класса CSymbolInfo
       mysymbol.Name(_Symbol);
    //--- задаем идентификатора эксперта (Expert Magic No) в объекте класса CTrade
       mytrade.SetExpertMagicNumber(EA_Magic);
    //--- задаем максимально допустимого проскальзывания в объекте класса CTrade
       mytrade.SetDeviationInPoints(dev);
    //--- получаем хэндл индикатора ADX
       adxHandle=iADX(NULL,0,ADX_Period);
    //--- получаем хэндл индикатора Moving Average
       maHandle=iMA(_Symbol,Period(),MA_Period,0,MODE_EMA,PRICE_CLOSE);
    //--- если хэндлы неверные
       if(adxHandle<0 || maHandle<0)
         {
          Alert("Ошибка создания хэнлов индикаторов MA и ADX - ошибка: ",GetLastError(),"!!");
          return(-1);
         }
       STP = StopLoss;
       TKP = TakeProfit;
    //--- будем поддерживать брокеров с 5/3 знаками
       if(_Digits==5 || _Digits==3)
         {
          STP = STP*10;
          TKP = TKP*10;
         }
    //--- установим процент капитала для торговли
        TPC = TradePct;
        TPC = TPC/100;
    //---

    Мы устанавливаем текущий символ для объекта класса CSymbolInfo. Мы также устанавливаем идентификатор эксперта (магический номер) и отклонение (в пунктах), которые будут использоваться объектом класса CTrade. После этого мы получаем хэндлы индикаторов и показываем ошибку в случае, если хэндлы созданы не были.

    Далее мы вводим корректировку значений stop loss и take profit для котировок точность 3/5 знаков после запятой, также мы преобразовываем процент используемой для торговли свободной маржи счета.

    2.1.10 Функция OnDeinit

    //--- Освобождаем хэдлы наших индикаторов
       IndicatorRelease(adxHandle);
       IndicatorRelease(maHandle);

    Здесь мы освобождаем все хэндлы индикаторов. 

    2.1.11 Функция OnTick

    //--- проверим снова, может ли советник торговать
        if (checkTrading() == false) 
       {
          Alert("Советник не может торговать, поскольку не удовлетворяются некоторые торговые требования");
          return;
       }
    //--- Объявим структуру типа MqlRates, которую будем использовать при торговле
       MqlRates mrate[];   // будет использована для хранения цен, объемов и спреда каждого бара
    /*
         Убедимся в том, что значения наших массивов для котировок, 
         значений индикатора ADX и MA указаны как таймсерии
    */
    // массив котировок
       ArraySetAsSeries(mrate,true);
    // массив значений ADX
       ArraySetAsSeries(adxVal,true);
    // массив значений MA
       ArraySetAsSeries(maVal,true);
    // массив значений -DI
       ArraySetAsSeries(minDI,true);
    // массив значений +DI
       ArraySetAsSeries(plsDI,true);
    

    В этом разделе мы снова решили проверить способность советника торговать. Если функция checktrade вернула false, советник будет ждать следующего тика и только тогда снова совершать проверку.

    После этого мы объявили MQL5-структуру типа MqlRates, которая будет использоваться для получения цен каждого бара и затем применили функцию ArraySetAsSeries для всех требуемых массивов.

    //--- Получим последние котировки, используя функцию объект класса CSymbolInfo
      if (!mysymbol.RefreshRates())
         {
          Alert("Ошибка обновления котировок - ошибка: ",GetLastError(),"!");
          return;
         }
       
    //--- Копируем данные по 3-м последним барам
       if(CopyRates(_Symbol,_Period,0,3,mrate)<0)
         {
          Alert("Ошибка копирования исторических котировок - ошибка: ",GetLastError(),"!");
          return;
         }
    
    //--- советник должен проверять условия торговли только при появлении нового бара
    //--- объявим статическую переменную типа datetime
       static datetime Prev_time;
    //--- объявим массив из одного элемента для хранения времени начала текущего бара (бар 0)
       datetime Bar_time[1];
    //--- копируем время текущего бара
       Bar_time[0] = mrate[0].time;
    //--- если оба времени равны, новый бар не появился
       if(Prev_time==Bar_time[0])
         {
          return;
         }
    //--- сохраним время в статической переменной, 
       Prev_time = Bar_time[0]; 

    Мы используем объект класса CSymbolInfo для получения текущих цен и копируем бары цен в массив mrates.  Сразу после этого производится проверка наличия нового бара.

    Если появился новый бар, тогда наш советник будет проверять наступление условий для покупки/продажи, иначе он будет ждать появление нового бара.

    //--- копируем новые значения индикаторов в массивы, используя хэндлы индикаторов
       if(CopyBuffer(adxHandle,0,0,3,adxVal)<3 || CopyBuffer(adxHandle,1,0,3,plsDI)<3
          || CopyBuffer(adxHandle,2,0,3,minDI)<3)
         {
          Alert("Ошибка копирования буферов индикатора ADX - ошибка:",GetLastError(),"!!");
          return;
         }
       if(CopyBuffer(maHandle,0,0,3,maVal)<3)
         {
          Alert("Ошибка копирования буфера индикатора Moving Average - ошибка:",GetLastError());
          return;
         }
    //--- ошибок нет, продолжаем
    // Копируем цену закрытия предыдущего бара (бара, предшествующего текущему, т.е. бара 1)
       p_close=mrate[1].close;  // цена закрытия бара 1

    Здесь мы использовали функцию CopyBuffer для копирования значений наших индикаторов в массивы, в случае возникновения ошибок будут выведены сообщения об ошибках. Затем копируется цена закрытия предыдущего бара.

    //--- Есть ли открытые позиции по текущему символу?
      bool Buy_opened = false, Sell_opened=false; 
       if (myposition.Select(_Symbol) ==true)  // открытые позиции есть
        {
          if (myposition.PositionType()== POSITION_TYPE_BUY)
           {
                Buy_opened = true;  // длинная позиция (Buy)
               //--- получаем значения StopLoss и Take Profit
               double buysl = myposition.StopLoss();      // Stop Loss
               double buytp = myposition.TakeProfit();    // Take Profit
               //--- проверяем, можем ли мы их модифицировать
               if (ClosePosition("BUY",p_close)==true)
                 {
                    Buy_opened = false;   // позиция была закрыта
                    return;                 // ждем нового бара
                 }
               else
               {
                  if (CheckModify("BUY",p_close)==true) // мы можем ее модифицировать
                  {
                      Modify("BUY",buysl,buytp);
                      return;                           // ждем нового бара
                  }
               } 
           }
          else if(myposition.PositionType() == POSITION_TYPE_SELL)
           {
                Sell_opened = true; // короткая позиция (Sell)
                // Получаем значения StopLoss и Take Profit
                double sellsl = myposition.StopLoss();    // Stop Loss
                double selltp = myposition.TakeProfit();  // Take Profit
                 if (ClosePosition("SELL",p_close)==true)
                 {
                   Sell_opened = false;                   // позиция была закрыта
                   return;                                // ждем нового бара
                 }
                 else
                 {
                     if (CheckModify("SELL",p_close)==true) // мы можем ее модифицировать
                     {
                         Modify("SELL",sellsl,selltp);
                         return;                           // ждем нового бара
                     }
                 } 
           }
        } 

    Для выбора и проверки наличия открытой позиции для текущего символа мы использовали объект класса CPositionInfo. Если позиция существует, и это позиция на покупку (BUY), мы устанавливаем значение переменной Buy_opened в true, а затем используем объект класса CPositionInfo для получения значений Stop Loss и Take Profit позиции. При помощи функции ClosePosition, определенной выше, мы проверяем, была ли закрыта позиция. Если функция возвратила true (позицию закрыли), мы устанавливаем Buy_opened в false, поскольку позиция BUY только что была закрыта. Советник будет ждать нового тика.

    Однако, если функция возвратила false, позиция не была закрыта. Теперь самое время проверить, можем ли мы изменить параметры позиции. Это достигается при помощи функции CheckModify, которую мы определили ранее.  Если эта функция возвратила true, это означает, что параметры позиции могут быть модифицированы, поэтому мы используем функцию Modify для изменения параметров позиции.

    С другой стороны, если позиция существует, и это позиция на продажу (SELL), то устанавливаем значение переменной Sell_opened в true и используем объект класса CPositionInfo для получения значений Stop Loss и Take Profit позиции. Мы повторяем те же шаги, которые были в случае с позицией на покупку - проверяем, была ли закрыта позиция, и если она не была закрыта,  проверяем, нужно ли изменять ее параметры.

     if(checkBuy()==true)
       {
        //--- есть открытые позиции на покупку?
        if(Buy_opened)
          {
           Alert("Уже есть открытая позиция на покупку!!!");
           return;   //--- не будем добавлять к позиции
          }
    
        double mprice=NormalizeDouble(mysymbol.Ask(),_Digits);               //--- последняя цена ask
        double stloss = NormalizeDouble(mysymbol.Ask() - STP*_Point,_Digits); //--- Stop Loss
        double tprofit = NormalizeDouble(mysymbol.Ask()+ TKP*_Point,_Digits); //--- Take Profit
        //--- проверка наличия достаточного количества средств
        if(ConfirmMargin(ORDER_TYPE_BUY,mprice)==false)
          {
           Alert("Недостаточно средств для торговли с заданными параметрами");
           return;
          }
        //--- открываем позицию на покупку и проверяем результат
        if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
         //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit)) 
          {//--- Запрос выполнен или ордер размещен
            Alert("Ордер на покупку был успешно размещен, Ticket#:",mytrade.ResultDeal(),"!!");
          }
        else
         {
            Alert("Ордер на покупку объема:",mytrade.RequestVolume(), 
                  ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(),
                  ", цена:", mytrade.RequestPrice(), 
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
            return;
         }
       }

    Также мы можем использовать функцию PositionOpen:

     if(checkBuy()==true)
       {
        //--- есть открытые позиции на покупку?
        if(Buy_opened)
          {
           Alert("Уже есть открытая позиция на покупку!!!");
           return;   //--- не будем добавлять к позиции
          }
    
        double mprice=NormalizeDouble(mysymbol.Ask(),_Digits);               //--- последняя цена Ask
        double stloss = NormalizeDouble(mysymbol.Ask() - STP*_Point,_Digits); //--- Stop Loss
        double tprofit = NormalizeDouble(mysymbol.Ask()+ TKP*_Point,_Digits); //--- Take Profit
        //--- check margin
        if(ConfirmMargin(ORDER_TYPE_BUY,mprice)==false)
          {
           Alert("Недостаточно средств для торговли с заданными параметрами");
           return;
          }
        //--- открываем позицию на покупку и проверяем результат
        / /if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
         if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit))
          {//--- Запрос выполнен или ордер размещен
            Alert("Ордер на покупку был успешно размещен, Ticket#:",mytrade.ResultDeal(),"!!");
          }
        else
         {
            Alert("Ордер на покупку объема:",mytrade.RequestVolume(), 
                  ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(),
                  ", цена:", mytrade.RequestPrice(), 
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
            return;
         }
       }

    Здесь мы используем функцию checkBuy для проверки продажи, если она возвращает true, то условия для продажи выполняются. Если у нас уже есть открытая позиция на покупку (BUY), мы не хотим размещать новый ордер.

    Мы используем функцию ConfirmMargin для проверки размера маржи счета - он должен быть больше, чем размер маржи, требуемый для размещения этого ордера. Если эта функция возвратила true, мы затем используем объект класса CSymbolInfo для получения текущей цены Ask, нужной для вычисления значений Stop loss и Take profit.

    Используя объект класса CTrade, мы размещаем наш ордер и используем тот же самый объект для получения кода возврата торговой операции. Выводимое сообщение зависит от результата.

          if(checkSell()==true)
            {
             //--- есть открытая позиция на продажу?
             if(Sell_opened)
               {
                Alert("Уже есть открытая позиция на продажу!!!");
                return;    //--- не добавляем к открытой позиции
               }
    
             double sprice=NormalizeDouble(mysymbol.Bid(),_Digits);             //--- последняя цена Bid
             double ssloss=NormalizeDouble(mysymbol.Bid()+STP*_Point,_Digits);   //--- Stop Loss
             double stprofit=NormalizeDouble(mysymbol.Bid()-TKP*_Point,_Digits); //--- Take Profit
             //--- проверка наличия достаточного количества средств
             if(ConfirmMargin(ORDER_TYPE_SELL,sprice)==false)
               {
                Alert("Недостаточно средств для торговли с заданными параметрами");
                return;
               }
             //--- открываем позицию на продажу и проверяем результат
             if(mytrade.Sell(Lot,_Symbol,sprice,ssloss,stprofit))
             //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_SELL,Lot,sprice,ssloss,stprofit)) 
             {//--- запрос выполнен или ордер размещен
                Alert("Ордер на продажу был успешно размещен, Ticket#:",mytrade.ResultDeal(),"!!");
               }
             else
               {
                Alert("Ордер на продажу объема:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
                        ", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                      " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
                return;
               }
            }

    Также мы можем использовать функцию PositionOpen:

    if(checkSell()==true)
            {
             //--- есть открытая позиция на продажу?
             if(Sell_opened)
               {
                Alert("Уже есть открытая позиция на продажу!!!");
                return;    //--- не добавляем к открытой позиции
               }
    
             double sprice=NormalizeDouble(mysymbol.Bid(),_Digits);             //--- последняя цена Bid
             double ssloss=NormalizeDouble(mysymbol.Bid()+STP*_Point,_Digits);   //--- Stop Loss
             double stprofit=NormalizeDouble(mysymbol.Bid()-TKP*_Point,_Digits); //--- Take Profit
             //--- проверка наличия достаточного количества средств
             if(ConfirmMargin(ORDER_TYPE_SELL,sprice)==false)
               {
                Alert("You do not have enough money to place this trade based on your setting");
                return;
               }
             //--- открываем позицию на продажу и проверяем результат
             //if(mytrade.Sell(Lot,_Symbol,sprice,ssloss,stprofit))
             if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_SELL,Lot,sprice,ssloss,stprofit)) 
               { //---Запрос выполнен или ордер размещен
                Alert("Ордер на продажу был успешно размещен, Ticket#:",mytrade.ResultDeal(),"!!");
               }
             else
               {
                Alert("Ордер на продажу объема:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
                        ", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                     " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
                return;
               }
            }

    Таким же образом, как это делалось для случая покупки, мы использовали CheckSell для проверки условий продажи. Если она возвращает true, и у нас на текущий момент нет открытой позиции на продажу, мы используем функцию ConfirmMargin для проверки наличия достаточного количества денег для размещения ордера. Если ConfirmMargin возвратила true, мы используем объект класса CTrade для размещения ордера и вывода сообщения в зависимости от результата выполнения запроса.

    До сих пор мы рассматривали вопрос о том, как использовать торговые классы Стандартной библиотеки при написании советника. Следующее, что мы сделаем - протестируем нашего советника в Тестере стратегий и посмотрим результаты его работы. Скопилируйте код советника и загрузите его в Тестер стратегий.

    Рисунок 3. Отчет о результатах компиляции советника

    Рисунок 3. Отчет о результатах компиляции советника

    При тестировании на дневном графике пары GBPUSD с настройками: Take Profit - 270, Stop Loss - 100 and Trails Point (TP/SL) - 32, мы получили следующие результаты:

    Рисунок 4. Отчет тестирования советника на дневном графике GBPUSD

    Рисунок 4. Отчет тестирования советника на дневном графике GBPUSD

    Рисунок 5. График Баланса/прибыли тестрования советника на дневном графике GBPUSD

    Рисунок 5. Динамика баланса/средств при тестировании советника на дневном графике GBPUSD

    Рисунок 6. Отчеты в журнале тестирования об изменениях параметров открытой позиции

    Рисунок 6. Отчет в журнале тестирования об изменениях параметров открытой позиции

    Результат тестирования на дневном графике AUDUSD  с настройками TP - 270, SL - 100, Trail Point (TP/SL) - 32, мы получили следующие результаты:


    Рисунок 7. Результат тестирования советника - сделки на графике GBPUSD

    Рисунок 7. Результат тестирования советника - сделки на графике GBPUSD

    Вы можете сами протестировать советник на любом другом дневном графике с различными настройками Take profit, Stop loss и Trail_point и посмотреть результаты.

    Однако, вы должны понимать, что этот советник был написан лишь для тестовых целей...

    Теперь, рассмотрим, как мы можем использовать другие классы (COrderInfo, CHistoryOrderInfo, и CDealInfo) для получения информации об ордерах и сделках.

    2.2 Размещение и удаление отложенных ордеров

    В данном разделе мы напишем простейший советник, который будет размещать отложенный ордер (BuyStop или SellStop) при возникновении условий на покупку или продажу.

    2.2.1 Включаем необходимые классы

    //+------------------------------------------------------------------+
    //|  Включаемые файлы с классами, которые будут использованы         |
    //+------------------------------------------------------------------+
    //--- Класс CTrade
    #include <Trade\Trade.mqh>
    //--- Класс CPositionInfo
    #include <Trade\PositionInfo.mqh>
    //--- Класс CSymbolInfo
    #include <Trade\SymbolInfo.mqh>
    //--- Класс COrderInfo
    #include <Trade\OrderInfo.mqh>

    Четыре класса, которые мы включили, будут использоваться в нашем советнике. Ниже мы рассмотрим их на примерах.

    Я не буду подробно объяснять каждую часть кода этого советника, поскольку он подобен тому, что мы уже обсуждали. Однако главные моменты мы все же рассмотрим.

    Отличие в том, что здесь мы решили объявить массив mrate[] типа MqlRates на глобальном уровне.

    //--- Определяем структуру типа MqlRates, которую мы будем использовать в нашей торговле
    MqlRates mrate[];     // будет хранить цены, объемы и спреды баров
    

    Файлы с описанием классов мы включили, не забудем создать объекты каждого из классов:

    //+------------------------------------------------------------------+
    //|  Создаем объекты классов                                         |
    //+------------------------------------------------------------------+
    //--- Объект класса CTrade
    CTrade mytrade;
    //--- Объект класса CPositionInfo
    CPositionInfo myposition;
    //--- Объект класса CSymbolInfo
    CSymbolInfo mysymbol;
    //--- Объект класса COrderInfo
    COrderInfo myorder;

    Функции CheckBuy() и CheckSell() те же, что и в советнике, который мы рассматривали ранее.

    Здесь мы хотим поместить ордера BUYSTOP и SELLSTOP в случае выполнения условий на покупку или продажу.

    Пройдемся по некоторым функциям, которые мы создали для облегчения нашей работы.

    2.2.2 Функция CountOrders

    //+-----------------------------------------------------------------------------+
    //| Считает общее количество ордеров, размещенных советником на данном символе  |
    //+-----------------------------------------------------------------------------+
    int CountOrders()
    {
       int mark=0;
       for (int i=OrdersTotal()-1; i>=0; i--)
       {
          if (myorder.Select(OrderGetTicket(i)))
          {
             if (myorder.Magic()==EA_Magic && myorder.symbol()==_Symbol) mark++;
          }
       }
       return(mark);
    }

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

    Мы использовали объект класса COrderInfo для получения свойств ордера, если он был успешно выбран функцией myorder.Select().

    Если Magic и символ ордера соответствуют заданным условиям, это означает, что ордер был размещен нашим советником, поэтому увеличиваем счетчик, размещенный в переменной mark.

    2.2.3 Функция DeletePending

    //+------------------------------------------------------------------+
    //| Проверяет и удаляет отложенные ордера                            |
    //+------------------------------------------------------------------+
    bool DeletePending()
      {
       bool marker=false;
    //--- проверка всех отложенных ордеров
       for(int i=OrdersTotal()-1; i>=0; i--)
         {
          if(myorder.Select(OrderGetTicket(i)))
            {
             if(myorder.Magic()==EA_Magic && myorder.Symbol()==_Symbol)
               {
                //--- проверим время ордера - оно должно быть меньше, чем время два бара назад
                if(myorder.TimeSetup()<mrate[2].time)
                  {
                   //--- удаляем этот отложенный ордер
                   //--- был ли ордер успешно удален?
                   //--- проверяем результат
                    if(mytrade.OrderDelete(myorder.Ticket()))  
                      { // Запрос успешно выполнен
                          Alert("Отложенный ордер с тикетом #", myorder.Ticket(), " был успешно удален!!");
                          marker=true;
                      }
                     else
                      {
                             Alert("Запрос на удаление отложенного ордера #",myorder.Ticket(),
                               " не выполнен - ошибка: ",mytrade.ResultRetcodeDescription());
    
                      }
    
                  }
               }
            }
         }
       return(marker);
      }

    Как и функция CountOrders, эта функция также использует методы класса COrderInfo для получения свойств ордеров. Эта функция делает проверку на наличие любых несработавших отложенных ордеров, установленных три бара назад (с временем установки, меньшим чем mrate[2].time).

    Если таковые присутствуют, для удаления ордера вызывается функция OrderDelete класса CTrade. В случае успеха, функция возвращает true, иначе false.

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

    // есть ли у нас более чем 3 установленных отложенных ордера
    if (CountOrders()>3) 
      {
         DeletePending(); 
         return;  
      }

    2.2.4 Размещение отложенного ордера

       if(checkBuy()==true)
         {
          Alert("Общее количество отложенных ордеров:",CountOrders(),"!!");
          //--- есть ли открытая позиция на покупку?
          if(Buy_opened)
            {
             Alert("Уже есть позиция на покупку!!!");
             return;    //--- не добавлять к открытой позиции
            }
          // цена покупки = bar 1 High + 2 pip + spread
          int sprd=mysymbol.Spread();
          double bprice =mrate[1].high + 10*_Point + sprd*_Point;
          double mprice=NormalizeDouble(bprice,_Digits);               //--- цена покупки 
          double stloss = NormalizeDouble(bprice - STP*_Point,_Digits); //--- Stop Loss
          double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits); //--- Take Profit
          //--- разместить ордер BuyStop
          if(mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit))
          //if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_BUY_STOP,Lot,0.0,bprice,stloss,tprofit,ORDER_TIME_GTC,0)) 
            {
             //--- запрос выполнен или ордер размещен
             Alert("Ордер BuyStop был успешно размешен, тикет ордера #:",mytrade.ResultOrder(),"!!");
             return;
            }
          else
            {
             Alert("Запрос на размещение ордера BuyStop с объемом:",mytrade.RequestVolume(), 
                    ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
             return;
            }
         }

    Мы также можем использовать функцию OrderOpen для размещения ордера BUYSTOP:

       if(checkBuy()==true)
         {
          Alert("Общее количество отложенных ордеров:",CountOrders(),"!!");
          //--- есть ли открытая позиция на покупку?
          if(Buy_opened)
            {
             Alert("Уже есть позиция на покупку!!!");
             return;    //--- не добавлять к открытой позиции
            }
          // цена покупки = bar 1 High + 2 pip + spread
          int sprd=mysymbol.Spread();
          double bprice =mrate[1].high + 10*_Point + sprd*_Point;
          double mprice=NormalizeDouble(bprice,_Digits);               //--- цена покупки
          double stloss = NormalizeDouble(bprice - STP*_Point,_Digits); //--- Stop Loss
          double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits); //--- Take Profit
          //--- разместить ордер BuyStop
          //if(mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit))
          if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_BUY_STOP,Lot,0.0,bprice,stloss,tprofit,ORDER_TIME_GTC,0)) 
            {
             //--- запрос выполнен или ордер размещен
             Alert("Ордер BuyStop был успешно размешен, тикет ордера #:",mytrade.ResultOrder(),"!!");
             return;
            }
          else
            {
             Alert("Запрос на размещение ордера BuyStop с объемом:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
                    ", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
             return;
            }
         }

    При размещении нашего ордера BUYSTOP мы указали цену: цена High бара 1 + 2 пункта + спред. 

    Запомните, что на графике указывается цена Bid, при размещении ордеров на покупку нужно использовать цену Ask, вот почему мы решили добавить спред к цене High бара 1, так что теперь мы имеем соответствующую цену Ask + 2 пункта. Цены Stop Loss и Take Profit уже заданы во входных параметрах.

    После того, как мы подготовили все необходимые параметры, мы используем функцию OrderOpen класса CTrade для размещения нашего ордера. В нашем случае мы используем тип ордера  ORDER_TYPE_BUY_STOP. Мы использовали ту же цену для лимитного ордера, однако это не ордер типа BuyLimit. Мы установили срок действия ордера в ORDER_TIME_GTC, которое означает, то ордер будет действителен до отмены.

    При указании сроков действия ордера ORDER_TIME_GTC или ORDER_TIME_DAY не требуется указания времени истечения, поэтому мы установили его в 0.

    if(checkSell()==true)
         {
          Alert("Общее количество отложенных ордеров:",CountOrders(),"!!");
          //--- есть ли открытая позиция на продажу?
          if(Sell_opened)
            {
             Alert("Уже есть позиция на продажу!!!");
             return;    //--- не добавлять к открытой позиции
            }
          //--- цена продажи = bar 1 Low - 2 pip 
          double sprice=mrate[1].low-10*_Point;
          double slprice=NormalizeDouble(sprice,_Digits);            //--- цена продажи
          double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);   //--- Stop Loss
          double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits); //--- Take Profit
          //--- разместить ордер SellStop
          if(mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit))
          //if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELL_STOP,Lot,0.0,slprice,ssloss,stprofit,ORDER_TIME_GTC,0)) 
            {
             //--- запрос выполнен или ордер размещен
             Alert("Ордер SellStop был успешно размешен, тикет ордера #",mytrade.ResultOrder(),"!!");
             return;
            }
          else
            {
             Alert("Запрос на размещение ордера SellStop с объемом:",mytrade.RequestVolume(),
                  ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
             return;
            }
         }

    Для размещения ордера SELL STOP мы также можем использовать функцию OrderOpen:

    if(checkSell()==true)
         {
          Alert("Общее количество отложенных ордеров:",CountOrders(),"!!");
          //--- есть ли открытая позиция на продажу?
          if(Sell_opened)
            {
             Alert("Уже есть позиция на продажу!!!");
             return;    //--- не добавлять к открытой позиции
            }
          //--- цена продажи = bar 1 Low - 2 pip 
          double sprice=mrate[1].low-10*_Point;
          double slprice=NormalizeDouble(sprice,_Digits);            //--- цена продажи
          double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);   //--- Stop Loss
          double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits); //--- Take Profit
          //--- разместить ордер SellStop
          //if(mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit))
          if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELL_STOP,Lot,0.0,slprice,ssloss,stprofit,ORDER_TIME_GTC,0)) 
            {
             //--- запрос выполнен или ордер размещен
             Alert("Ордер SellStop был успешно размешен, тикет ордера #",mytrade.ResultOrder(),"!!");
             return;
            }
          else
            {
             Alert("Запрос на размещение ордера SellStop с объемом:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
                    ", tp:",mytrade.RequestTP(), ", цена:", mytrade.RequestPrice(),
                  " не выполнен -ошибка:",mytrade.ResultRetcodeDescription());
             return;
            }
         }

    Как и в случае с ордерами BuyStop, цена открытия вычисляется как цена Low бара 1 + 2 пункта. Здесь нам не нужно добавлять спред, поскольку открытие ордеров на покупку производится по ценам Bid. Для размещения ордера SellStop мы использовали ту же функцию OrderOpen. В данном случае тип ордера установлен как ORDER_TYPE_SELL_STOP.

    Результаты работы нашего простого советника приведены ниже.

    Рисунок 8. Результаты теста советника, торгующего по отложенным ордерам

    Рисунок 8. Результаты теста советника, торгующего по отложенным ордерам

     Рисунок 9. Графический отчет этого советника

    Рисунок 9.  Графический отчет этого советника

    Рисунок 10. Сделки на графике

    Рисунок 10. Сделки на графике

     

    2.3 Получение свойств ордеров и сделок

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

    В этой стадии отложенного ордера теперь не существует, поскольку он сработал и превратился в сделку.

    Для более полного понимания этой процедуры рассмотрим детали журнала одной из наших торговых операций.

    Рисунок 11. Процедура обработки ордеров

    Рисунок 11. Процедура обработки ордеров

    • Шаг 1: Размещенный отложенный ордер ждет условий исполнения (отложенный ордер, pending order)
    • Шаг 2: Условие выполнилось, отложенный ордер сработал, превратившись в сделку (отложенный ордер теперь в истории)
    • Шаг 3: Сделка произведена и мы имеем открытую позицию (сделка теперь в истории).

    2.3.1 Получение свойств ордеров в истории

    //+------------------------------------------------------------------+
    //|  Включаем все классы, которые будут использоваться               |
    //+------------------------------------------------------------------+
    //--- Класс СHistoryOrderInfo
    #include <Trade\HistoryOrderInfo.mqh>
    //+------------------------------------------------------------------+
    //|  Создаем объект класса                                           |
    //+------------------------------------------------------------------+
    //--- Объект класса CHistoryOrderInfo
    CHistoryOrderInfo myhistory;
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //--- Получаем все исторические ордера и их детали
       int buystop=0;
       int sellstop=0;
       int buylimit=0;
       int selllimit=0;
       int buystoplimit=0;
       int sellstoplimit=0;
       int buy=0;
       int sell=0;
       int s_started=0;
       int s_placed=0;
       int s_cancelled=0;
       int s_partial=0;
       int s_filled=0;
       int s_rejected=0;
       int s_expired=0;
    
       ulong o_ticket;
    //--- Выбираем все исторические записи
       if(HistorySelect(0,TimeCurrent())) //-- Все исторические ордера
         {
          //--- Получаем общее количество ордеров в истории
          for(int j=HistoryOrdersTotal(); j>0; j--)
            {
             //--- Выбираем ордер по индексу
             o_ticket=HistoryOrderGetTicket(j);
             if(o_ticket>0)
               {
                //--- Устанавливаем тикет ордера для дальнейшей работы с ним
                myhistory.Ticket(o_ticket);
                Print("Индекс ордера ",j," Тикет: ",myhistory.Ticket()," !");
                Print("Индекс ордера ",j," Время установки: ",TimeToString(myhistory.TimeSetup())," !");
                Print("Индекс ордера ",j," Цена открытия: ",myhistory.PriceOpen()," !");
                Print("Индекс ордера ",j," Символ: ",myhistory.Symbol()," !");
                Print("Индекс ордера ",j," Тип ордера: ",myhistory.OrderType()," !");
                Print("Индекс ордера ",j," Описание типа: ",myhistory.TypeDescription()," !");
                Print("Индекс ордера ",j," Magic: ",myhistory.Magic()," !");
                Print("Индекс ордера ",j," Время исполнения: ", myhistory.TimeDone() ," !");
                Print("Индекс ордера ",j," Начальный объем: ", myhistory.VolumeInitial() ," !");
                //
                if(myhistory.OrderType() == ORDER_TYPE_BUY_STOP) buystop++;
                if(myhistory.OrderType() == ORDER_TYPE_SELL_STOP) sellstop++;
                if(myhistory.OrderType() == ORDER_TYPE_BUY) buy++;
                if(myhistory.OrderType() == ORDER_TYPE_SELL) sell++;
                if(myhistory.OrderType() == ORDER_TYPE_BUY_LIMIT) buylimit++;
                if(myhistory.OrderType() == ORDER_TYPE_SELL_LIMIT) selllimit++;
                if(myhistory.OrderType() == ORDER_TYPE_BUY_STOP_LIMIT) buystoplimit++;
                if(myhistory.OrderType() == ORDER_TYPE_SELL_STOP_LIMIT) sellstoplimit++;
    
                if(myhistory.State() == ORDER_STATE_STARTED) s_started++;
                if(myhistory.State() == ORDER_STATE_PLACED) s_placed++;
                if(myhistory.State() == ORDER_STATE_CANCELED) s_cancelled++;
                if(myhistory.State() == ORDER_STATE_PARTIAL) s_partial++;
                if(myhistory.State() == ORDER_STATE_FILLED) s_filled++;
                if(myhistory.State() == ORDER_STATE_REJECTED) s_rejected++;
                if(myhistory.State() == ORDER_STATE_EXPIRED) s_expired++;
               }
            }
         }
    // Вывод статистики
       Print("По типу ордеров");
       Print("Рыночных ордеров Buy: ",buy);
       Print("Рыночных ордеров Sell: ",sell);
       Print("Отложенных ордеров Buy Stop: ",buystop);
       Print("Отложенных ордеров Sell Stop: ",sellstop);
       Print("Отложенных ордеров Buy Limit: ",buylimit);
       Print("Отложенных ордеров Sell Limit: ",selllimit);
       Print("Отложенных ордеров Buy Stop Limit: ",buystoplimit);
       Print("Отложенных ордеров Sell Stop Limit: ",sellstoplimit);
       Print("Общее количество ордеров: ",HistoryOrdersTotal()," !");
    
       Print("По статусу ордеров");
       Print("Проверены на корректность, но еще не приняты брокером: ",s_started);
       Print("Принято: ",s_placed);
       Print("Снято клиентом: ",s_cancelled);
       Print("Выполнены частично: ",s_partial);
       Print("Выполнены полностью: ",s_filled);
       Print("Отклонены: ",s_rejected);
       Print("Сняты по истечении срока действия ордера: ",s_expired);
      }
    

    Этот простой скрипт показывает, как получить свойства ордеров в истории.

    Мы включили класс CHistoryOrderInfo и создали объект класса, который используется для получения свойств ордеров.

    Рисунок 12. Результат работы скрипта history_order.mq5 

    Рисунок 12. Результат работы скрипта history_order.mq5

    2.3.2 Получение свойств сделок в истории

    //+------------------------------------------------------------------+
    //|  Включаем все классы, которые будут использоваться               |
    //+------------------------------------------------------------------+
    //--- Класс CDealInfo
    #include <Trade\DealInfo.mqh>
    //+------------------------------------------------------------------+
    //|  Создаем объект класса                                           |
    //+------------------------------------------------------------------+
    //--- Объект класса CDealInfo
    CDealInfo mydeal;
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //--- Получим историю всех сделок и их свойства
        int buy=0;
        int sell=0;
        int deal_in=0;
        int deal_out=0;
        ulong d_ticket;
        // Get all history records
        if (HistorySelect(0,TimeCurrent())) 
        {
          // Получаем общее количество сделок в истории
          for (int j=HistoryDealsTotal(); j>0; j--)
          {
             // выбираем сделку по тикету
             d_ticket = HistoryDealGetTicket(j);
             if (d_ticket>0)
             {
              // устанавливаем тикет сделки
              mydeal.Ticket(d_ticket);
              Print("Индекс сделки ", j ," Тикет: ", mydeal.Ticket() ," !");
              Print("Индекс сделки ", j ," Время исполнения: ", TimeToString(mydeal.Time()) ," !");
              Print("Индекс сделки ", j ," Цена: ", mydeal.Price() ," !");
              Print("Индекс сделки ", j ," Символ: ", mydeal.Symbol() ," !");
              Print("Индекс сделки ", j ," Тип сделки: ", mydeal.TypeDescription() ," !");
              Print("Индекс сделки ", j ," Magic: ", mydeal.Magic() ," !");
              Print("Индекс сделки ", j ," Время сделки: ", mydeal.Time() ," !");
              Print("Индекс сделки ", j ," Начальный объем: ", mydeal.Volume() ," !");
              Print("Индекс сделки ", j ," Направление сделки: ", mydeal.EntryDescription() ," !");
              Print("Индекс сделки ", j ," Прибыль сделки: ", mydeal.Profit() ," !");
              //
              if (mydeal.Entry() == DEAL_ENTRY_IN) deal_in++;
              if (mydeal.Entry() == DEAL_ENTRY_OUT) deal_out++;
              if (mydeal.DealType() == DEAL_TYPE_BUY) buy++;
              if (mydeal.DealType() == DEAL_TYPE_SELL) sell++;
             }
          }
        }
        // Вывод статистики
        Print("Общее количество сделок в истории :", HistoryDealsTotal(), " !");
        Print("Общее количество сделок IN : ", deal_in);
        Print("Общее количество сделок OUT : ", deal_out);
        Print("Общее количество сделок на покупку : ", buy);
        Print("Общее количество сделок на продажу: ", sell);
      }

    Этот простой скрипт показывает, как получать свойства записей наших сделок в истории.

    Рисунок 13. Результат работы скрипта history_deal.mq5

    Рисунок 13. Результат работы скрипта history_deal.mq5


    Выводы

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

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

    Было бы неплохо расширить раздел описания различных функций и рассмотреть примеры их использования при написании советников.

    Классы Стандартной библиотеки призваны облегчить жизнь трейдерам и разработчикам, используйте их.

    Перевод с английского произведен MetaQuotes Ltd.
    Оригинальная статья: https://www.mql5.com/en/articles/138

    Прикрепленные файлы |
    history_deal.mq5 (3.26 KB)
    history_order.mq5 (4.98 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (26)
    Yedelkin
    Yedelkin | 26 февр. 2011 в 19:10

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

    //Выбрать все исторические ордера за указанный временной период
    if (HistorySelect(0,TimeCurrent()))   // все ордера
       {    
         // получаем общее количество ордеров
         int o_total = OrdersTotal();
       }
    Но если функция HistorySelect() предназначена для запроса "историю сделок и ордеров", т.е. для запроса исторических ордеров, зачем использовать эту функцию при запросе общего количества действующих отложенных ордеров?

     

    Yedelkin
    Yedelkin | 26 февр. 2011 в 19:26
    Yedelkin:

    В классе  COrderInfo ...

     Но функция Type() в классе отсутствует.

    Та же самая ситуация с классами CDealInfo и CPositionInfo
    Yedelkin
    Yedelkin | 26 февр. 2011 в 21:42

    Может кто объяснит? Вот есть такой кусочек кода из класса CDealInfo:

    //+------------------------------------------------------------------+
    //|                                                     DealInfo.mqh |
    //+------------------------------------------------------------------+
    #include <Object.mqh>
    #include "SymbolInfo.mqh"
    //+------------------------------------------------------------------+
    //| Class CDealInfo.                                                 |
    //| Appointment: Class for access to history deal info.              |
    //|              Derives from class CObject.                         |
    //+------------------------------------------------------------------+
    class CDealInfo : public CObject
      {
       ...
    public:
       ...
       //--- fast access methods to the string position propertyes
       string            Symbol()           const;
       ...
      };
    ...
    //+------------------------------------------------------------------+
    //| Get the property value "DEAL_SYMBOL".                            |
    //| INPUT:  no.                                                      |
    //| OUTPUT: the property value "DEAL_SYMBOL".                        |
    //| REMARK: no.                                                      |
    //+------------------------------------------------------------------+
    string CDealInfo::Symbol() const
      {
       return(HistoryDealGetString(m_ticket,DEAL_SYMBOL));                           //тупо return
      }
    ...
    //+------------------------------------------------------------------+
    //| Converts the deal parameters to text.                            |
    //| INPUT:  str  - receiving string,                                 |
    //|         deal - pointer at the class instance.                    |
    //| OUTPUT: formatted string.                                        |
    //| REMARK: no.                                                      |
    //+------------------------------------------------------------------+
    string CDealInfo::FormatDeal(string& str) const
      {
       string type,volume,price,date;
       CSymbolInfo symbol;                                                    
    //--- set up
       symbol.Name(Symbol());
       int digits=symbol.Digits();
    //--- form the description of the deal
       switch(Type())
         {
          ...
         }
    //--- return the result
       return(str);
      }
    ...
    //+------------------------------------------------------------------+

     Здесь функция CDealInfo::FormatDeal(string& str) содержит вот такую строчку:

       symbol.Name(Symbol());
    В свою очередь, функция Symbol() определена как в самом классе CDealInfo, так и среди стандартных функций клиентского терминала. Какая именно функция передаётся в качестве аргумента в функцию symbol.Name()? По какому правилу? 
    Mykola Demko
    Mykola Demko | 26 февр. 2011 в 22:37
    Yedelkin:

    Может кто объяснит? Вот есть такой кусочек кода из класса CDealInfo:

     Здесь функция CDealInfo::FormatDeal(string& str) содержит вот такую строчку:

    В свою очередь, функция Symbol() определена как в самом классе CDealInfo, так и среди стандартных функций клиентского терминала. Какая именно функция передаётся в качестве аргумента в функцию symbol.Name()? По какому правилу? 

    Тут работает тоже правило что и с областью видимости переменных. Если под одним именем объявлена переменная на глобальном и локальном уровне, то внутри локальной области имя будет указывать на локальную переменную, а во вне на глобальную.

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

    Yedelkin
    Yedelkin | 26 февр. 2011 в 22:49
    Urain:

    Тут работает тоже правило что и с областью видимости переменных. Если под одним именем объявлена переменная на глобальном и локальном уровне, то внутри локальной области имя будет указывать на локальную переменную, а во вне на глобальную.

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

    Спасибо огромное! Чётко и понятно!
    Разработка и реализация новых виджетов на основе класса CChartObject Разработка и реализация новых виджетов на основе класса CChartObject
    После написания статьи про полуавтоматический советник с графическим интерфейсом пользователя у меня возникла необходимость расширения интерфейса новым функционалом для более сложных индикаторов и экспертов. Ознакомившись с классами Стандартной библиотеки, я сделал новые виджеты. В этой статье описан процесс создания и использования новых элементов пользовательского интерфейса, созданных на базе класса CChartObjectEdit.
    Александр Ануфренко: "Знал бы, где упасть - перинку бы подстелил" (ATC 2010) Александр Ануфренко: "Знал бы, где упасть - перинку бы подстелил" (ATC 2010)
    Рискованная разработка Александра Ануфренко (Anufrenko321) в течение трех недель не покидала первую тройку Чемпионата. Пережив на прошлой неделе чудовищный стоп-лосс, эксперт потерял около $60 000, но сейчас вновь подбирается к лидирующим позициям. Автор этого интересного эксперта решил рассказать о принципах работы и особенностях своего детища.
    Димитар Манов:"На Чемпионате я боюсь только исключительных обстоятельств"(ATC 2010) Димитар Манов:"На Чемпионате я боюсь только исключительных обстоятельств"(ATC 2010)
    В недавнем отчете Бориса Одинцова одним из самых стабильных советников Чемпионата был назван эксперт болгарского Участника Manov. Мы решили поговорить с автором этой разработки и выяснить, в чем заключается секрет ее успеха. В интервью Димитар Манов рассказывает о том, какую ситуацию его эксперт не переживет, почему он не использует индикаторы и рассчитывает ли на победу в соревновании.
    Создание интерактивного советника для полуавтоматической торговли с заданным риском Создание интерактивного советника для полуавтоматической торговли с заданным риском
    Некоторые трейдеры используют автоматизированные системы торговли, другие совмещают автоматическую торговлю с ручной, основанной на показаниях нескольких индикаторов. Я принадлежу ко второй группе, поэтому возникла необходимость в интерактивном инструменте для динамической оценки рисков и ценовых уровней непосредственно с графика. В данной статье мы представим способ реализации интерактивного советника для полуавтоматической торговли с заданным уровнем соотношения прибыль/риск (Reward/Risk ratio).