Русский

Учебник по MQL4  Простые программы на MQL4  Совместное использование программ

Совместное использование программ


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

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

Функция iCustom()

double iCustom(string symbol, int timeframe, string name, ..., int mode, int shift)

Расчет указанного пользовательского индикатора. Пользовательский индикатор должен быть скомпилирован (файл с расширением EX4) и находиться в директории Каталог_терминала\experts\indicators.

Параметры:

symbol - символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.

timeframe - период. Может быть одним из периодов графика. 0 означает период текущего графика.

name - имя пользовательского индикатора.

... - Список параметров (при необходимости). Передаваемые параметры должны соответствовать порядку объявления и типу внешних (extern) переменных пользовательского индикатора.

mode - Индекс линии индикатора. Может быть от 0 до 7 и должен соответствовать индексу, используемому одной из функций SetIndexBuffer.

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


Рассмотрим, как практически может быть использована функция iCustom(), для этого решим следующую задачу:

Задача 30. Торговая стратегия построена на данных пользовательского индикатора rocseparate.mq4. Если линия скорости изменения цены на текущем таймфрейме (оранжевая) пересекает сглаженную среднюю линию скорости (красная жирная) ниже некоторого заданного уровня снизу вверх, то считать значимым торговый критерий на покупку (открыть Buy и закрыть Sell). При зеркальных условиях считать значимым критерий на продажу. Представить код эксперта, реализующего эту стратегию.

Принцип построения пользовательского индикатора rocseparate.mq4 подробно рассмотрен в разделе Пользовательский индикатор ROC. На Рис. 131 показаны две точки, в которых линия скорости изменения цены на текущем таймфрейме (М15) пересекается со средней сглаженной линией скорости изменения цены. В точке А оранжевая линия пересекает красную снизу вверх, причём место пересечения линий находится ниже уровня -0.001. В точке В оранжевая пересекает красную сверху вниз, причём точка пересечения находится выше уровня 0.001. Факт пересечения указанных линий должен быть вычислен в эксперте и расцениваться как сигнал на покупку (точка А - закрыть Sell и открыть Buy) или продажу (точка В - закрыть Buy и открыть Sell).


Рис. 131. Пересечения линий пользовательского индикатора рассматривается как торговый критерий.

При решении подобных задач можно использовать готовый эксперт, изменив в нём порядок расчёта торговых критериев. В данном случае можно взять за основу эксперт tradingexpert.mq4, представленный в разделе Простой эксперт. Эксперт shared.mq4, вычисляющий торговые критерии на основе пользовательского индикатора, будет выглядеть так:

//--------------------------------------------------------------------
// shared.mq4
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
#property copyright "Copyright © Book, 2007"
#property link "http://AutoGraf.dp.ua"
//--------------------------------------------------------------- 1 --
// М15
extern double StopLoss =100; // SL для открываемого ордера
extern double TakeProfit=35; // ТР для открываемого ордера
extern double Lots =0.1; // Жестко заданное колич. лотов
extern double Prots =0.07; // Процент свободных средств
//-------------------------------------------------------------- 1a --
extern int Period_MA_1 =56; // Период расчётной МА
extern int Bars_V =34; // Колич.баров для расчёта скор
extern int Aver_Bars =0; // Колич. баров для сглаживания
extern double Level =0.001;
//-------------------------------------------------------------- 1b --
bool Work=true; // Эксперт будет работать.
string Symb; // Название финанс. инструмента
//--------------------------------------------------------------- 2 --
int start()
{
int
Total, // Количество ордеров в окне
Tip=-1, // Тип выбран. ордера (B=0,S=1)
Ticket; // Номер ордера
double
MA_1_t, // Значен. МА_1 текущее
MA_2_t, // Значен. МА_2 текущее
Lot, // Колич. лотов в выбран.ордере
Lts, // Колич. лотов в открыв.ордере
Min_Lot, // Минимальное количество лотов
Step, // Шаг изменения размера лота
Free, // Текущие свободные средства
One_Lot, // Стоимость одного лота
Price, // Цена выбранного ордера
SL, // SL выбранного ордера
TP; // TP выбранного ордера
bool
Ans =false, // Ответ сервера после закрытия
Cls_B=false, // Критерий для закрытия Buy
Cls_S=false, // Критерий для закрытия Sell
Opn_B=false, // Критерий для открытия Buy
Opn_S=false; // Критерий для открытия Sell
//--------------------------------------------------------------- 3 --
// Предварит.обработка
if(Bars < Period_MA_1) // Недостаточно баров
{
Alert("Недостаточно баров в окне. Эксперт не работает.");
return; // Выход из start()
}
if(Work==false) // Критическая ошибка
{
Alert("Критическая ошибка. Эксперт не работает.");
return; // Выход из start()
}
//--------------------------------------------------------------- 4 --
// Учёт ордеров
Symb=Symbol(); // Название фин.инстр.
Total=0; // Количество ордеров
for(int i=1; i<=OrdersTotal(); i++) // Цикл перебора ордер
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // Если есть следующий
{ // Анализ ордеров:
if (OrderSymbol()!=Symb)continue; // Не наш фин. инструм
if (OrderType()>1) // Попался отложенный
{
Alert("Обнаружен отложенный ордер. Эксперт не работает.");
return; // Выход из start()
}
Total++; // Счётчик рыночн. орд
if (Total>1) // Не более одного орд
{
Alert("Несколько рыночных ордеров. Эксперт не работает.");
return; // Выход из start()
}
Ticket=OrderTicket(); // Номер выбранн. орд.
Tip =OrderType(); // Тип выбранного орд.
Price =OrderOpenPrice(); // Цена выбранн. орд.
SL =OrderStopLoss(); // SL выбранного орд.
TP =OrderTakeProfit(); // TP выбранного орд.
Lot =OrderLots(); // Количество лотов
}
}
//--------------------------------------------------------------- 5 --
// Торговые критерии
int H= 1000; // Колич.баров в расчётной истории
int P= Period_MA_1; // Период расчётной МА
int B= Bars_V; // Колич.баров для расчёта скорост
int A= Aver_Bars; // Колич. баров для сглаживания
//-------------------------------------------------------------- 5a --
double L_1=iCustom(NULL,0,"rocseparate",H,P,B,A,1,0);
double L_5=iCustom(NULL,0,"rocseparate",H,P,B,A,5,0);
//-------------------------------------------------------------- 5b --
if (L_5<=-Level && L_1>L_5)
{
Opn_B=true; // Критерий откр. Buy
Cls_S=true; // Критерий закр. Sell
}
if (L_5>=Level && L_1<L_5)
{
Opn_S=true; // Критерий откр. Sell
Cls_B=true; // Критерий закр. Buy
}
//--------------------------------------------------------------- 6 --
// Закрытие ордеров
while(true) // Цикл закрытия орд.
{
if (Tip==0 && Cls_B==true) // Открыт ордер Buy..
{ //и есть критерий закр
Alert("Попытка закрыть Buy ",Ticket,". Ожидание ответа..");
RefreshRates(); // Обновление данных
Ans=OrderClose(Ticket,Lot,Bid,2); // Закрытие Buy
if (Ans==true) // Получилось :)
{
Alert ("Закрыт ордер Buy ",Ticket);
break; // Выход из цикла закр
}
if (Fun_Error(GetLastError())==1) // Обработка ошибок
continue; // Повторная попытка
return; // Выход из start()
}

if (Tip==1 && Cls_S==true) // Открыт ордер Sell..
{ // и есть критерий закр
Alert("Попытка закрыть Sell ",Ticket,". Ожидание ответа..");
RefreshRates(); // Обновление данных
Ans=OrderClose(Ticket,Lot,Ask,2); // Закрытие Sell
if (Ans==true) // Получилось :)
{
Alert ("Закрыт ордер Sell ",Ticket);
break; // Выход из цикла закр
}
if (Fun_Error(GetLastError())==1) // Обработка ошибок
continue; // Повторная попытка
return; // Выход из start()
}
break; // Выход из while
}
//--------------------------------------------------------------- 7 --
// Стоимость ордеров
RefreshRates(); // Обновление данных
Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Миним. колич. лотов
Free =AccountFreeMargin(); // Свободн средства
One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// Стоимость 1 лота
Step =MarketInfo(Symb,MODE_LOTSTEP); // Шаг изменен размера

if (Lots > 0) // Если заданы лоты,то
Lts =Lots; // с ними и работаем
else // % свободных средств
Lts=MathFloor(Free*ProtsOne_LotStep)*Step;// Для открытия

if(Lts < Min_Lot) Lts=Min_Lot; // Не меньше минимальн
if (Lts*One_Lot > Free) // Лот дороже свободн.
{
Alert(" Не хватает денег на ", Lts," лотов");
return; // Выход из start()
}
//--------------------------------------------------------------- 8 --
// Открытие ордеров
while(true) // Цикл закрытия орд.
{
if (Total==0 && Opn_B==true) // Открытых орд. нет +
{ // критерий откр. Buy
RefreshRates(); // Обновление данных
SL=Bid - New_Stop(StopLoss)*Point; // Вычисление SL откр.
TP=Bid + New_Stop(TakeProfit)*Point; // Вычисление TP откр.
Alert("Попытка открыть Buy. Ожидание ответа..");
Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP);//Открытие Buy
if (Ticket > 0) // Получилось :)
{
Alert ("Открыт ордер Buy ",Ticket);
return; // Выход из start()
}
if (Fun_Error(GetLastError())==1) // Обработка ошибок
continue; // Повторная попытка
return; // Выход из start()
}
if (Total==0 && Opn_S==true) // Открытых орд. нет +
{ // критерий откр. Sell
RefreshRates(); // Обновление данных
SL=Ask + New_Stop(StopLoss)*Point; // Вычисление SL откр.
TP=Ask - New_Stop(TakeProfit)*Point; // Вычисление TP откр.
Alert("Попытка открыть Sell. Ожидание ответа..");
Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP);//Открытие Sel
if (Ticket > 0) // Получилось :)
{
Alert ("Открыт ордер Sell ",Ticket);
return; // Выход из start()
}
if (Fun_Error(GetLastError())==1) // Обработка ошибок
continue; // Повторная попытка
return; // Выход из start()
}
break; // Выход из while
}
//--------------------------------------------------------------- 9 --
return; // Выход из start()
}
//-------------------------------------------------------------- 10 --
int Fun_Error(int Error) // Ф-ия обработ ошибок
{
switch(Error)
{ // Преодолимые ошибки
case 4: Alert("Торговый сервер занят. Пробуем ещё раз..");
Sleep(3000); // Простое решение
return(1); // Выход из функции
case 135:Alert("Цена изменилась. Пробуем ещё раз..");
RefreshRates(); // Обновим данные
return(1); // Выход из функции
case 136:Alert("Нет цен. Ждём новый тик..");
while(RefreshRates()==false) // До нового тика
Sleep(1); // Задержка в цикле
return(1); // Выход из функции
case 137:Alert("Брокер занят. Пробуем ещё раз..");
Sleep(3000); // Простое решение
return(1); // Выход из функции
case 146:Alert("Подсистема торговли занята. Пробуем ещё..");
Sleep(500); // Простое решение
return(1); // Выход из функции
// Критические ошибки
case 2: Alert("Общая ошибка.");
return(0); // Выход из функции
case 5: Alert("Старая версия терминала.");
Work=false; // Больше не работать
return(0); // Выход из функции
case 64: Alert("Счет заблокирован.");
Work=false; // Больше не работать
return(0); // Выход из функции
case 133:Alert("Торговля запрещена.");
return(0); // Выход из функции
case 134:Alert("Недостаточно денег для совершения операции.");
return(0); // Выход из функции
default: Alert("Возникла ошибка ",Error); // Другие варианты
return(0); // Выход из функции
}
}
//-------------------------------------------------------------- 11 --
int New_Stop(int Parametr) // Проверка стоп-прик.
{
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Миним. дистанция
if (Parametr<Min_Dist) // Если меньше допуст.
{
Parametr=Min_Dist; // Установим допуст.
Alert("Увеличена дистанция стоп-приказа.");
}
return(Parametr); // Возврат значения
}
//-------------------------------------------------------------- 12 --

Рассмотрим, какие преобразования внесены в исходный код (tradingexpert.mq4). Значительная часть кода эксперта, взятого за основу, не изменилась. Изменения внесены в два блока - блок 1-2 и блок 5-6.

В блоке 5-6 рассчитываются торговые критерии. В представленном эксперте торговая стратегия построена на основе всего двух торговых критериев - критерия открытия Buy и критерия открытия Sell. Принятая в эксперте стратегия предусматривает возможность одновременного присутствия в торговле только одного рыночного ордера, отложенные ордера не допускаются. Стратегия предполагает также закрытие противоположного ордера при срабатывании критерия открытия; например, если значимым является критерий для открытия Buy, то это также означает, что необходимо закрыть Sell.

Для того, чтобы в эксперте shared.mq4 использовать результаты вычислений, выполненных в пользовательском индикаторе rocseparate.mq4, необходимо исполнить функцию iCustom():

   double L_1 = iCustom(NULL,0,"rocseparate",H,P,B,A,1,0);
double L_5 = iCustom(NULL,0,"rocseparate",H,P,B,A,5,0);

В данном случае формальные параметры, указанные в вызове функции iCustom(), означают следующее:

NULL - расчёты в индикаторе осуществляются на основе данных по текущему финансовому инструменту; в данном случае эксперт прикреплён в оно EURUSD, значит, будут использованы данные по EURUSD (см. Рис. 131);

0 - при расчётах используются данные, соответствующие текущему таймфрейму; в данном случае текущий таймфрейм М15, значит, будут использованы данные, соответствующие М15;

"rocseparate" - название пользовательского индикатора, в котором будут выполнены расчёты.

H,P,B и A - список настраиваемых параметров. В данном случае пользовательский индикатор rocseparate.mq4 имеет настраиваемые параметры (блок 2-3 в коде rocseparate.mq4). Для того чтобы предоставить пользователю возможность настраивать значения этих параметров из эксперта, они указаны в списке передаваемых параметров функции iCustom(). В эксперте могут быть определены значения этих параметров, не совпадающие по значению с теми, что указаны в индикаторе, и при выполнении расчётов в индикаторе будут использоваться именно эти переданные значения. Эти параметры означают следующее:

H - количество баров в расчётной истории;

P - период расчётной МА;

B - количество баров для расчёта скорости;

A - количество баров для сглаживания.

(подробное объяснение сущности указанных параметров см. в разделе Пользовательский индикатор ROC).

1 (5) - индекс линии индикатора. В пользовательском индикаторе rocseparate.mq4 используется 6 индикаторных массивов. Линия скорости изменения цены на текущем таймфрейме (оранжевая) строится на основе значений индикаторного массива Line_1[], для которого используется буфер с индексом 1. Средняя сглаженная линия скорости строится на основе значений элементов массива Line_5[], индекс используемого буфера 5.

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

Для того, чтобы предоставить пользователю возможность вручную в эксперте изменять настраиваемые параметры индикатора, в блоке 1а-1b (эксперта) указаны внешние переменные. В блоке 5-5а значения этих настраиваемых параметров переприсвоены другим переменным с более короткими именами - это сделано для удобства представления кода в блоке 5а-5b. Таким образом, пользователь может по своему усмотрению указать в эксперте shared.mq4 параметры, при которых будут производиться расчёты в пользовательском индикаторе rocseparate.mq4. После исполнения функция iCustom() вернёт значение, соответствующее значению заданного элемента индикаторного массива, вычисленного в заданном индикаторе при заданных значениях настраиваемых параметров.

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

Для исполнения функции iCustom() не требуется, чтобы соответствующий технический индикатор был прикреплён к окну финансового инструмента. Также и вызов функции iCustom() из какой-либо прикладной программы не приводит к присоединению соответствующего индикатора к окну финансового инструмента. Присоединение технического индикатора к окну финансового инструмента тоже не приводит к вызову функции iCustom() в какой-либо прикладной программе.

Торговые критерии в эксперте (блок 5-6) вычисляются на основе значений элементов массивов, полученных с помощью функции Custom(). Например, критерий для открытия Buy и закрытия Sell вычисляется так:

    if (L_5<=-Level && L_1>L_5)
{
Opn_B = true; // Критерий откр. Buy
Cls_S = true; // Критерий закр. Sell
}

Если последнее известное значение средней сглаженной линии скорости изменения цены (L_5) меньше заданного уровня (значение настраиваемого параметра Level = 0.001) и при этом последнее известное значение линии скорости изменения цены в текущем таймфрейме (L_1) больше средней сглаженной линии скорости цены (L_5), то значимыми являются критерий для открытия ордера Buy и критерий закрытия ордера Sell. Для подтверждения значимости противоположных критериев используются зеркальные условия.

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