|
|
Заработок на Рынке FOREX и РТС Сайтах . | Приветствую Вас Гость | RSS |
Форекс портал
Программирования ЕА на MQL5
| |
Шрус | Дата: Среда, 02.08.2017, 16:47 | Сообщение # 1 |
Генералиссимус
Группа: Администраторы
Сообщений: 8006
Статус: Offline
| Краткий курс программирования ЕА на MQL5 без ООП и стандартной библиотеки
Этот курс предназначен для начинающих знакомство с языком программирования MQL5. Надеюсь, с азами программирования вы знакомы. Знаете что такое переменная, типы переменных, видимость переменных. Знаете что такое функция, пользовательская функция и прочие минимальные знания программирования вам не чужды. Для примера напишем советник по избитой теме торговли, основанную на пересечении двух индикаторов Moving Average.
Итак, начинаем: Также надеюсь, знаете, как создать новый файл будущего советника. Меню «Файл» затем «Создать» отметить, что создаём советник и далее. Либо через контекстное меню «Новый файл»... и так далее. MetaEditor в помощь нам, создаёт такой код.
Код //+------------------------------------------------------------------+ //| MyFirstEA.mq5 | //| Viktorov | //| v4forex@yandex.ru | //+------------------------------------------------------------------+ #property copyright "Viktorov" #property link "v4forex@yandex.ru" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
Теперь начинаем вставлять свой код. Для начала нам надо задать параметры торговли и используемых индикаторов. Обращение к индикаторам в MQL5 происходит не так как в MQL4, поэтому надо объявить переменные, в которых будем хранить хендлы индикаторов. А поскольку индикаторов у нас два, то и хендлов должно быть два. Затем в функции int OnInit() получим хендлы необходимых индикаторов.
В итоге наш код будет выглядеть уже вот так.
Код //+------------------------------------------------------------------+ //| MyFirstEA.mq5 | //| Viktorov | //| v4forex@yandex.ru | //+------------------------------------------------------------------+ #property copyright "Viktorov" #property link "v4forex@yandex.ru" #property version "1.00"
//---- input parameters input double lot = 0.1; // Размер лота input int tacke = 400; // TackeProfit input int loss = 200; // StopLoss input int periodFastMa = 8; // Период усреднения быстрой МА input ENUM_MA_METHOD metodFastMa = 1; // Метод усреднения быстрой МА input ENUM_APPLIED_PRICE priceFastMA = 1; // Используемая цена быстрой МА input int periodSlowMa = 13; // Период усреднения медленной МА input ENUM_MA_METHOD metodSlowMa = 1; // Метод усреднения медленной МА input ENUM_APPLIED_PRICE priceSlowMA = 1; // Используемая цена медленной МА
int handleFastMA // хендл быстрой МА , handleSlowMA; // хендл медленной МА
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- handleFastMA = iMA(_Symbol, PERIOD_CURRENT, periodFastMa, 0, metodFastMa, priceFastMA); handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, periodSlowMa, 0, metodSlowMa, priceSlowMA); if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE) return(INIT_FAILED); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
Теперь надо как-то определить, что произошло пересечение линий индикаторов. Для этого напишем пользовательскую функцию определяющую факт пересечения, назовём её Crossing. Возвращать функция будет 0, 1 или -1 следовательно тип функции будет int.
Код int Crossing() { double fastMA[2], slowMA[2]; CopyBuffer(handleFastMA, 0, 1, 2, fastMA); CopyBuffer(handleSlowMA, 0, 1, 2, slowMA); if(fastMA[0] < slowMA[0] && fastMA[1] > slowMA[1]) return(0); if(fastMA[0] > slowMA[0] && fastMA[1] < slowMA[1]) return(1);
return(-1); }/*******************************************************************/
В данной функции в заранее объявленные массивы копируются значения индикаторов на первом и втором барах. Затем сравнивается положение линий. Если на втором баре fastMA была ниже slowMA а на первом стало наоборот, следовательно, быстрая МА пересекла медленную МА снизу вверх. И наоборот, если на втором баре fastMA была выше slowMA а стало наоборот, то быстрая МА пересекла медленную МА сверху вниз. Поместим её в самый конец нашего кода. Теперь самое время попытаться получить результат вызова этой функции. Вставим в функцию void OnTick() вызов нашей функции и примем значение в переменную int isCrossed. А для контроля вместо открытия позиции просто напечатаем текст "Откроем позицию Buy" или "Откроем позицию Sell".
Теперь код функции OnTick() выглядит так
Код //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int isCrossed = Crossing(); if(isCrossed == 0) Print("Откроем позицию Buy"); if(isCrossed == 1) Print("Откроем позицию Sell"); } //+------------------------------------------------------------------+
Но вот незадача… Получается что пересечение действительно на каждом тике пока не закроется текущий бар… Следовательно откроется несколько позиций подряд. Чтобы это предотвратить надо организовать открытие позиций только один раз на баре в момент его первого тика. Для этого напишем ещё одну пользовательскую функцию.
Выглядит она так.
Код bool newBar() { static datetime timeLastBar; MqlRates mqlRates[]; int s = 0; do { s++; } while(CopyRates(_Symbol, PERIOD_CURRENT, 0, 1, mqlRates) < 0 && s < 7); bool ret = timeLastBar != mqlRates[0].time; if(ret) timeLastBar = mqlRates[0].time; return(ret); }/*******************************************************************/
В этой функции объявлена статическая static переменная типа datetime timeLastBar которая хранит время открытия бара на котором было последнее обращение к этой функции. Для определения времени открытия текущего бара будем использовать функцию CopyRates() и помещать результат в массив структур типа MqlRates. Затем полученное время последнего бара сравнивается с временем хранящимся в переменной timeLastBar и если оно изменилось в переменную запишется время последнего бара и функция вернёт true. Если-же время не изменилось, возвращается false. Поместим его опять-же в самый конец нашего кода и исправим вызов функции Crossing()
Таким образом, вызов функции выглядит так.
Код //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int isCrossed = Crossing(); if(newBar()) { if(isCrossed == 0) Print("Откроем позицию Buy"); if(isCrossed == 1) Print("Откроем позицию Sell"); } }
Теперь можно запустить код в тестере стратегий и понаблюдать когда в журнале будет печататься наши сообщения.
|
|
| |
Шрус | Дата: Среда, 02.08.2017, 17:01 | Сообщение # 2 |
Генералиссимус
Группа: Администраторы
Сообщений: 8006
Статус: Offline
| Краткий курс программирования ЕА на MQL5 без ООП и стандартной библиотеки
И вот теперь самое трудное и новое. Будем писать функции открытия и модификации позиций. Начнём с того, что определим имя функции, типы и имена входящих параметров, объявим и обнулим переменные структур которые используются в функции проверки OrderCheck( ) и функции запроса OrderSend()
Код bool openPosition(ENUM_ORDER_TYPE type // Тип ордера , double volume // Запрашиваемый объем сделки в лотах , int sl // Stop Loss ордера в пунктах , int tp // Take Profit ордера в пунктах , int magic = 0 // MagickNumber ордера , string comm = NULL // комментарий ордера/позиции ) { MqlTradeCheckResult checkResult; ZeroMemory(checkResult); MqlTradeRequest request; ZeroMemory(request); MqlTradeResult tradeResult; ZeroMemory(tradeResult); return(false); }/*******************************************************************/
Затем объявим переменную для хранения актуальных цен и заполним структуру mqlTick.
Код MqlTick mqlTick; int s = 0; do { s++; } while(!SymbolInfoTick(_Symbol, mqlTick) && s < 7); double price = type == ORDER_TYPE_BUY ? mqlTick.ask : mqlTick.bid; double stop = type == ORDER_TYPE_BUY ? price-(sl*_Point) : price+(sl*_Point) , take = type == ORDER_TYPE_BUY ? price+(tp*_Point) : price-(tp*_Point);
Получим это свойство в переменную типа перечисления.
Код ENUM_SYMBOL_TRADE_EXECUTION exec = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE);
Теперь самое время заполнять структуру запроса. От режима заключения сделок зависит не только поле type_filling но и как заполнять поля sl и tp Если тип исполнения Market то стопы и тейки можно устанавливать только после открытия позиции посредством модификации позиции. Соответственно при заполнении учтём этот факт.
Заполнение структуры:
Код request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = volume; request.price = price; request.sl = exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : sl == 0 ? 0.0 : NormalizeDouble(stop, _Digits); request.tp = exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : tp == 0 ? 0.0 : NormalizeDouble(take, _Digits); request.deviation = 555; request.type = type; request.type_filling = exec == SYMBOL_TRADE_EXECUTION_MARKET ? ORDER_FILLING_FOK : ORDER_FILLING_RETURN; request.magic = magic; request.comment = comm;
ВСЁ… Теперь проверим достаточность средств и правильность заполнения структуры запроса и отправим запрос на исполнение. И последнее, если тип исполнения Market то после открытия позиции надо будет её выбрать для дальнейшей работы с ней и установить Stop Loss и Take Profit. Так выглядит код открытия позиции.
Код bool openPosition(ENUM_ORDER_TYPE type // Тип ордера , double volume // Запрашиваемый объем сделки в лотах , int sl // Stop Loss ордера в пунктах , int tp // Take Profit ордера в пунктах , int magic = 0 // MagickNumber ордера , string comm = NULL // комментарий ордера/позиции ) { MqlTradeCheckResult checkResult; ZeroMemory(checkResult); MqlTradeRequest request; ZeroMemory(request); MqlTradeResult tradeResult; ZeroMemory(tradeResult); MqlTick mqlTick; int s = 0; do { s++; } while(!SymbolInfoTick(_Symbol, mqlTick) && s < 7); double price = type == ORDER_TYPE_BUY ? mqlTick.ask : mqlTick.bid; double stop = type == ORDER_TYPE_BUY ? price-(sl*_Point) : price+(sl*_Point) , take = type == ORDER_TYPE_BUY ? price+(tp*_Point) : price-(tp*_Point); ENUM_SYMBOL_TRADE_EXECUTION exec = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE); request.action = TRADE_ACTION_DEAL; request.symbol = _Symbol; request.volume = volume; request.price = price; request.sl = exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : sl == 0 ? 0.0 : NormalizeDouble(stop, _Digits); request.tp = exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : tp == 0 ? 0.0 : NormalizeDouble(take, _Digits); request.deviation = 555; request.type = type; request.type_filling = exec == SYMBOL_TRADE_EXECUTION_MARKET ? ORDER_FILLING_FOK : ORDER_FILLING_RETURN; request.magic = magic; request.comment = comm; if(!OrderCheck(request, checkResult)) Print(__FUNCTION__, " ", checkResult.retcode, " ", request.volume, " ", checkResult.margin_free-checkResult.margin); else { if(!OrderSend(request, tradeResult)) Print(__FUNCTION__, " ", tradeResult.retcode); if(exec == SYMBOL_TRADE_EXECUTION_MARKET) { int n = 0; do { n++; } while(!PositionSelectByTicket(tradeResult.order) && n < 5); sl_tp_Modification(stop, take, tradeResult.order); } return(true); } return(false); }/*******************************************************************/
Поскольку модификация позиций выполняется той-же функцией OrderSend() подробно разбирать её не имеет смысла. Отличие только в заполняемых полях структуры.
Так выглядит код модификации позиции.
Код bool sl_tp_Modification(double stopLoss , double takeProfit , ulong ticket ) { bool ret = false; MqlTradeRequest request; MqlTradeCheckResult checkResult; MqlTradeResult tradeResult; ZeroMemory(request); ZeroMemory(checkResult); ZeroMemory(tradeResult); if(PositionSelectByTicket(ticket)) { request.action = TRADE_ACTION_SLTP; request.symbol = PositionGetString(POSITION_SYMBOL); request.sl = stopLoss; request.tp = takeProfit; request.position = ticket; if(!OrderCheck(request, checkResult)) { Print("****** StopLoss ", stopLoss); Print("****** Take_Profit ", takeProfit); Print("****** ticket ", ticket); return(ret); } } ret = OrderSend(request, tradeResult); if(!ret) Print(__FUNCTION__, " ", tradeResult.retcode); return(ret); }/*******************************************************************/
Вот теперь можно внести изменения и вместо печати в журнал открывать позиции.
Код //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int isCrossed = Crossing(); if(newBar()) { if(isCrossed == 0) openPosition(ORDER_TYPE_BUY, lot, loss, tacke); if(isCrossed == 1) openPosition(ORDER_TYPE_SELL, lot, loss, tacke); } }
Замечание: Наш советник предназначен для обучения и не будет, не нацелен на извлечение прибыли.
Важно: Для правильного заполнения поля type_filling нам необходимо знать режимы заключения сделок по конкретному инструменту.
Скачать Полный код советника
|
|
| |
|
| |