MQL 4: Библиотеки функций и их использование в программах (17-21.01.05)
|

MQL 4: Библиотеки функций и их использование в программах (17-21.01.05)

   После публичной премьеры бета версии MetaTrader 4 прошло не так много времени, но благодаря активному обсуждению на форуме фирмы MetaQuotes Software достоинств и недостатков терминала, разработчики, прислушиваясь к мнению будующих пользователей программы, вносят некоторые изменения как в интерфейс пользователя программы, так и в программный интерфейс испольняющей MQL-программы среды. Так, в одной из последних сборок программы были упразднены синонимы для массивов Open, Low, High, Close, Time и Volume.


   В связи с этим, обращаем ваше внимание на то, что эти шесть массивов уже не имеют сокращённых синонимов, упомянутых в предыдущей статье. Теперь использование имени массива O не эквивалентно использованию имени Open, L – не эквивалентно Low, H – не эквивалентно High, C – не эквивалентно Close, T – не эквивалентно Time и V – не эквивалентно Volume, соответственно. Таким образом, программы, написанные с использованием подобных сокращений, придётся немного исправить. Со слов разработчиков, сделано это было с целью избежания путаницы. Доверимся интуиции разработчиков и двинемся далее. Ещё одно замечание хотелось бы сделать в связи с одной неточностью, допущенной автором в статье “Язык программирования MQL 4: Выполнение программ, их типы и структура”, опубликованной в 48-м номере журнала Forex Magazine.


   В статье утверждалось, что у программ типа советник есть эксклюзивная возможность – во время своего выполнения совершать сделки. Такой возможности нет у других типов пользовательских программ. На самом же деле, такая возможность существует ещё у одного варианта MQL-программ – у пользовательских скриптов.


   Сегодня, исправляя допущенную неточность, мы создадим скрипт совершающий сделку. Думаем, что не стоит объяснять читателям, каким образом осуществляется открытие маркет ордера в программе MetaTrader. Те, кто ещё не имеет такого опыта, могут самостоятельно потренироваться, ведь каждый из скачавших терминал может зарегистрироваться на сервере для получения демонстрационного счёта. Демонстрационный счёт во многом схож с реальным и может помочь новичкам попробовать свои силы на финансовых ранках. Часто, особенно у трейдеров новичков, это может занять достаточно продолжительное время. Возможность быстро открывать рыночные ордера может быть по достоинсиву оценена теми, кто торгует на новостях, скальперами и другими интрадей трейдерами. В этой статье будет приведён пример скрипта практически моментально выставляющего маркет ордер на покупку. Одним из преимуществ открытия маркет ордера с помощью скрипта является то, что вычисление параметров ордера типа “stop loss”, “take profit” и размер открываемой позиции можно возложить на MQL-программу. Особенно полезной такая возможность может оказаться при быстрых движениях рынка, когда каждая секунда промедления может оказаться очень дорогой в прямом смысле этого слова. Но сам скрипт сегодня для нас, как для изучающих MQL 4, будет интересен тем, что на его примере будет рассмотренно создание и использование библиотеки функций.


Итак, начнём с описания алгоритма работы скрипта:
. установить степень риска, то есть процент свободного депозита, которым мы согласны пожертвовать в случае неудачного для нас исхода развития событий на рынке;
. выяснить уровень, на котором нужно выставлять “stop loss” в данной ситуации;
. определить уровень “take profit”;
. подсчитать величину лота, который удовлетворял бы степени риска и размеру “stop loss”;
. попытаться исполнить маркет ордер с полученными ранее параметрами.


   Для того, чтобы подсчитать величину лота открываемого ордера в программе использована функция MMGetLotsCount. Ничего страшного не произошло бы если бы мы встроили реализацию эту функцию в тот же файл, что содержит код скрипта. Но может так оказаться, что эта же функция в дальнейшем потребуется нам для написания эксперта. Если не использовать каких-то ухищрений, то в эксперте использующем тот же алгоритм вычисления размера лота пришлось бы так же повторить, встроив реализацию этой функцию в тот же файл, что содержит код эксперта. Представим себе ситуацию, когда вы пересмотрели и хотябы частично изменили способ вычисления размера открываемой позиции. Все MQL-программы, которые используют старый способ вычислений должны быть исправлены и перекомпилированы. Если окажется, что код этой функции нужно исправить в трёх-четырёх местах, то, не велика беда, можно это сделать достаточно быстро, но хуже, когда окажется, что он был использован в двадцати программах – временные затраты на изменение всех программ могут быть весьма ощутимыми. Но и это не самое страшное, может случиться так, что в одной или нескольких программах код по какой-либо причине случайно не будет обновлён. Тогда начнётся настоящий кошмар для пользователя таких MQL-программ, так как часть из них будет вычислять размер лота одним способом, а остальные – совсем по-другому.


   Для того чтобы избежать подобных осложнений программистам на MQL 4 предлагается создавать библиотеки функций. В этой статье мы создадим библиотеку функций MMlib (Money Management Library), состоящую пока только из одной функции MMGetLotsCount. В дальнейшем в MMlib можно будет добавить другие функции для вычисления тех или иных показателей управления капиталом.


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


. в папке expertslibraries нужно cоздать файл mmlib.mq4, содержащий заголовок из коментариев, такой, какой имеет любая другая MQL-программа;
. вслед за заголовком добавить модулю свойство library:


#property library


   Компилятор языка MQL4 достаточно умен и не включает в скомпилированный модуль функции, которые не вызываются нигде внутри этого модуля. Cвойство модуля “library” гарантирует, что все неиспользуемые, но реализованные в исходном коде модуля функции попадут в скомпилированный модуль.


. поместить реализации функций, которые после его компиляции в библиотеку можно будет импортировать в свою программу.
. в папке expertsincludes cоздать файл mmlib.mqh, содержащий заголовок из коментариев, такой какой имеет любая другая MQL-программа;
. вслед за заголовком добавить инструкцию, импортирующую скомпилированную библиотеку MMlib:
. далее в файл mmlib.mqh добавить объявление функции MMGetLotsCount, такое же, как было в mmlib.mq4, но без тела функции.


   На этом создание библиотеки, содержащей единственную функцию MMGetLotsCount закончено. Если в эту библиотеку потребуется добавить ещё функции, то их объявления нужно будет вписать в файл MMlib.mqh, а реализации этих функций записать в MMlib.mq4. Теперь, перекомпилировав модуль, мы получим библиотеку МMlib.ex4, содержащую функцию MMGetLotsCount и только что добавленные функции.


   Подобным образом можно создать ваши собственные библиотеки, содержащие ваши собственные функции. Кроме того, что использование библиотек функций помогает решить проблемы описанные выше, ещё одним из преимуществ создания библиотек функций заключается в том, что их можно распространять в виде скомпилированных модулей, сопровождаемых .mqh файлами. Таким образом, можно соблюсти права на интеллектуальную собственность. Ещё одним из преимуществ создания библиотек функций является то, что если не изменять параметры вызова функций, то их можно модифицировать независимо от использующих их программ. Например, если обнаружится, что в одной из библиотечных функций допущена ошибка, её можно исправить и перекомпилировать только модуль, содержащий исправленную функцию; использующую его программу перекомпилировать не требуется.


   Использовать библиотеку в своей программе можно включив в свой код .mqh-файл, соответствующий библиотеке. Такие файлы обычно называют заголовочным файлом, а .mq4-файл библиотеки называют файлом реализации. С файлами реализации мы имеем дело только на этапе создания библиотеки, т.е на этапе создания .ex4-файла. Таким образом, использование “спрятанных” в библиотеке функций обычно осуществляется с помошью включения в свою программу заголовочного файла, соответствующего библиотеке, используя инструкцию #include. За разъяснениями особенностей работы этой инструкции советуем обратиться к справочнику по языку MQL 4 с сайта MeatQuotes Software. Далее приводится код всех трёх файлов, который иллюстрирует сказанное выше. Следует помнить, что код рекомендуется рассматривать только, как пример реализации библиотеки функций и использующего его скрипта и не пытаться использовать его для получения реальной прибыли на фининсовых рынках. Также приведённый код, благодаря обилию коментариев, лёгок для понимания и может служить стартовой точкой для создания своего варианта скрипта с требуемой функциональностью.


Содержание файла mmlib.mq4


//+——————————————————————+
//| mmlib.mq4 |
//| Copyright c 2004, Alexander Ivanov. |
//| alexander@indus.ru |
//+——————————————————————+
#property copyright “Copyright c 2004, Alexander Ivanov.”
#property link “mailto:alexander@indus.ru
#property library
double MMGetLotsCount(double dPrice, double dStopLoss, double dRisk, double dLeverage)
{
double dSpread = MarketInfo(Symbol(),MODE_SPREAD);
double dPoint = MarketInfo(Symbol(),MODE_POINT);
double dFreeMargin = AccountFreeMargin();
double dMoneyRisk = dFreeMargin*dRisk/100;
double dPointsRisk = MathAbs(dPrice-dStopLoss)/dPoint;
double dPointCost = dMoneyRisk/dPointsRisk;
double dLots = dPointCost/dLeverage;
// Округляем dLots до десятых долей, т.к. MT пока
// не позволяет совершать сделки с более мелкими лотами
return(NormalizeDouble(dLots,1));
//////////////////////////////////////////////////////////////////
//
// ВНИМАНИЕ!!!
//
// Нужно быть аккуратным всвязи с тем, что округление
// происходит до ближайшей десятой доли, т.е. 0.06 станет 0.1
//
// Если такой алгоритм Вас не устраивает, то
// реализуйте свой способ округления, например, округление до
// ближайшей меньшей десятой доли
//
//////////////////////////////////////////////////////////////////
}



Содержание файла mmlib.mqh


//+——————————————————————+
//| mmlib.mqh |
//| Copyright c 2004, Alexander Ivanov. |
//| alexander@indus.ru |
//+——————————————————————+
#import “mmlib.ex4”
double MMGetLotsCount(double dPrice, double dStopLoss, double dRisk, double dLeverage);


Содержание файла order_by.mq4


//+——————————————————————+
//| order_buy.mq4 |
//| Copyright © 2004, Alexander Ivanov. |
//| alexander@indus.ru |
//+——————————————————————+
#property copyright “Copyright © 2004, Alexander Ivanov.”
#property link “mailto:alexander@indus.ru
// Далее константа DAYS_TO_CONSIDER будет нам заменять количество
// дней, на которых мы будем искать минимум для выставления Stop loss’а
#define DAYS_TO_CONSIDER 3
// Можно было бы самому добавить такие определения в stdlib.mqh
// но будем надеяться, что MetaQuotes Software сделают это самостоятельно
// в следующей версии stdlib.mqh, а пока, для удобства пользования,
// определяем константы ошибок в этом модуле. После добавления их в
// stdlib.mqh, следующие две строки нужно будет закомментировать
#define ERROR_NOT_ENOUGH_MONEY 134
#define ERROR_PRICE_CHANGED 135
//////////////////////////////////////////////////////////////////
//
// ВНИМАНИЕ!!!
//
// Разрешить импорт функций из скомпилированных библиотек
// можно поставив галочку ’Разрешить импортирование внешних экспертов’
// на закладке ’Советники’ окна ’Настройки’ вызываемого выбором пункта


// меню ’Сервис’->’Настройки’
//
// В нашем случае это обязательно, так как мы собираемся импортировать
// функцию ErrorDescription(…) из библиотеки stdlib и
// функцию MMGetLotsCount(…) из библиотеки mmlib
//
//////////////////////////////////////////////////////////////////
// Подключаем библиотеки, содержащие реализации используемых
// в этом скрипте функций
#include <stdlib.mqh> // содержит нужную нам функцию ErrorDescription(…)
#include <mmlib.mqh> // содержит нужную нам функцию MMGetLotsCount(…)
int start()
{
// while(true) означает, что мы собираемся выполнять попытку
// выставить ордер до тех пор, пока он не будет принят к исполнению
// или получится так, что у нас на счету закочатся свободные деньги
// … или пользователь вручную не прекратит выполнение скрипта,например,
// выбрав соответствующий пункт из контекстного меню ценового графика
while(true) {
double dMyRisk = 20; // Предположим, что каждый раз мы согласны рисковать
// двадцатью процентами свободного депозита
double dMyLeverage = 100; // Плечо, предоставляемое нам брокером
// DaysLowArray – это массив, в который будут помещены минимальные
// дневные значения за DAYS_TO_CONSIDER последних дней
double DaysLowArray[DAYS_TO_CONSIDER];
// Копируем DAYS_TO_CONSIDER дневных значений Low в массив DaysLowArray
if(ArrayCopySeries(DaysLowArray, MODE_LOW, Symbol(),PERIOD_D1)
< DAYS_TO_CONSIDER) {
return(PrintErrorDescription());
}
// В качестве значения Stop loss будем использовать
// минимальное дневное значение текущего и двух предыдущих дней
double dMyStopLoss =
DaysLowArray[ArrayMinimum(DaysLowArray,DAYS_TO_CONSIDER – 1,0)];
// Запоминаем текущую цену на тот случай, если во время вычислений
// количества лотов цена сильно изменится.
double dMyPrice = Ask;
MQL 4: Библиотеки функций и их использование в программах.
FOREX MAGAZINE © 2004-2005
Январь 2005 №52/3
59
// Для простоты устанавливаем Take profit в два раза больше, чем Stop loss
double dMyTakeProfit = dMyPrice + 2*(dMyPrice-dMyStopLoss);
double dMyLots =
MMGetLotsCount(dMyPrice ,dMyStopLoss ,dMyRisk ,dMyLeverage);
Print(“Цена = “, dMyPrice,
” Стоп = “, dMyStopLoss,
” Тейк профит = “, dMyTakeProfit,
” Лотов = “, dMyLots);
if(OrderSend(Symbol() // Торгуемый инструмент
,OP_BUY // Тип операции
,dMyLots // Количество лотов
,dMyPrice // Цена ордера
,3 // Проскальзывание
,dMyStopLoss // Stop loss
,dMyTakeProfit // Take profit
,”Ordered by “order_buy” script”
,255 // Уникальный номер
,0 // Время окончания работы отложенного ордера
,HotPink // Цвет метки на графике
) <= 0) {
int error = PrintErrorDescription();
// Обработка ошибки, возникшей во время выставления ордера
switch(error) {
case ERROR_NOT_ENOUGH_MONEY: {
// нехватка свободных денег, придётся закончить торговлю :-(
return(error);
}
case ERROR_PRICE_CHANGED: {
// цена успела измениться, нужно обновить котировки
// и попробовать ещё раз выставить ордер
RefreshRates();
break;
}
default: {
// ждём 10 секунд
Sleep(10000);
}
}
} else {


OrderPrint();
break;
}
}
return(0);
}
// Код исполняемый функцией PrintErrorDescription() используется
// дважды внутри функции start(), и поэтому мы сочли нужным вынести
// его в отдельную функцию
int PrintErrorDescription()
{
int error=GetLastError();
Print(“Error = “,ErrorDescription(error));
return(error);
}



Александр Иванов
для Forex Magazine
fxtrade@tomsk.ru