//+-------------------------------------------------------------------------------------+ //| RollBackInTrend_Expert.mq4 | //| Scriptong | //| | //+-------------------------------------------------------------------------------------+ #property copyright "Scriptong" #property link "scriptong@mail.ru" //---- input parameters extern double Lots = 0.1; // Объем открываемой позиции extern string A1 = "Параметры индикатора MACD"; extern int FastMACD = 12; // Период быстрой МА extern int SlowMACD = 26; // Период медленной МА extern int SignalMACD = 9; // Период сигнальной МА extern string A2 = "====================================="; extern string OpenOrderSound = "ok.wav"; // Звук для открытия позиции extern int MagicNumber = 17589 ; // Магик позиций советника extern double Koeff = 0.85; // Часть ширины канала, от которой считается цель позиции bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, UpFractal1, UpFractal2, DownFractal1, DownFractal2, TP, SL; datetime LastBar; int Signal, TypeOrder, LastType, TicketOrder, TU1, TU2, TD1, TD2; //+-------------------------------------------------------------------------------------+ //| Функция инициализации эксперта | //+-------------------------------------------------------------------------------------+ int init() { //---- 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 - == Окончание блока ============================================================= LastBar = 0; Activate = True; // Все проверки успешно завершены, возводим флаг активизации эксперта //---- return(0); } //+-------------------------------------------------------------------------------------+ //| Функция деинициализации эксперта | //+-------------------------------------------------------------------------------------+ 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 OpenOrder(int Type, double Price, double SL, double TP, int Num) { // Блок проверки достаточности свободных средств if(AccountFreeMarginCheck(Symbol(), OP_BUY, Lots) <= 0 || GetLastError() == 134) { if(!FreeMarginAlert) { Print("Недостаточно средств для открытия позиции. Free Margin = ", AccountFreeMargin()); FreeMarginAlert = True; } return(False); } FreeMarginAlert = False; // --------------------------------------------- switch (Type) { case OP_BUY: string S = "BUY"; break; case OP_SELL: S = "SELL"; break; case OP_BUYSTOP: S = "BUYSTOP"; break; case OP_SELLSTOP: S = "SELLSTOP"; break; case OP_BUYLIMIT: S = "BUYLIMIT"; break; case OP_SELLLIMIT: S = "SELLLIMIT"; break; } if(WaitForTradeContext()) // ожидание освобождения торгового потока { Comment("Отправлен запрос на открытие ордера ", S, " ..."); int ticket=OrderSend(Symbol(), Type, Lots, Price, 0, SL, TP, NULL, MagicNumber*10+Num, 0, CLR_NONE); // открытие позиции // Попытка открытия позиции завершилась неудачей 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(False); } // --------------------------------------------- // Удачное открытие позиции Comment("Позиция ", S, " открыта успешно!"); PlaySound(OpenOrderSound); return(True); // ------------------------ } else { Comment("Время ожидания освобождения торгового потока истекло!"); return(False); } } //+-------------------------------------------------------------------------------------+ //| Ожидание торгового потока. Если поток свободен, то результат True, иначе - False | //+-------------------------------------------------------------------------------------+ bool WaitForTradeContext() { int P = 0; // цикл "пока" while(IsTradeContextBusy() && P < 5) { P++; Sleep(1000); } // ------------- if(P == 5) return(False); return(True); } //+-------------------------------------------------------------------------------------+ //| Находим два последних нижних и верхних фрактала, по которым определяем текущий тренд| //| (0 - нет, 1 - восходящий, 2 - нисходящий) | //+-------------------------------------------------------------------------------------+ int FindFractals() { // - 1 - ======= Поиск фракталов ======================================================== UpFractal1 = 0; UpFractal2 = 0; DownFractal1 = 0; DownFractal2 = 0; int i = 3; while ((UpFractal1 == 0 || UpFractal2 == 0 || DownFractal1 == 0 || DownFractal2 == 0) && i < Bars) // цикл продолжается, пока не будут найдены все четыре фрактала { double Up = iFractals(Symbol(), 0, MODE_UPPER, i); // верхний фрактал double Dn = iFractals(Symbol(), 0, MODE_LOWER, i); // нижний фрактал if (Up != 0) // если на текущем баре есть верхний фрактал if (UpFractal1 == 0) // и это первый найденный верхний фрактал { UpFractal1 = Up; // сохраняем его значение TU1 = i; // и запоминаем номер бара } else if (UpFractal2 == 0) // если это уже второй найденный верхний фрактал { UpFractal2 = Up; // то сохраняем его значение во вторую переменную TU2 = i; // и снова запоминаем номер бара } if (Dn != 0) // если на текущем баре есть нижний фрактал if (DownFractal1 == 0) // и это первый найденный нижний фрактал { DownFractal1 = Dn; // сохраняем его значение TD1 = i; // и запоминаем номер бара } else if (DownFractal2 == 0) // если это уже второй найденный нижний фрактал { DownFractal2 = Dn; // то сохраняем его значение во вторую переменную TD2 = i; // и снова запоминаем номер бара } i++; } // - 1 - ======= Окончание блока ======================================================== // - 2 - ======= Анализ полученных результатов ========================================== if (UpFractal1 == 0 || DownFractal1 == 0 || // Если не найден один из фракталов, UpFractal2 == 0 || DownFractal2 == 0)// то это ошибка и функция вернет значение -1 { Print("Ошибка в истории. Не найден один из фракталов!"); return(-1); } // Определение тренда if(UpFractal1 > UpFractal2 && DownFractal1 > DownFractal2) return(1); // Восходящий if(UpFractal1 < UpFractal2 && DownFractal1 < DownFractal2) return(2); // Нисходящий // - 2 - ======= Окончание блока ======================================================== return(0); // нет тренда // ------------------ } //+-------------------------------------------------------------------------------------+ //| Расчет коэффициентов K и B в уравнении прямой по двум точкам | //| Уравнение прямой Y = K*X + B, поэтому составив систему из двух уравнений, | //| можно найти неизвестные K и B, если известны Y1, Y2, X1, X2 | //+-------------------------------------------------------------------------------------+ void DirectLineCalc(int XB, double YB, int XE, double YE, double &B, double &K) { B = (YB*XE - YE*XB)/(XE - XB); K = (YE - B)/XE; } //+-------------------------------------------------------------------------------------+ //| Расчет сигнала по MACD и каналу, образованного фракталами | //+-------------------------------------------------------------------------------------+ void GetSignal() { Signal = 0; // обнуление сигнала // - 1 - == Берем значения MACD на 1-4 барах ============================================ double MACD1, MACD2, MACD3, MACD4, SMACD; MACD1 = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 1); MACD2 = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 2); MACD3 = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 3); MACD4 = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 4); SMACD = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_SIGNAL, 1); // - 1 - == Окончание блока ============================================================= // - 2 - == Сигнал открытия BUY ========================================================= double DirectB = 0, DirectK = 0; if (SMACD < MACD1 && MACD1 < MACD2 && MACD2 > MACD3 && MACD3 > MACD4) // откат на MACD if (FindFractals() == 1) // построен восходящий канал { double ThirdFractal = DownFractal1; // в качестве одного нижнего фрактала сначала int ThirdNumber = TD1; // берем первый нижний фрактал DirectLineCalc(TU1, UpFractal1, TU2, UpFractal2, DirectB, DirectK); // расчет K и B double ParallelB = DownFractal1 - DirectK*TD1; // Расчет B нижней границы канала if (DownFractal2 < DirectK*TD2+ParallelB)//Если второй нижний фрактал не помещается { // в полученный канал, то пересчитываем нижнюю границу по нему ThirdFractal = DownFractal2; ThirdNumber = TD2; ParallelB = DownFractal2 - DirectK*TD2; } // Проверяем, попадают ли цены закрытия всех свечей в полученный канал for (int i = MathMax(TD2, TU2); i >= MathMax(TD1, TU1); i--) if (Close[i] < DirectK*i+ParallelB || Close[i] > DirectK*i+DirectB) { DirectK = 0; // если не попадают, то канал не сформирован break; } // --------------- if (DirectK != 0 && ND(Bid+StopLevel) < ND((DirectB+ParallelB)/2)) { // все цены закрытия попали в канал и цена находится в нижней половине канала TP = (DirectB-ParallelB)*Koeff+ParallelB; SL = ThirdFractal-2*Tick; Signal = 1; // то это сигнал открытия BUY } } // - 2 - == Окончание блока ============================================================= // - 3 - == Сигнал открытия SELL ======================================================== if (SMACD > MACD1 && MACD1 > MACD2 && MACD2 < MACD3 && MACD3 < MACD4) // откат на MACD if (FindFractals() == 2) // построен нисходящий канал { ThirdFractal = UpFractal1; // в качестве одного верхнего фрактала сначала берем ThirdNumber = TU1; // первый верхний фрактал DirectLineCalc(TD1, DownFractal1, TD2, DownFractal2, DirectB, DirectK); // K и B ParallelB = UpFractal1 - DirectK*TU1;//Расчет коэффициента B верхней границы канала if (UpFractal2 > DirectK*TU2+ParallelB)// Если второй верхний фрактал не помещается { // в полученный канал, то пересчитываем верхнюю границу по нему ThirdFractal = UpFractal2; ThirdNumber = TU2; ParallelB = UpFractal2 - DirectK*TU2; } // Проверяем, попадают ли цены закрытия всех свечей в полученный канал for (i = MathMax(TD2, TU2); i >= MathMax(TD1, TU1); i--) if (Close[i] > DirectK*i+ParallelB || Close[i] < DirectK*i+DirectB) { DirectK = 0; // если не попадают, то канал не сформирован break; } // --------------- if (DirectK != 0 && ND(Bid-StopLevel) > ND((DirectB+ParallelB)/2)) { // все цены закрытия попали в канал и цена находится в верхней половине канала SL = ThirdFractal + Spread + 2*Tick; TP = ParallelB - (ParallelB - DirectB)*Koeff+Spread; Signal = 2; // то это сигнал открытия SELL } } // - 3 - == Окончание блока ============================================================= } //+-------------------------------------------------------------------------------------+ //| Удаляет все отложенники типа Type. Если удалить не удалось, то вернет 1. | //+-------------------------------------------------------------------------------------+ bool CheckPendingsReal(int Type) { for (int i = OrdersTotal()-1; i >= 0; i--) if(OrderSelect(i, SELECT_BY_POS)) if(OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber && OrderType() > 2) // поиск отложенного ордера if(OrderType() == Type) // Если ордер типа Type { if (WaitForTradeContext()) { if (!OrderDelete(OrderTicket())) // то пытаемся удалить его return(False); // не удалось удалить - вернем False } else return(False);//вернем False, если не дождались освобождения торгового потока } else return(False); // вернем False, если найден ордер не соответствующий Type return(True); // если все ОК, то вернем True } //+-------------------------------------------------------------------------------------+ //| Закрывает все позиции типа Type. Если закрыть не удалось закрыть, то 1. | //| Если присутствует противоположная, то возвращает 2. В случае успеха 0. | //+-------------------------------------------------------------------------------------+ int CheckOrdersReal(int Type) { for (int i = OrdersTotal()-1; i >= 0; i--) if(OrderSelect(i, SELECT_BY_POS)) if(OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber && OrderType() < 2) // поиск "своей" сделки if(OrderType() == Type) // Если позиция типа Type { if (WaitForTradeContext()) { if(Type == OP_BUY) double Pr = ND(MarketInfo(Symbol(), MODE_BID)); else Pr = ND(MarketInfo(Symbol(), MODE_ASK)); if (!OrderClose(OrderTicket(), OrderLots(), Pr, 3))// то пытаемся закрыть ее return(1); // не удалось закрыть - вернем 1 } else return(1);//вернем 1, если не удалось дождаться освобождения торгового потока } else return(2); // вернем 2, если открыта позиция, противоположная указанной return(0); // если все ОК, то вернем 0 } //+-------------------------------------------------------------------------------------+ //| Поиск первичной позиции и установка ей отложенника | //+-------------------------------------------------------------------------------------+ bool AddPending() { // - 1 - == Поиск первичной позиции ===================================================== for (int i = OrdersTotal()-1; i >= 0; i--) if (OrderSelect(i, SELECT_BY_POS)) if (OrderSymbol() == Symbol() && MathMod(OrderMagicNumber(), 10) == 0 && OrderType() < 2) // поиск первичной позиции , которая открылась по сигналу { bool Res = False; int Type = OrderType(); // запоминаем ее параметры double OpenP = OrderStopLoss(); double TP = MathAbs(OrderOpenPrice()-OrderStopLoss()); double SL = OrderTakeProfit(); // - 1 - == Окончание блока ============================================================= // - 2 - == Поиск отложенника, который должен быть установлен на стопе позиции ========== for (int j = OrdersTotal()-1; j >= 0; j--) if (OrderSelect(j, SELECT_BY_POS)) if (OrderSymbol() == Symbol() && MathMod(OrderMagicNumber(), 10) == 1 && OrderType() > 2) { Res = True; // отложенник найден, нет нужды его устанавливать break; } // - 2 - == Окончание блока ============================================================= // - 3 - == Установка отложенного ордера ================================================ if (!Res) // если отложенник не найден, то усановим его { if (Type == OP_BUY) // если первичная позиция длинная, то { Type = OP_SELLSTOP; // устанавливаем SELL STOP TP = OpenP - TP; // и цель отстоит от цены открытия на величину стопа } else { // если первичная позиция короткая, то Type = OP_BUYSTOP; // устанавливаем BUY STOP TP = OpenP + TP; // и цель отстоит от цены открытия на величину стопа } if (!OpenOrder(Type, OpenP, SL, TP, 1)) // если установить не удалось return(False); // возвращаем False } // - 3 - == Окончание блока ============================================================= break; } return(True); } //+-------------------------------------------------------------------------------------+ //| Поиск первичной позиции и удаление соответствующего ей отложенника, если позиции нет| //+-------------------------------------------------------------------------------------+ void DeletePending() { for (int i = OrdersTotal()-1; i >= 0; i--) if (OrderSelect(i, SELECT_BY_POS)) if (OrderSymbol() == Symbol() && MathMod(OrderMagicNumber(), 10) == 1 && OrderType() > 2) // поиск отложенного ордера { bool Res = False; int Ticket = OrderTicket(); // Поиск позиции, соответствующей отложеннику for (int j = OrdersTotal()-1; j >= 0; j--) if (OrderSelect(j, SELECT_BY_POS)) if (OrderSymbol() == Symbol() && MathMod(OrderMagicNumber(), 10) == 0 && OrderType() < 2) { Res = True; break; } // Если отложенника нет, то удаляем if (!Res) OrderDelete(Ticket); // ------------------------------------ break; } } //+-------------------------------------------------------------------------------------+ //| Функция START эксперта | //+-------------------------------------------------------------------------------------+ int start() { // - 1 - == Разрешено ли советнику работать? =========================================== if (!Activate || FatalError) // Отключается работа советника, если функция return(0); // init завершилась с ошибкой или имела место фатальная ошибка // - 1 - == Окончание блока ============================================================ // - 2 - == Удаление отложенного ордера при закрытии первичной позиции по профиту ======= DeletePending(); // - 2 - == Окончание блока ============================================================ // - 3 - == Контроль открытия нового бара ============================================== if (LastBar == Time[0]) //Если время посчитанного бара равно времени текущего бара return(0); // то это означает, что новый бар еще не открылся // - 3 - == Окончание блока ============================================================ // - 4 - == Сбор информации об условиях торговли ======================================== Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текщий спрэд StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов // - 4 - == Окончание блока ============================================================ // - 5 - == Расчет текущего сигнала ===================================================== GetSignal(); // - 5 - == Окончание блока ============================================================ // - 6 - == Открытие длинной позиции =================================================== if (Signal == 1) { int Res = CheckOrdersReal(OP_SELL); // Противоположную позицию - закрыть if (Res == 0) // если нет открытых позиций { if (!CheckPendingsReal(OP_BUYSTOP)) return(0); // и нет отложенных ордеров RefreshRates(); if (ND(Bid-SL-StopLevel) > 0) if (!OpenOrder(OP_BUY, ND(Ask), ND(SL), ND(TP), 0)) // если не удалось открыть return(0); // то попробуем со следующим тиком } if(Res == 1) return(0); //закрыть противоположную не вышло, ждем до следующего тика } // - 6 - == Окончание блока ============================================================ // - 7 - == Открытие короткой позиции =================================================== if (Signal == 2) { Res = CheckOrdersReal(OP_BUY); // Противоположную позицию - закрыть if (Res == 0) // если нет открытых позиций { if (!CheckPendingsReal(OP_SELLSTOP)) return(0); // и нет SELL STOP RefreshRates(); if (ND(SL-Ask-StopLevel) > 0) if (!OpenOrder(OP_SELL, ND(Bid), ND(SL), ND(TP), 0))// если не удалось открыть return(0); // то попробуем со следующим тиком } if (Res == 1) return(0);//закрыть противоположную не вышло, ждем до следующего тика } // - 7 - == Окончание блока ============================================================ // - 8 - == Добавление отложенного ордера к первичной позиции =========================== if (!AddPending()) return(0); // - 8 - == Окончание блока ============================================================ LastBar = Time[0]; // новый бар "посчитали" //---- return(0); } //+------------------------------------------------------------------+