//+-------------------------------------------------------------------------------------+ //| MorningFlat_OpenLevel_Expert.mq4 | //| Scriptong | //| scriptong@mail.ru | //+-------------------------------------------------------------------------------------+ #property copyright "Scriptong" #property link "scriptong@mail.ru" //---- input parameters extern int TakeProfit = 40; extern int StopLoss = 200; extern double Lots = 0.1; extern string B1 = "Время начала и окончания флэта с точностью до минут"; extern int StartHour = 0; extern int StartMinute = 0; extern int EndHour = 8; extern int EndMinute = 0; extern string B2 = "========================================================"; extern string OpenOrderSound = "ok.wav"; extern int MagicNumber = 10002; bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, OpenAfterFlat; int Signal, LastTicket; datetime LastChannel = 0, BeginDay, LastOpen; //+-------------------------------------------------------------------------------------+ //| expert initialization function | //+-------------------------------------------------------------------------------------+ int init() { FatalError = False; Activate = False; // - 1 - == Сбор информации об условиях торговли ======================================== Tick = MarketInfo(Symbol(), MODE_TICKSIZE); // минимальный тик Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов MinLot = MarketInfo(Symbol(), MODE_MINLOT); // минимальный разрешенный объем сделки MaxLot = MarketInfo(Symbol(), MODE_MAXLOT); // максимальный разрешенный объем сделки LotStep = MarketInfo(Symbol(), MODE_LOTSTEP); // шаг приращения объема сделки // - 1 - == Окончание блока ============================================================= // - 2 - == Приведение объема сделки к допустимому и проверка корректности объема ======= Lots = MathRound(Lots/LotStep)*LotStep; // округление объема до ближайшего допустимого if(Lots < MinLot || Lots > MaxLot) // объем сделки не меньше MinLot и не больше MaxLot { Comment("Параметром Lots был задан неправильный объем сделки! Советник отключен!"); return(0); } // - 2 - == Окончание блока ============================================================= // - 3 - == Проверка корректности входных параметров ==================================== if (TakeProfit <= StopLevel/Point && TakeProfit != 0) { Comment("Значение параметра TakeProfit должно быть больше ", StopLevel/Point, " пунктов."); Print("Значение параметра TakeProfit должно быть больше ", StopLevel/Point, " пунктов."); return(0); } if (StopLoss <= (StopLevel+Spread)/Point && StopLoss != 0) { Comment("Значение параметра StopLoss должно быть больше ",(StopLevel+Spread)/Point, " пунктов."); Print("Значение параметра StopLoss должно быть больше ", (StopLevel+Spread)/Point, " пунктов."); return(0); } if(StartHour < 0 || EndHour < 0 || StartHour > 23 || EndHour > 23 || StartHour > EndHour || (StartHour == EndHour && StartMinute >= EndMinute)) { Comment("Значения StartHour и EndHour должны лежать в диапазоне от 0 до 24 и ", "StartHour < EndHour."); Print("Значения StartHour и EndHour должны лежать в диапазоне от 0 до 24 и ", "StartHour < EndHour."); return(0); } if(StartMinute < 0 || EndMinute < 0 || StartMinute > 59 || EndMinute > 59) { Comment("Значения StartHour и EndHour должны лежать в диапазоне от 0 до 24 и ", "StartHour < EndHour."); Print("Значения StartHour и EndHour должны лежать в диапазоне от 0 до 24 и ", "StartHour < EndHour."); return(0); } // - 3 - == Окончание блока ============================================================= // - 4 - ============ Поиск своих позиций, открытых в течение текущего дня ============== LastOpen = 0; LastTicket = -1; for (int i = 0; i < OrdersTotal(); i++) if (OrderSelect(i, SELECT_BY_POS)) if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) { LastOpen = OrderOpenTime(); LastTicket = OrderTicket(); break; } if (LastOpen == 0) for (i = 0; i < OrdersHistoryTotal(); i++) if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) LastOpen = MathMax(LastOpen, OrderOpenTime()); // - 4 - =================================== Окончание блока ============================ Activate = True; //---- return(0); } //+-------------------------------------------------------------------------------------+ //| expert deinitialization function | //+-------------------------------------------------------------------------------------+ int deinit() { //---- Comment(""); //---- return(0); } //+-------------------------------------------------------------------------------------+ //| Приведение значений к точности одного тика | //+-------------------------------------------------------------------------------------+ double ND(double A) { return(NormalizeDouble(A, Digits)); } //+-------------------------------------------------------------------------------------+ //| Расшифровка сообщения об ошибке | //+-------------------------------------------------------------------------------------+ string ErrorToString(int Error) { switch(Error) { case 2: return("зафиксирована общая ошибка, обратитесь в техподдержку."); case 5: return("у вас старая версия терминала, обновите ее."); case 6: return("нет связи с сервером, попробуйте перезагрузить терминал."); case 64: return("счет заблокирован, обратитесь в техподдержку."); case 132: return("рынок закрыт."); case 133: return("торговля запрещена."); case 149: return("запрещено локирование."); } } //+-------------------------------------------------------------------------------------+ //| Ожидание торгового потока. Если поток свободен, то результат True, иначе - False | //+-------------------------------------------------------------------------------------+ bool WaitForTradeContext() { int P = 0; // цикл "пока" while(IsTradeContextBusy() && P < 5) { P++; Sleep(1000); } // ------------- if(P == 5) return(False); return(True); } //+-------------------------------------------------------------------------------------+ //| "Правильное" открытие позиции | //| В отличие от OpenOrder проверяет соотношение текущих уровней и устанавливаемых | //| Возвращает: | //| 0 - нет ошибок | //| 1 - Ошибка открытия | //| 2 - Ошибка значения Price | //| 3 - Ошибка значения SL | //| 4 - Ошибка значения TP | //| 5 - Ошибка значения Lot | //+-------------------------------------------------------------------------------------+ int OpenOrderCorrect(int Type, double Price, double SL, double TP, bool Redefinition = True) // Redefinition - при True доопределять параметры до минимально допустимых // при False - возвращать ошибку { // - 1 - == Проверка достаточности свободных средств ==================================== if(AccountFreeMarginCheck(Symbol(), OP_BUY, Lots) <= 0 || GetLastError() == 134) { if(!FreeMarginAlert) { Print("Недостаточно средств для открытия позиции. Free Margin = ", AccountFreeMargin()); FreeMarginAlert = True; } return(5); } FreeMarginAlert = False; // - 1 - == Окончание блока ============================================================= // - 2 - == Корректировка значений Price, SL и TP или возврат ошибки ==================== RefreshRates(); switch (Type) { case OP_BUY: string S = "BUY"; if (MathAbs(Price-Ask)/Point > 3) if (Redefinition) Price = ND(Ask); else return(2); if (ND(TP-Bid) < StopLevel && TP != 0) if (Redefinition) TP = ND(Bid+StopLevel); else return(4); if (ND(Bid-SL) < StopLevel) if (Redefinition) SL = ND(Bid-StopLevel); else return(3); break; case OP_SELL: S = "SELL"; if (MathAbs(Price-Bid)/Point > 3) if (Redefinition) Price = ND(Bid); else return(2); if (ND(Ask-TP) < StopLevel) if (Redefinition) TP = ND(Ask-StopLevel); else return(4); if (ND(SL-Ask) < StopLevel && SL != 0) if (Redefinition) SL = ND(Ask+StopLevel); else return(3); break; case OP_BUYSTOP: S = "BUYSTOP"; if (ND(Price-Ask) < StopLevel) if (Redefinition) Price = ND(Ask+StopLevel); else return(2); if (ND(TP-Price) < StopLevel && TP != 0) if (Redefinition) TP = ND(Price+StopLevel); else return(4); if (ND(Price-SL) < StopLevel) if (Redefinition) SL = ND(Price-StopLevel); else return(3); break; case OP_SELLSTOP: S = "SELLSTOP"; if (ND(Bid-Price) < StopLevel) if (Redefinition) Price = ND(Bid-StopLevel); else return(2); if (ND(Price-TP) < StopLevel) if (Redefinition) TP = ND(Price-StopLevel); else return(4); if (ND(SL-Price) < StopLevel && SL != 0) if (Redefinition) SL = ND(Price+StopLevel); else return(3); break; case OP_BUYLIMIT: S = "BUYLIMIT"; if (ND(Ask-Price) < StopLevel) if (Redefinition) Price = ND(Ask-StopLevel); else return(2); if (ND(TP-Price) < StopLevel && TP != 0) if (Redefinition) TP = ND(Price+StopLevel); else return(4); if (ND(Price-SL) < StopLevel) if (Redefinition) SL = ND(Price-StopLevel); else return(3); break; case OP_SELLLIMIT: S = "SELLLIMIT"; if (ND(Price - Bid) < StopLevel) if (Redefinition) Price = ND(Bid+StopLevel); else return(2); if (ND(Price-TP) < StopLevel) if (Redefinition) TP = ND(Price-StopLevel); else return(4); if (ND(SL-Price) < StopLevel && SL != 0) if (Redefinition) SL = ND(Price+StopLevel); else return(3); break; } // - 2 - == Окончание блока ============================================================= // - 3 - == Открытие ордера с ожидание торгового потока ================================= if(WaitForTradeContext()) // ожидание освобождения торгового потока { Comment("Отправлен запрос на открытие ордера ", S, " ..."); int ticket=OrderSend(Symbol(), Type, Lots, Price, 3, SL, TP, NULL, MagicNumber, 0);// открытие позиции // Попытка открытия позиции завершилась неудачей if(ticket<0) { int Error = GetLastError(); if(Error == 2 || Error == 5 || Error == 6 || Error == 64 || Error == 132 || Error == 133 || Error == 149) // список фатальных ошибок { Comment("Фатальная ошибка при открытии позиции т. к. "+ ErrorToString(Error)+" Советник отключен!"); FatalError = True; } else Comment("Ошибка открытия позиции ", S, ": ", Error); // нефатальная ошибка return(1); } // --------------------------------------------- // Удачное открытие позиции Comment("Позиция ", S, " открыта успешно!"); if (OrderSelect(ticket, SELECT_BY_TICKET)) { LastOpen = OrderOpenTime(); LastTicket = OrderTicket(); } PlaySound(OpenOrderSound); return(0); // ------------------------ } else { Comment("Время ожидания освобождения торгового потока истекло!"); return(1); } // - 3 - == Окончание блока ============================================================= } //+-------------------------------------------------------------------------------------+ //| Замена стандартного блока "if-else" | //+-------------------------------------------------------------------------------------+ double IF(bool Condition, double IfTrue, double IfFalse) { if (Condition) return(IfTrue); // Если условие истинно, то возвращаем значение IfTrue else return(IfFalse); // Если условие ложно, то возвращаем значение IfFalse } //+-------------------------------------------------------------------------------------+ //| Расчет границ канала и подсчет баров ниже и выше линии открытия | //+-------------------------------------------------------------------------------------+ void GetSignal() { Signal = 0; // - 1 - ====================== Получение значений границ канала ======================== // Бар, соответсвующий началу суток плюс смещение в часах и минутах int StartBar = iBarShift(Symbol(), 0, BeginDay+StartHour*3600+StartMinute*60); // Бар, соответствующий последнему бару "утреннего флэта" int FinishBar = iBarShift(Symbol(), 0, BeginDay+EndHour*3600+EndMinute*60)+1; double LowV = Low[iLowest(Symbol(), 0, MODE_LOW, StartBar-FinishBar+1, // Нижняя FinishBar)]; // граница канала double HighV = High[iHighest(Symbol(), 0, MODE_HIGH, StartBar-FinishBar+1, FinishBar)]; // Верхняя граница канала double OpenL = Open[StartBar]; // Цена открытия диапазона double CloseL = Close[FinishBar]; // Цена закрытия диапазона OpenAfterFlat = ND(Open[FinishBar-1]); // Цена открытия сделки int AboveBars = 0; // Закрытие и открытие свечи выше линии int BelowBars = 0; // Закрытие и открытие свечи ниже линии int CenterBars = 0; // Закрытие и открытие свечи по разные стороны от линии for (int j = StartBar; j >= FinishBar; j--) { if (Open[j] >= OpenL && Close[j] >= OpenL) // Подсчет баров, закрывшихся выше линии AboveBars++; else if (Open[j] <= OpenL && Close[j] <= OpenL) // Подсчет баров, закрывшихся ниже линии BelowBars++; else CenterBars++; } // - 1 - == Окончание блока ============================================================= // - 2 - == Генерация сигнала =========================================================== if (CloseL < OpenL && BelowBars > AboveBars+CenterBars) // Цена закрытия диапазона ниже // цены открытия диапазона и количество баров ниже цены открытия больше, чем // количество всех остальных баров Signal = 1; // Открытие BUY if (CloseL > OpenL && AboveBars > BelowBars+CenterBars) // Цена закрытия диапазона выше // цены открытия диапазона и количество баров выше цены открытия больше, чем // количество всех остальных баров Signal = -1; // Открытие SELL // - 2 - == Окончание блока ============================================================= return(True); } //+-------------------------------------------------------------------------------------+ //| Проверка существования позиции и закрытие ее, если наступил следующий день | //| Также проверяется время окончания утреннего флэта | //+-------------------------------------------------------------------------------------+ bool CheckOpen() { if (OrderSelect(LastTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) if (WaitForTradeContext()) { if (OrderType() == OP_BUY) double Price = Bid; else Price = Ask; if (!OrderClose(OrderTicket(), OrderLots(), ND(Price), 3)) return(False); } if (Hour() > EndHour || (Hour() == EndHour && Minute() > EndMinute)) return(True); else return(False); } //+-------------------------------------------------------------------------------------+ //| Функция START эксперта | //+-------------------------------------------------------------------------------------+ int start() { // - 1 - == Разрешено ли советнику работать? =========================================== if (!Activate || FatalError) // Отключается работа советника, если функция return(0); // init завершилась с ошибкой или имела место фатальная ошибка // - 1 - == Окончание блока ============================================================ // - 2 - == Сбор информации об условиях торговли ======================================== Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов // - 2 - == Окончание блока ============================================================ // - 3 - ==================== Проверка срабатывания сигнала за текущий день ============= BeginDay = iTime(Symbol(), PERIOD_D1, 0); if (LastOpen > BeginDay) return(0); else if (!CheckOpen()) return(0); // - 3 - == Окончание блока ============================================================ // - 4 - =============== Расчет сигнала при окончании флэта ============================= if (LastChannel < BeginDay && (Hour() > EndHour || (Hour() == EndHour && Minute() > EndMinute))) { GetSignal(); LastChannel = BeginDay; } // - 4 - == Окончание блока ============================================================ // - 5 - == Открытие позиций ============================================================ if (Signal == 1 && // Открытие BUY, если сигнал для покупки Bid <= OpenAfterFlat) //и текущая цена лучше, чем цена открытия самого первого { // бара после окончания утреннего флэта RefreshRates(); double SL = ND(IF(StopLoss == 0, 0, Ask - StopLoss*Tick)); double TP = ND(IF(TakeProfit == 0, 0, Ask + TakeProfit*Tick)); if (OpenOrderCorrect(OP_BUY, ND(Ask), SL, TP, True) != 0) // открытие позиции return(0); // если не удалось открыть, то попытка переносится на следующий тик } if (Signal == -1 && // Открытие SELL, если сигнал для продажи Bid >= OpenAfterFlat) //и текущая цена лучше, чем цена открытия самого первого { // бара после окончания утреннего флэта RefreshRates(); SL = ND(IF(StopLoss == 0, 0, Bid + StopLoss*Tick)); TP = ND(IF(TakeProfit == 0, 0, Bid - TakeProfit*Tick)); if (OpenOrderCorrect(OP_SELL, ND(Bid), SL, TP, True) != 0) // открытие позиции return(0); // если не удалось открыть, то попытка переносится на следующий тик } // - 5 - == Окончание блока ============================================================ return(0); }