//+-------------------------------------------------------------------------------------+ //| SessionTrade_Expert.mq4 | //| Scriptong | //| scriptong@mail.ru | //+-------------------------------------------------------------------------------------+ #property copyright "Scriptong" #property link "scriptong@mail.ru" //---- input parameters extern double Lots = 0.1; extern int Delta = 1; extern int AzianFlatStart = 0; extern int AzianFlatEnd = 8; extern int EuropeanFlatStart = 8; extern int EuropeanFlatEnd = 16; extern int AmericanFlatStart = 16; extern int AmericanFlatEnd = 24; extern string OpenOrderSound = "ok.wav"; extern int MagicNumber = 47589; bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, LastLots, LowV, HighV, SessionLow, SessionHigh; datetime LastBar, NowDay, LastOpen[3]; int Start[3], Finish[3], CurSession; //+-------------------------------------------------------------------------------------+ //| expert initialization function | //+-------------------------------------------------------------------------------------+ int init() { //---- Activate = False; FatalError = 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 (!ToArray(0, AzianFlatStart, AzianFlatEnd) || !ToArray(1, EuropeanFlatStart, EuropeanFlatEnd) || !ToArray(2, AmericanFlatStart, AmericanFlatEnd)) return(0); // - 3 - === Окончание блока ============================================================ ArrayInitialize(LastOpen, 0); // - 4 - === Восстановление значений элементов массива LastOpen ========================= for (int i = OrdersHistoryTotal() - 1; i >= 0; i--) if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) // поиск в истории счета if (OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber) { int ID = MathMod(OrderMagicNumber(), 10); LastOpen[ID] = MathMax(LastOpen[ID], OrderOpenTime()); } for (i = OrdersTotal() - 1; i >= 0; i--) if (OrderSelect(i, SELECT_BY_POS)) // поиск в окне терминала if (OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber) { ID = MathMod(OrderMagicNumber(), 10); LastOpen[ID] = MathMax(LastOpen[ID], OrderOpenTime()); } // - 4 - === Окончание блока ============================================================ LastBar = 0; Activate = True; //---- return(0); } //+-------------------------------------------------------------------------------------+ //| expert deinitialization function | //+-------------------------------------------------------------------------------------+ int deinit() { //---- Comment(""); //---- return(0); } //+-------------------------------------------------------------------------------------+ //| Перекомпоновка входных параметров в массив | //+-------------------------------------------------------------------------------------+ bool ToArray(int Num, int St, int End) { if (St < 0 || End < 0 || St > 24 || End > 24 || St >= End) { Comment("Значения времени Start и End должны лежать в диапазоне от 0 до 24 и Start < End."); Print("Значения времени Start и End должны лежать в диапазоне от 0 до 24 и Start < End."); return(False); } int i = Num-1; while (i >= 0) { if (Start[i] >= St || Finish[i] >= End || Finish[i] > St) { Comment("Обнаружено пересечение сессий. Советник отключен."); Print("Обнаружено пересечение сессий. Советник отключен."); return(False); } i--; } Start[Num] = St; Finish[Num] = End; return(True); } //+------------------------------------------------------------------------------------+ //| Приведение значений к точности одного тика | //+------------------------------------------------------------------------------------+ double ND(double A) { return(NormalizeDouble(A, Digits)); } //+-------------------------------------------------------------------------------------+ //| Расчет границ канала | //+-------------------------------------------------------------------------------------+ void GetSignal() { // - 1 - ==== Если канал не будет рассчитан, то все значения останутся нулевыми ========= LowV = 0; HighV = 0; CurSession = -1; int DayBar = 0; int k = 0; // - 1 - ============================= Окончание блока ================================== // - 2 - ==== Определение текущей торговой сесиии ======================================= for (k = 0; k < 3; k++) if (Hour() >= Start[k] && Hour() < Finish[k]) { if (k == 0) { DayBar++; int j = 2; } else j = k - 1; // - 2 - ============================= Окончание блока ================================== // - 3 - ==== Расчет уровней ============================================================ datetime BeginDay = iTime(Symbol(), PERIOD_D1, DayBar); // Время начала дня if (!(TimeDayOfWeek(BeginDay) == 5 && k == 0)) // Исключаем азиатскую сессию { // понедельника // Бар, соответствующий началу предыдущей сесии int StartBar = iBarShift(Symbol(), 0, BeginDay+Start[j]*3600); // Бар, соответствующий окончанию предыдущей сесии int FinishBar = iBarShift(Symbol(), 0, BeginDay+Finish[j]*3600)+1; // Нижняя граница LowV = Low[iLowest(Symbol(), 0, MODE_LOW, StartBar-FinishBar+1, FinishBar)]; // Верхняя граница HighV = High[iHighest(Symbol(), 0, MODE_HIGH, StartBar-FinishBar+1, FinishBar)]; // Минимум текущей сессии SessionLow = Low[iLowest(Symbol(), 0, MODE_LOW, FinishBar+1)]; // Максимум текущей сессии SessionHigh = High[iHighest(Symbol(), 0, MODE_HIGH, FinishBar+1)]; } CurSession = k;// номер текущей сессии (0-азиатская, 1-европейская, 2-американская) break; } // - 3 - ============================= Окончание блока ================================== } //+-------------------------------------------------------------------------------------+ //| Расшифровка сообщения об ошибке | //+-------------------------------------------------------------------------------------+ 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("запрещено локирование."); } } //+-------------------------------------------------------------------------------------+ //| "Правильное" открытие позиции | //| В отличие от OpenOrder проверяет соотношение текущих уровней и устанавливаемых | //| Возвращает: | //| 0 - нет ошибок | //| 1 - Ошибка открытия | //| 2 - Ошибка значения Price | //| 3 - Ошибка значения SL | //| 4 - Ошибка значения TP | //| 5 - Ошибка значения Lot | //+-------------------------------------------------------------------------------------+ int OpenOrderCorrect(int Type, double Lot, double Price, double SL, double TP, bool Redefinition = True) // Redefinition - при True доопределять параметры до минимально допустимых // при False - возвращать ошибку { // - 1 - == Проверка достаточности свободных средств ==================================== if(AccountFreeMarginCheck(Symbol(), OP_BUY, Lot) <= 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+Tick); else return(4); if (ND(Bid-SL) <= StopLevel) if (Redefinition) SL = ND(Bid-StopLevel-Tick); 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-Tick); else return(4); if (ND(SL-Ask) <= StopLevel && SL != 0) if (Redefinition) SL = ND(Ask+StopLevel+Tick); else return(3); break; case OP_BUYSTOP: S = "BUYSTOP"; if (ND(Price-Ask) <= StopLevel) if (Redefinition) Price = ND(Ask+StopLevel+Tick); else return(2); if (ND(TP-Price) <= StopLevel && TP != 0) if (Redefinition) TP = ND(Price+StopLevel+Tick); else return(4); if (ND(Price-SL) <= StopLevel) if (Redefinition) SL = ND(Price-StopLevel-Tick); else return(3); break; case OP_SELLSTOP: S = "SELLSTOP"; if (ND(Bid-Price) <= StopLevel) if (Redefinition) Price = ND(Bid-StopLevel-Tick); else return(2); if (ND(Price-TP) <= StopLevel) if (Redefinition) TP = ND(Price-StopLevel-Tick); else return(4); if (ND(SL-Price) <= StopLevel && SL != 0) if (Redefinition) SL = ND(Price+StopLevel+Tick); else return(3); break; case OP_BUYLIMIT: S = "BUYLIMIT"; if (ND(Ask-Price) <= StopLevel) if (Redefinition) Price = ND(Ask-StopLevel-Tick); else return(2); if (ND(TP-Price) <= StopLevel && TP != 0) if (Redefinition) TP = ND(Price+StopLevel+Tick); else return(4); if (ND(Price-SL) <= StopLevel) if (Redefinition) SL = ND(Price-StopLevel-Tick); else return(3); break; case OP_SELLLIMIT: S = "SELLLIMIT"; if (ND(Price - Bid) <= StopLevel) if (Redefinition) Price = ND(Bid+StopLevel+Tick); else return(2); if (ND(Price-TP) <= StopLevel) if (Redefinition) TP = ND(Price-StopLevel-Tick); else return(4); if (ND(SL-Price) <= StopLevel && SL != 0) if (Redefinition) SL = ND(Price+StopLevel+Tick); else return(3); break; } // - 2 - == Окончание блока ============================================================= // - 3 - == Открытие ордера с ожидание торгового потока ================================= if(WaitForTradeContext()) // ожидание освобождения торгового потока { Comment("Отправлен запрос на открытие ордера ", S, " ..."); datetime Exp = NowDay + Finish[CurSession]*3600; if (Exp - TimeCurrent() <= 600) Exp = TimeCurrent() + 660; int ticket=OrderSend(Symbol(), Type, Lot, Price, 3, SL, TP, NULL, MagicNumber*10+CurSession, Exp);// открытие позиции // Попытка открытия позиции завершилась неудачей 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, " открыта успешно!"); PlaySound(OpenOrderSound); return(0); // ------------------------ } else { Comment("Время ожидания освобождения торгового потока истекло!"); return(1); } // - 3 - == Окончание блока ============================================================= } //+-----------------------------------------------------------------------------------+ //| Ожидание торгового потока. Если поток свободен, то результат True, иначе - False | //+-----------------------------------------------------------------------------------+ bool WaitForTradeContext() { int P = 0; // цикл "пока" while(IsTradeContextBusy() && P < 5) { P++; Sleep(1000); } // ------------- if(P == 5) return(False); return(True); } //+-------------------------------------------------------------------------------------+ //| Установка отложенных ордеров | //+-------------------------------------------------------------------------------------+ void DoTransactions() { // - 1 - == Установка ордера Buy Stop =================================================== if (LastOpen[CurSession] < NowDay) // Устанавливался ли ордер за время текущей сессии? if (Close[1] < LowV && Open[1] < LowV) // Цена закрепилась за нижней границей канала { RefreshRates(); double Price = ND(LowV + Spread + Delta*Tick); // Цена открытия Buy Stop - нижняя // граница канала double SL = ND(SessionLow - (HighV - LowV) - Tick); // Стоп для Buy Stop double TP = ND(HighV); // профит для Buy Stop - противоположная граница канала if (OpenOrderCorrect(OP_BUYSTOP, Lots, Price, SL, TP, False) == 0) LastOpen[CurSession] = TimeCurrent(); // Ордер успешно открыт, записываем время else return; } // - 1 - === Окончание блока ============================================================ // - 2 - === Установка ордера Sell Stop ================================================= if (LastOpen[CurSession] < NowDay) // Устанавливался ли ордер за время текущей сессии? if (Close[1] > HighV && Open[1] > HighV)// Цена закрепилась за верхней границей канала { RefreshRates(); Price = ND(HighV); // Цена открытия Sell Stop - верхняя граница канала SL = ND(SessionHigh + (HighV - LowV) + Spread + Tick); // Стоп для Sell Stop TP = ND(LowV + Spread); // профит для Sell Stop - противоположная граница канала if (OpenOrderCorrect(OP_SELLSTOP, Lots, Price, SL, TP, False) == 0) LastOpen[CurSession] = TimeCurrent(); // Ордер успешно открыт, записываем время else return; } // - 2 - === Окончание блока ============================================================ LastBar = Time[0]; // Больше на текущем баре не работаем } //+-------------------------------------------------------------------------------------+ //| Функция START эксперта | //+-------------------------------------------------------------------------------------+ int start() { // - 1 - === Разрешено ли советнику работать? =========================================== if (!Activate || FatalError) // Отключается работа советника, если функция return(0); // init завершилась с ошибкой или имела место фатальная ошибка // - 1 - === Окончание блока ============================================================ // - 2 - == Контроль образования нового бара для ускорения работы эксперта ============== if (LastBar == Time[0]) return(0); // - 2 - === Окончание блока ============================================================ // - 3 - == Сбор информации об условиях торговли ======================================== Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов // - 3 - === Окончание блока ============================================================ // - 4 - == Определение времени начала текущего дня и расчет уровней ==================== NowDay = iTime(Symbol(), PERIOD_D1, 0); GetSignal(); // - 4 - === Окончание блока ============================================================ // - 5 - == Установка отложенного ордера, если определены уровни ======================== if (LowV != 0 && HighV != 0 && CurSession != -1) DoTransactions(); // - 5 - === Окончание блока ============================================================ //---- return(0); } //+------------------------------------------------------------------+