#property copyright "Scritong" #property link "scriptong@mail.ru" //---- input parameters extern double Lots = 0.1; extern string A1 = "Период ATR"; extern int ATRPeriod = 24; // Период для расчёта ATR extern string A2 = "Период локального экстремума"; extern int Range = 5; //Среди скольких баров сигнальный бар должен быть экстремумом extern string A3 = "Множитель для расчета уровней стопа и профита"; extern double Factor = 5; // Множитель средней волатильности extern string OpenOrderSound = "ok.wav"; // Звуковой сигнал при открытии позиции extern int MagicNumber = 11235; // Уникальный идентификатор своих ордеров int Signal; // Текущий сигнал bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, FreezeLevel, BegSL, // Начальное значение стопа BegTP, // Начальное значение профита EndSL, // Значение стопа, если не достигнут "короткий" профит или стоп EndTP; // Значение профита, если не достигнут "короткий" профит или стоп datetime LastBar, // Время открытия последнего посчитанного бара LastSignal; // Время открытия последнего сигнального бара //+-------------------------------------------------------------------------------------+ //| Функция инициализации эксперта | //+-------------------------------------------------------------------------------------+ int init() { FatalError = False; // - 1 - == Сбор информации об условиях торговли ======================================== Tick = MarketInfo(Symbol(), MODE_TICKSIZE); // минимальный тик Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов FreezeLevel = ND(MarketInfo(Symbol(), MODE_FREEZELEVEL)*Point); // уровень заморозки MinLot = MarketInfo(Symbol(), MODE_MINLOT); // минимальный разрешенный объем сделки MaxLot = MarketInfo(Symbol(), MODE_MAXLOT); // максимальный разрешенный объем сделки LotStep = MarketInfo(Symbol(), MODE_LOTSTEP); // шаг приращения объема сделки // - 1 - == Окончание блока ============================================================= // - 2 - == Приведение объема сделки к допустимому и проверка корректности объема ======= Lots = LotRound(Lots); // округление объема до ближайшего допустимого // - 2 - == Окончание блока ============================================================= // - 3 - ========================== Учет истории сигналов стратегии ===================== LastSignal = 0; // Последний сигнал не определен for (int i = Bars-4; i > 0; i--) // Расчет ведется от начала истории до текущего бара GetSignal(i); // Расчет значения сигнала // - 3 - =========================== Окончание блока ==================================== LastBar = 0; Activate = True; // Все проверки успешно завершены, возводим флаг активизации эксперта return(0); } //+-------------------------------------------------------------------------------------+ //| Функция деинициализации эксперта | //+-------------------------------------------------------------------------------------+ int deinit() { Comment(""); return(0); } //+-------------------------------------------------------------------------------------+ //| Проверка объема на корректность и округление | //+-------------------------------------------------------------------------------------+ double LotRound(double L) { return(MathRound(MathMin(MathMax(L, MinLot), MaxLot)/LotStep)*LotStep); } //+-------------------------------------------------------------------------------------+ //| Приведение значений к точности одного пункта | //+-------------------------------------------------------------------------------------+ 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 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, " ..."); int ticket=OrderSend(Symbol(), Type, Lot, 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, " открыта успешно!"); PlaySound(OpenOrderSound); return(0); // ------------------------ } else { Comment("Время ожидания освобождения торгового потока истекло!"); return(1); } // - 3 - == Окончание блока ============================================================= } //+-------------------------------------------------------------------------------------+ //| Приведение значений к точности одного тика | //+-------------------------------------------------------------------------------------+ double NP(double A) { return(MathRound(A/Tick)*Tick); } //+-------------------------------------------------------------------------------------+ //| Генерация сигналов покупки и продажи | //+-------------------------------------------------------------------------------------+ void GetSignal(int Num) { Signal = 0; // - 1 - ============ Определение значений средней и единичной волатильностей =========== double ATR1 = iATR(NULL, 0, ATRPeriod, Num); // Последнее известное значение АТR double ATR2 = iATR(NULL, 0, ATRPeriod, Num+1); // Предпоследнее известное значение ATR double ATR3 = iATR(NULL, 0, ATRPeriod, Num+2);//Предпредпоследнее известное значение ATR double Curr1 = High[Num] - Low[Num]; // Волатильность первой свечи double Curr2 = High[Num+1] - Low[Num+1]; // Волатильность второй свечи double Curr3 = High[Num+2] - Low[Num+2]; // Волатильность третьей свечи // - 1 - ========================== Окончание блока ===================================== if (Curr1 > ATR1 && Curr2 < ATR2 && Curr3 <= ATR3)//Единичная волатильность стала больше { // средней волатильности после двух подряд меньших значений // - 2 - ======================== Генерация сигнала покупки ============================= if (Open[Num] > Close[Num] && // свеча медвежья Close[Num] < (High[Num] + Low[Num])/2) // закрылась ниже середины if (iLowest(NULL, 0, MODE_LOW, Range, Num) == Num && // Свеча - локальный минимум High[Num] < High[Num+1]) // Максимум свечи ниже предыдущего максимума { Signal = 1; // Сигнал покупки LastSignal = Time[0]; // Запоминаем время последнего сигнала BegSL = Open[Num-1] - ATR1; // Расчет начального стопа BegTP = High[Num]; // Расчет начального профита EndSL = Open[Num-1] - Factor*ATR1; // Окончательный уровень стопа EndTP = Open[Num-1] + Factor*ATR1; // Окончательный уровень профита } // - 2 - ========================== Окончание блока ===================================== // - 3 - ======================== Генерация сигнала продажи ============================= if (Open[Num] < Close[Num] && // свеча бычья Close[Num] > (High[Num] + Low[Num])/2) // закрылась выше середины if (iHighest(NULL, 0, MODE_HIGH, Range, Num) == Num &&// Свеча - локальный максимум Low[Num] > Low[Num+1]) // Минимум свечи выше предыдущего минимума { Signal = -1; // Сигнал продажи LastSignal = Time[0]; // Запоминаем время последнего сигнала BegSL = Open[Num-1] + ATR1 + Spread; // Расчет начального стопа BegTP = Low[Num] + Spread; // Расчет начального профита EndSL = Open[Num-1] + Factor*ATR1 + Spread; // Окончательный уровень стопа EndTP = Open[Num-1] - Factor*ATR1 + Spread; // Окончательный уровень профита } // - 3 - ========================== Окончание блока ===================================== } } //+-------------------------------------------------------------------------------------+ //| Поиск своих ордеров. Возвращает: | //| 0 - Своих позиций нет. Можно открывать новую позицию | //| 1 - Не удалось закрыть указанную позицию. Требуется еще попытка | //| 2 - Существует противоположная указанной позиция, открытая по последнему сигналу | //+-------------------------------------------------------------------------------------+ int CheckOrders(int Type) // Type - Тип позиции, которую нужно закрыть или -1, если тип { // позиции не имеет значения for (int i = OrdersTotal()-1; i >= 0; i--) // Используется весь список ордеров if (OrderSelect(i, SELECT_BY_POS)) // Убедимся, что ордер выбран if (OrderMagicNumber() == MagicNumber && // Ордер открыт экспертом, OrderSymbol() == Symbol()) // который прикреплен к текущей валютной паре if (OrderType() == Type || Type < 0)// Если тип ордера равен заданному или неважен { if (WaitForTradeContext()) // Свободен ли торговый поток? { if (Type == OP_BUY) // Если следует закрыть длинную сделку, double Price = MarketInfo(Symbol(), MODE_BID); // то применяется цена BID else // Если следует закрыть короткую сделку, Price = MarketInfo(Symbol(), MODE_ASK); // то применяется цена ASK if (!OrderClose(OrderTicket(), OrderLots(), NP(Price), 3)) // Если сделку не return(1); // удалось закрыть, то результат функции - 1 } } else // найден противоположный параметру Type по типу ордер if (OrderOpenTime() >= LastSignal) // Если последний сигнал уже отработан, то return(2); // открывать новую сделку нельзя - результат 2 return(0); // Можно открывать сделку } //+-------------------------------------------------------------------------------------+ //| Открытие позиций | //+-------------------------------------------------------------------------------------+ bool Trade() { // - 1 - ==================== Открытие длинной позиции ================================== if (Signal > 0) { int Res = CheckOrders(OP_SELL); // закрытие коротких сделок, если таковые имеются if (Res == 0) // Сделок, открытых по текущему сигналу, нет, можно открывать новую { if (OpenOrderCorrect(OP_BUY, Lots, NP(Ask), NP(BegSL), NP(BegTP)) != 0) return(False); } if (Res == 1) return(False);//Существует короткая позиция, которую закрыть не удалось } // - 1 - ==================== Окончание блока =========================================== // - 2 - ==================== Открытие короткой позиции ================================= if (Signal < 0) { Res = CheckOrders(OP_BUY); // закрытие длинных сделок, если таковые имеются if (Res == 0) // Сделок, открытых по текущему сигналу, нет, можно открывать новую { if (OpenOrderCorrect(OP_SELL, Lots, NP(Bid), NP(BegSL), NP(BegTP)) != 0) return(False); } if (Res == 1) return(False);// Существует длинная позиция, которую закрыть не удалось } // - 2 - ==================== Окончание блока =========================================== return(True); } //+-------------------------------------------------------------------------------------+ //| Функция установки новых стопа и профита после отработки ордером более одного периода| //+-------------------------------------------------------------------------------------+ bool ModifyCheck() { for (int i = OrdersTotal()-1; i >= 0; i--) // Используется весь список ордеров if (OrderSelect(i, SELECT_BY_POS)) // Убедимся, что ордер выбран if (OrderMagicNumber() == MagicNumber && // Ордер открыт экспертом, OrderSymbol() == Symbol()) // который прикреплен к текущей валютной паре if (TimeCurrent() - OrderOpenTime() >= Period()*60) // Ордер существует дольше // одного временного периода графика if (MathAbs(OrderStopLoss() - EndSL) >= Tick ||// не равен нужному уровень стопа MathAbs(OrderTakeProfit() - EndTP) >= Tick) // или уровень профита // Попытка изменения уровней стопа или профита (или обоих уровней вместе) if (WaitForTradeContext()) // Свободен ли торговый поток? { RefreshRates(); // Обновление значений переменных Bid и Ask if ((OrderType() == OP_BUY && // Если сделка длинная, Bid - EndSL > StopLevel && // а новый стоп и EndTP - Bid > StopLevel) || // новый профит достаточно далеки или (OrderType() == OP_SELL && // сделка короткая, EndSL - Ask > StopLevel && // а новый стоп Ask - EndTP > StopLevel)) // и новый профит достаточно далеки { // то можно произвести попытку изменения if (!OrderModify(OrderTicket(), 0, NP(EndSL), NP(EndTP), 0)) // при return(False); // неудаче функция вернет False } else // если новый стоп или новый профит расположены близко к текущей return(false); // цене, то результат тоже False } else // Не удалось дождаться освобождения торгового потока - результат False return(False); return(True); // Все действия завершены успешно - результат True } //+-------------------------------------------------------------------------------------+ //| Функция start эксперта | //+-------------------------------------------------------------------------------------+ int start() { // - 1 - ========================== Можно ли работать эксперту? ========================= if (!Activate || FatalError) return(0); // - 1 - ============================= Окончание блока ================================== // - 2 - ========================== Контроль открытия нового бара ======================= if (LastBar == Time[0]) return(0); // - 2 - ============================= Окончание блока ================================== // - 3 - ========================== Расчет сигналов открытия и закрытия ================= GetSignal(1); // - 3 - ============================= Окончание блока ================================== // - 4 - ================== Выполнение операций открытия и закрытия позиций ============= if (!Trade()) return(0); // - 4 - ============================= Окончание блока ================================== // - 5 - ==== Изменение стопов и профитов, если не удалось получить "быструю" прибыль === if (!ModifyCheck()) return(0); // - 5 - ============================= Окончание блока ================================== LastBar = Time[0]; return(0); }