#property copyright "Scritong" #property link "scriptong@mail.ru" //---- input parameters extern double Lots = 0.1; extern int MAPeriod = 37; extern int MAMethod = MODE_EMA; extern int MAPrice = PRICE_CLOSE; extern string OpenOrderSound = "ok.wav"; // Звуковой сигнал при открытии позиции extern int MagicNumber = 11238; // Уникальный идентификатор своих ордеров int BuyTicket, // Идентификатор ордера BUY или BUYSTOP SellTicket, // Идентификатор ордера SELL или SELLSTOP BuyType, // Тип ордера BUY или BUYSTOP SellType; // Тип ордера SELL или SELLSTOP bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, FreezeLevel, BuyLevel, // Уровень открытия Buy Stop SellLevel, // Уровень открытия Sell Stop LastUp, // Последний фрактал выше средней LastDn; // Последний фрактал ниже средней bool BuyChangeEnable, // Флаг разрешения смены уровня Buy по последнему фракталу выше // средней SellChangeEnable; // Флаг разрешения смены уровня Sell по последнему фракталу ниже // средней datetime LastBar, // Время открытия бара, на котором производился вызов торговых функций LastCalc; // Время открытия последнего обработанного бара //+-------------------------------------------------------------------------------------+ //| Функция инициализации эксперта | //+-------------------------------------------------------------------------------------+ 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 - ================== Проверка корректности входных параметров ==================== if (MAPeriod < 2) { Comment("Значение MAPeriod должно быть больше 1. Советник отключен!"); Print("Значение MAPeriod должно быть больше 1. Советник отключен!"); return(False); } if (MAMethod < 0 || MAMethod > 3) { Comment("Значение MAMethod должно быть от 0 до 3. Советник отключен!"); Print("Значение MAMethod должно быть от 0 до 3. Советник отключен!"); return(False); } if (MAPrice < 0 || MAPrice > 6) { Comment("Значение MAPrice должно быть от 0 до 6. Советник отключен!"); Print("Значение MAPrice должно быть от 0 до 6. Советник отключен!"); return(False); } // - 3 - =========================== Окончание блока ==================================== // - 4 - ================== Формирование текущих уровней BuyLevel и SellLevel =========== BuyLevel = 999999; // Уровень не определен LastUp = 999999; // Фрактал не найден SellLevel = 0; // Уровень не определен LastDn = 0; // Фрактал не найден BuyChangeEnable = true; // Разрешено в качестве уровня применить первый же найденный // фрактал выше средней SellChangeEnable = true; // Разрешено в качестве уровня применить первый же найденный // фрактал ниже средней LastCalc = 1; // Необходимо произвсти поиск по всей имеющейся истории GetSignal(); // Расчет уровней // - 4 - =========================== Окончание блока ==================================== 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() { // - 1 - ================================ Инициализация перменных ======================= int BegBar = iBarShift(NULL, 0, LastCalc); // - 1 - ==================================== Окончание блока =========================== for (int i = BegBar; i > 0; i--) { // - 2 - ================================ Поиск "верхних" фракталов ===================== // - 2.1 - ================= Отслеживание пробития фрактала ========================= if (High[i] >= BuyLevel + Tick && BuyChangeEnable) // Если случилось пробитие уровня { // BUY BuyChangeEnable = False; // до пробития уровня Sell запрещается изменение величины // BuyLevel SellChangeEnable = True; // величина SellLevel может изменяться SellLevel = LastDn; // и принимает значение последнего фрактала ниже средней } // - 2.1 - ========================== Окончание блока ============================== // - 2.2 - ======================== Поиск нового фрактала =========================== if (i > 2) { double Fr = iFractals(NULL, 0, MODE_UPPER, i); // Получение значения "верхнего" // фрактала на свече №i if (Fr != 0) // Если фрактал найден { double MA = iMA(NULL, 0, MAPeriod, 0, MAMethod, MAPrice, i); if (Fr > MA) // Чтобы фрактал был достоен внимания, он должен { // находиться выше средней LastUp = Fr; // В любом случае сохраняется его значение if (Fr < BuyLevel && BuyChangeEnable) // Если фрактал ниже предыдущего или BuyLevel = Fr; // разрешено обновление, то уровень обновляется } } } // - 2.2 - ========================== Окончание блока ============================== // - 2 - ==================================== Окончание блока =========================== // - 3 - ================================ Поиск "нижних" фракталов ====================== // - 3.1 - ================= Отслеживание пробития фрактала ========================= if (Low[i] <= SellLevel - Tick && SellChangeEnable) // Если случилось пробитие уровня { // Sell SellChangeEnable = False; // до пробития уровня Buy запрещается изменение величины // SellLevel BuyChangeEnable = True; // величина BuyLevel может изменяться BuyLevel = LastUp; // и принимает значение последнего фрактала выше средней } // - 3.1 - ========================== Окончание блока ============================== // - 3.2 - ======================== Поиск нового фрактала =========================== if (i > 2) { Fr = iFractals(NULL, 0, MODE_LOWER, i); // Получение значения "нижнего" // фрактала на свече №i if (Fr != 0) // Если фрактал найден { MA = iMA(NULL, 0, MAPeriod, 0, MAMethod, MAPrice, i); if (Fr < MA) // Чтобы фрактал был достоен внимания, он должен { // находиться ниже средней LastDn = Fr; // В любом случае сохраняется его значение if (Fr > SellLevel && SellChangeEnable) // Если фрактал ниже предыдущего или SellLevel = Fr; // разрешено обновление, то уровень обновляется } } } // - 3.2 - ========================== Окончание блока ============================== // - 3 - ==================================== Окончание блока =========================== } LastCalc = Time[2]; // С этого бара начнется следующий цикл поиска уровней return; } //+-------------------------------------------------------------------------------------+ //| Закрытие заданного рыночного ордера | //+-------------------------------------------------------------------------------------+ bool CloseOrder(int Ticket) { if (OrderSelect(Ticket, SELECT_BY_TICKET) && // Существует ордер с заданным тикетом и OrderCloseTime() == 0) // ордер не закрыт if (WaitForTradeContext()) // Свободен ли торговый поток? { if (OrderType() == OP_BUY) // Если следует закрыть длинную сделку, double Price = MarketInfo(Symbol(), MODE_BID); // то применяется цена BID else // Если следует закрыть короткую сделку, Price = MarketInfo(Symbol(), MODE_ASK); // то применяется цена ASK if (!OrderClose(OrderTicket(), OrderLots(), NP(Price), 3)) // Если сделку не return(False); // удалось закрыть, то результат функции - False } return(True); // Можно открывать сделку } //+-------------------------------------------------------------------------------------+ //| Открытие позиций и установка ордеров | //+-------------------------------------------------------------------------------------+ bool Trade() { // - 1 - ============================= Установка ордера BuyStop ========================= if (BuyLevel < 999999) // активен сигнал BUY (проверяется отложенный ордер Buy Stop и { // уровень стопа у имеющейся позиции Sell) double Price = NP(BuyLevel + Spread + Tick); // Расчет цены открытия Buy Stop // - 1.1 - ====================== Проверка существования Buy Stop =================== if (BuyType == OP_BUYSTOP) // Присутствует BuyStop if (OrderSelect(BuyTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { if (MathAbs(OrderOpenPrice() - Price) >= Tick) // Проверка цены открытия // Если не равна нужной цене, то производится изменение if (WaitForTradeContext()) // свободен ли торговый поток? { RefreshRates(); // Обновление значений Bid и Ask if (Price - Ask > StopLevel)// Если новое значение цены открытия достаточно if (!OrderModify(BuyTicket, Price, 0, 0, 0)) // далеко от текущей цены return(False); } else return(False); } // - 1.1 - =========================== Окончание блока ============================== // - 1.2 - ==== Проверка существования Sell при уходе цены выше цены открытия Buy === if (SellType == OP_SELL && Ask > Price)// Присутствует Sell при цене выше входа в Buy { CloseOrder(SellTicket); // В этом случае необходимо закрыть сделку return(False); } // - 1.2 - =========================== Окончание блока ============================== // - 1.3 - ======== Проверка существования Sell в "нормальных условиях" ============= if (SellType == OP_SELL) // Если есть SELL, то проверяется расположение его стопа if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { if (MathAbs(OrderStopLoss() - Price) >= Tick) // Проверка правильности стопа if (WaitForTradeContext()) // свободен ли торговый поток? { RefreshRates(); // Обновление значений Bid и Ask if (Price - Ask > StopLevel)//Если новое значение стопа открытия достаточно if (!OrderModify(SellTicket, 0, Price, 0, 0)) // далеко от текущей цены return(False); } else return(False); } // - 1.3 - =========================== Окончание блока ============================== // - 1.4 - ====================== Установка Buy Stop при его отсутствии ============= if (BuyType < 0 && Price > Ask) // Ордер BuyStop не установлен if (OpenOrderCorrect(OP_BUYSTOP, Lots, Price, 0, 0) != 0) return(False); // - 1.4 - =========================== Окончание блока ============================== } // - 1 - =============================== Окончание блока ================================ // - 2 - =========================== Установка ордера SellStop ========================== if (SellLevel > 0) // активен сигнал SELL (проверяется отложенный ордер Sell Stop и { // уровень стопа у имеющейся позиции Buy) Price = NP(SellLevel - Tick); // Расчет цены открытия Sell Stop // - 2.1 - =================== Проверка существования Sell Stop ===================== if (SellType == OP_SELLSTOP) // Присутствует SellStop if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Проверка цены открытия if (MathAbs(OrderOpenPrice() - Price) >= Tick) // Если не равна, то изменяется if (WaitForTradeContext()) // Свободен ли торговый поток? { RefreshRates(); // Обновление значений Bid и Ask if (Bid - Price > StopLevel)// Если новая цена достаточно далеко от текущей if (!OrderModify(SellTicket, Price, 0, 0, 0))// то меняется цена открытия return(False); } else return(False); } // - 2.1 - =========================== Окончание блока ============================== // - 2.2 - ==== Проверка существования Buy при уходе цены ниже цены открытия Sell === if (BuyType == OP_BUY && Bid < Price) // Присутствует BUY ниже уровня входа в Sell { CloseOrder(BuyTicket); return(False); } // - 2.2 - =========================== Окончание блока ============================== // - 2.3 - ======== Проверка существования Buy в "нормальных условиях" ============== if (BuyType == OP_BUY) // Если есть Buy, то проверяется расположение его стопа if (OrderSelect(BuyTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Если значение стопа не равно новому значению цены, то стоп изменяется if (MathAbs(OrderStopLoss() - Price) >= Tick) if (WaitForTradeContext()) // свободен ли торговый поток? { RefreshRates(); // Обновление значений Bid и Ask if (Bid - Price > StopLevel) // Новое значение стопа достаточно далеко от if (!OrderModify(BuyTicket, 0, Price, 0, 0)) // текущей цены return(False); } else return(False); } // - 2.3 - =========================== Окончание блока ============================== // - 2.4 - ====================== Установка Sell Stop при его отсутствии ============ if (SellType < 0 && Bid > Price) // Ордер SellStop не установлен if (OpenOrderCorrect(OP_SELLSTOP, Lots, Price, 0, 0) != 0) return(False); // - 2.4 - =========================== Окончание блока ============================== } // - 2 - =============================== Окончание блока ================================ return(True); } //+-------------------------------------------------------------------------------------+ //| Функция поиска своих ордеров | //+-------------------------------------------------------------------------------------+ void FindOrders() { BuyTicket = -1; SellTicket = -1; // Пока не нашли, считаем, что ни одного ордера нет BuyType = -1; SellType = -1; for (int i = OrdersTotal()-1; i >= 0; i--) // Используется весь список ордеров if (OrderSelect(i, SELECT_BY_POS)) // Убедимся, что ордер выбран if (OrderMagicNumber() == MagicNumber && // Ордер открыт экспертом, OrderSymbol() == Symbol()) // который прикреплен к текущей валютной паре { if (MathMod(OrderType(), 2) == 0) // Ордер BUY-типа { BuyTicket = OrderTicket(); // Сохраняем тикет BuyType = OrderType(); // Сохраняем тип } else // Ордер SELL-типа { SellTicket = OrderTicket(); // Сохраняем тикет SellType = OrderType(); // Сохраняем тип } } } //+-------------------------------------------------------------------------------------+ //| Функция start эксперта | //+-------------------------------------------------------------------------------------+ int start() { // - 1 - ========================== Можно ли работать эксперту? ========================= if (!Activate || FatalError) return(0); // - 1 - ================================ Окончание блока =============================== // - 2 - ========================== Контроль открытия нового бара ======================= if (LastBar == Time[0]) return(0); // - 2 - ================================ Окончание блока =============================== // - 3 - ============================== Поиск своих ордеров ============================= FindOrders(); // - 3 - ================================ Окончание блока =============================== // - 4 - ========================== Расчет сигналов открытия и закрытия ================= GetSignal(); // Расчет уровней BuyLevel и SellLevel if (!Trade()) return(0); // Установка и модификация ордеров // - 4 - ================================ Окончание блока =============================== LastBar = Time[0]; return(0); }