КАК ЗАРАБОТАТЬ НА ФОРЕКСЕ
Пятница, 03.05.2024, 10:06
Приветствую Вас Гость | RSS
 
Главная Учимся программировать на языке MQL4-Урок 16 - Ваш первый советник (часть 3)РегистрацияВход
<a href="http://instaforex.com/ru/">Форекс портал</a>
Меню сайта
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Урок 16 - Ваш первый советник (часть 3)

Привет! Соскучились? Надеюсь, Вам хватило времени познакомиться с торговыми функциями MQL4, потому что сегодня они нам проигодятся.
В предыдущих двух частях этого урока мы познакомились с нашим первым советником и поняли, какая идея за ним скрывается. Сегодня мы продолжим разбирать код нашего советника.
Итак, начнём.

Код, который мы уже имеем:

Код:
//+------------------------------------------------------------------+
//| My_First_EA.mq4 |
//| Kirill |
//| StockProgrammer@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link "StockProgrammer@mail.ru"

//---- input parameters
extern double TakeProfit=350.0;
extern double Lots=0.1;
extern double TrailingStop=35.0;
//+------------------------------------------------------------------+
//| expert initialization function |
//+------------------------------------------------------------------+
int init()
{
//----

//----
return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----

//----
return(0);
}

int Crossed (double line1 , double line2)
{
static int last_direction = 0;
static int current_dirction = 0;

if(line1>line2)current_dirction = 1; //up
if(line1<line2)current_dirction = 2; //down

if(current_dirction != last_direction) //changed
{
last_direction = current_dirction;
return (last_direction);
}

else
{
return (0);
}

}

//+------------------------------------------------------------------+
//| expert start function |
//+------------------------------------------------------------------+

int start()
{
//----
int cnt, ticket, total;
double shortEma, longEma;

if(Bars<100)
{
Print("bars less than 100");
return(0);
}

if(TakeProfit<10)
{
Print("TakeProfit less than 10");
return(0); // check TakeProfit
}

shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);

int isCrossed = Crossed (shortEma,longEma);

total = OrdersTotal();
if(total < 1)
{
if(isCrossed == 1)
{
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point, "My EA",12345,0,Green);
if(ticket>0)
{
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("BUY order opened : ",OrderOpenPrice());
}
else Print("Error opening BUY order : ",GetLastError());
return(0);
}

if(isCrossed == 2)
{
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,
Bid-TakeProfit*Point,"My EA",12345,0,Red);
if(ticket>0)
{
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("SELL order opened : ",OrderOpenPrice());
}
else Print("Error opening SELL order : ",GetLastError());
return(0);

}

return(0);
}

for(cnt=0;cnt<total;cnt++)
{
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if(OrderType()<=OP_SELL && OrderSymbol()==Symbol())
{
if(OrderType()==OP_BUY) // long position is opened
{
// should it be closed?
if(isCrossed == 2)
{
OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
return(0); // exit
}

// check for trailing stop
if(TrailingStop>0)
{
if(Bid-OrderOpenPrice()>Point*TrailingStop)
{
if(OrderStopLoss()<Bid-Point*TrailingStop)
{
OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
return(0);
}
}
}
}

else // go to short position
{
// should it be closed?
if(isCrossed == 1)
{
OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
return(0); // exit
}

// check for trailing stop
if(TrailingStop>0)
{
if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
{
if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
{
OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
return(0);
}
}
}
}
}
}

return(0);
}

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

Последее, что мы разобрали в прошлый раз, - это функция Crossed(). Таким образом, мы подошли вплотную к функции start().

Код:
int cnt, ticket, total;
В этой строчке мы объявили три переменные типа integer. Мы смогли объявить все три переменные в одной строчке, потому что они все одинакого типа.

Замечание: Чтобы объявить несколько переменных в одной строчке, необходимо начать эту строчку с названия их типа, затем перечислить идентификаторы переменных через запятую.

С тем же успехом можно было разбить эту строчку на три части:

Код:
int cnt;

int ticket;

int total;
Переменную cnt мы будем использовать в качестве счётчика в нашем "цикле просмотра открытых позиций".
Переменную ticket мы будем использовать для хранения тикета (идентификационного номера ордера), возвращаемого функцией OrderSend().
Переменную total мы будем использовать для хранения числа уже открытых ордеров.

Код:
double shortEma, longEma;
Опять же, мы объявили две переменные в одной строчке.
Мы будем использовать эти переменные для хранения значений быстрой EMA и медленной EMA.
Я надеюсь, Вы помните из предыдущей части этого урока, что мы должны следить за их пересечениями и взаиморасположениями. Исходя из этой информации мы будем принимать решения об открытии ордеров на покупку или продажу, а также их закрытии.

Код:
 if(Bars<100)
{
Print("bars less than 100");
return(0);
}
Мы предполагаем, что график, с которым мы работаем, имеет больше 100 баров. Если это не так, то индикаторы EMA не смогут функционирывать.
Число баров на графике хранится в переменной Bars. С помощью этой переменной мы проверяем количество баров на графике в данный момент и, если оно меньше 100, мы сделаем 2 вещи: во-первых сообщим пользователю, напечатав в лог эксперта сообщение "bars less than 100", во-вторых мы завершим функцию start() оператором return(0);
Таким образом, мы отказываемся работать, если баров на графике меньше 100.

Код:
 if(TakeProfit<10)
{
Print("TakeProfit less than 10");
return(0); // check TakeProfit
}
Также мы не можем работать с неработоспособным значением параметра TakeProfit.
Переменная TakeProfit является внешней (extern), что означает, что пользователь может изменить её в окошке настроек советника.
Мы хотим защитить пользователя нашего советника от его же ошибок. Короче говоря, сделать программу дуракоустойчивой ;)
Мы предполагаем, что любое значение меньше 10 для переменной TakeProfit является плохим выбором. Поэтому мы проверяем значение, заданное пользователем, и сравниваем его с 10. Если окажется, что значение меньше 10, мы сообщим об этом пользователю, распечатав сообщение "TakeProfit less than 10", и завершим функцию start() оператором return(0);

Код:
 shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);
Если всё в порядке: на графике больше 100 баров, и значение TakeProfit больше либо равно 10 - мы можем перейти к расчёту значений EMA на текущем баре.

Мы воспользуемся встроенным в MQL4 индикатором iMA, который рассчитает скользящие среднии.

Здесь нам придётся немного остановиться, чтобы поподробнее узнать о функции iMA.

iMA

Синтаксис:
double iMA( string symbol, int timeframe, int period, int ma_shift, int ma_method, int applied_price, int shift)

Описание:
Расчет скользящего среднего.

Параметры:
symbol - Символьное имя инструмента, на данных которого будет вычисляться индикатор. NULL означает текущий символ.
timeframe - Период. Может быть одним из периодов графика. 0 означает период текущего графика.
period - Период усреднения для вычисления скользящего среднего.
ma_shift - Сдвиг индикатора относительно ценового графика.
ma_method - Метод усреднения. Может быть любым из значений методов скользящего среднего (Moving Average).
applied_price - Используемая цена. Может быть любой из ценовых констант.
shift - Индекс получаемого значения из индикаторного буфера (сдвиг относительно текущего бара на указанное количество периодов назад).

Периоды графиков:


Методы скользящих:


Ценовые константы:


Всю эту информацию можно получить во встроенном справочнике MQL4. Достаточно лишь в окне Навигатор во вкладке Поиск набрать: iMA.

Код:
 shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0);
longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);
Теперь Вы понимаете, что означают эти строчки.
Переменной shortEma мы присвоили значение, равное:
Усреднённую методом экспоненциального среднего цену Close последних 8 баров, начиная с текущего.
Коротко можно говорить EMA8.
Переменной longEma мы присвоили значение, равное:
Усреднённую методом экспоненциального среднего цену Close последних 13 баров, начиная с текущего.
Коротко можно говорить EMA13.

Код:
int isCrossed = Crossed (shortEma,longEma);
Уверен, что Вы помните, что функция Crossed() принимает две переменные типа double и возвращает integer.
Первый параметр - это тек. значение первой линии, за которой мы хоти наблюдать. Второй - второй линии.
Функция будет следить за двумя линиями при каждом её вызове посредством сохранения их тек. значений в статических переменных, чтобы запоминать их предыдущее состояние.
- Функция возвращает 0, если взаиморасположение линий не изменилось.
- Функция возвращает 1, если взаиморасположение линий изменилось, и первая линия оказалась над второй.
- Функция возвращает 2, если взаиморасположение линий изменилось, и первая линия оказалась под второй.

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

Код:
 total = OrdersTotal();
if(total < 1)
{

...

}
Мы присвоили переменной total возвращаемое значение функции OrdersTotal().

Замечание: Функция OrdersTotal() возвращает общее количество открытых и ожидающих ордеров (см. урок 15).

Следующий за этим блок if сработает, только если нет ни одного открытого или ждущего ордера.

Блок if:

Код:
 {
if(isCrossed == 1)
{
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point, "My EA",12345,0,Green);
if(ticket>0)
{
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("BUY order opened : ",OrderOpenPrice());
}
else Print("Error opening BUY order : ",GetLastError());
return(0);
}

if(isCrossed == 2)
{
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point,"My EA",12345,0,Red);
if(ticket>0)
{
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("SELL order opened : ",OrderOpenPrice());
}
else Print("Error opening SELL order : ",GetLastError());
return(0);

}
В случае, когда EMA8 пересекла EMA13 снизу вверх, мы производим покупку. Для этого мы используем функцию OrderSend() (см. урок 15).

OrderSend()

Синтаксис:
int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)

Параметры:
symbol
- Мы воспользовались функцией Symbol(), чтобы получить строку с названием валюты, и передали её (строку) функции OrderSend().
cmd
- Мы использовали OP_BUY, чтобы открыть сделку на покупку.
volume
- Мы использовали переменную Lots, значение которой нам предоставил пользователь.
price
- Мы воспользовались переменной (на самом деле функцией) Ask, чтобы получить тек. значение цены ask, и передали его функции OrderSend().
slippage
- Мы использовали 3 в качестве значения допустимого проскальзывания.
stoploss
- Мы использовали 0 в качествет стоп-лосса, что означает, что у этого ордера нет стоп-лосса.
takeprofit
- Мы воспользовались переменной TakeProfit, значение которой нам предоставил пользователь. Умножили её на цену одного пункта, хранящуюся в переменной Point, и прибавили результат к тек. значению Ask (цена открытия ордера). Это Важный момент обратите на него внимание. Аналогичным образом надо рассчитывать стоп-лосс, если он не равен нулю.
comment
- Мы использовали строку "My EA" в качестве комментария.
magic
- Мы испольлзовали число 12345 в качестве magic-числа. Магическое число нужно только для того, чтобы советник отличал свои ордера от чужих.
expiration
- Мы использовали 0, потому что нашему ордеру не нужен срок истечения. Этот параметр применим только к отложенным ордерам.
arrow_color
- Мы выбрали зелёный, потому что мы любим деньги, а деньги зелёные ;)

Функция OrderSend() при успешном исполнении возвращает тикет открытого ордера, что мы используем в проверке:

Код:
if(ticket>0)
Затем мы функцией OrderSelect() выбираем открытый ордер по его тикету, и функцией OrderOpenPrice() узнаём цену его открытия.

Если всё прошло успешно, и функция OrderSend() вернула нормальный тикет (больший 0), а функция OrderSelect() благополучно выбрала нужный ордер, то в лог эксперта распечатается сообщение "BUY order opened : " + цена открытия.
Для более подробной информации о OrderSelect() и OrderOpenPrice() см. урок 15.

Иначе, если функция OrderSend() вернула -1, что означает, что было ошибка, мы должны об этом сообщить пользователю, выдав сообщение: :Error opening BUY order: " + номер ошибки, предоставленный функцией GetLastError(). При этом нам придётся завершить функцию start() опреатором return(0);

Код:
 if(isCrossed == 2)
{
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,
Bid-TakeProfit*Point,"My EA",12345,0,Red);
if(ticket>0)
{
if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
Print("SELL order opened : ",OrderOpenPrice());
}
else Print("Error opening SELL order : ",GetLastError());
return(0);

}
Здесь сценарий противоположный - EMA8 пересекает EMA13 сверху вниз. Теперь мы продаём.
Можете найти различия в вызовах функции OrderSend() ?
Правильно!

Следующие параметры не изменились:

symbol
volume
slippage
stoploss
comment
magic
expiration


А эти изменились:

cmd
- Мы использовали OP_SELL, чтобы открыть сделку на продажу.
price
- Мы воспользовались переменной (на самом деле функцией) Bid, чтобы получить тек. значение цены bid, и передали его функции OrderSend().
takeprofit
- Мы воспользовались переменной TakeProfit, значение которой нам предоставил пользователь. Умножили её на цену одного пункта, хранящуюся в переменной Point, и вычли результат к тек. значению Bid (цена открытия ордера).
arrow_color
- Мы выбрали красный, потому что мы любим деньги, а деньги зелёные, но нам нужен другой цвет для позиций на продажу ;)

Далее в этом куске кода всё аналогично.
Вот и всё... Мы закончили с блоком if(total<1). В следующ...

- Постой!

- Что?

- А я заметил ещё одну строчку в этом блоке, которую мы не разобрали!

- Где?

- Вот она:

Код:
 return(0);
- Отлично!
Посмотрите внимательно на эти строчки:

Код:
 if(total < 1)
{
if(isCrossed == 1)
{

...

}

if(isCrossed == 2)
{

...

}

return(0); // <<-- Вот она
}
Если EMA8 пересекает EMA13 снизу вверх, то мы покупаем.
Если EMA8 пересекает EMA13 сверху вниз, то мы продаём.
А что если они ещё не пересеклись? За это отвечает данная строчка. Она прервёт функцию start(), если ещё не было пересечения (в случае, если переменная isCrossed равна 1 или 2).
Теперь мы знаем, как наш советник открывает сделки. В последней части урока мы рассмотрим остаток кода и обсудим детали Тестера Стратегий MetaTrader.

Надеюсь, этот урок Вам понравился.
Поиск
<a href="http://instaforex.com/ru/">Форекс портал</a>
Copyright MyCorp © 2024
Бесплатный конструктор сайтов - uCoz