#property copyright "Scritong" #property link "scriptong@mail.ru" //---- input parameters extern double Lots = 0.1; extern double TPPercent = 80; // Прибыль в процентах от разницы начала первой волны и // конца второй волны extern string OpenOrderSound = "ok.wav"; // Звуковой сигнал при открытии позиции extern int MagicNumber = 11237; // Уникальный идентификатор своих ордеров int Signal, // Текущий сигнал BuyTicket, // Идентификатор ордера BUY или BUYSTOP SellTicket, // Идентификатор ордера SELL или SELLSTOP BuyType, // Тип ордера BUY или BUYSTOP SellType; // Тип ордера SELL или SELLSTOP bool Activate, FreeMarginAlert, FatalError; double Tick, Spread, StopLevel, MinLot, MaxLot, LotStep, FreezeLevel; int FN, // Номер бара, на котором зафиксирован первый (считается справа налево) фрактал SN, // Номер бара, на котором зафиксирован второй фрактал TN, // Номер бара, на котором зафиксирован третий фрактал FT, // Тип первого фрактала: 0 - нижний, 1 - верхний ST, // Тип второго фрактала TT; // Тип третьего фрактала double FV, // Значение цены первого фрактала SV, // Значение цены второго фрактала TV; // Значение цены третьего фрактала datetime LastBar; // Время открытия последнего посчитанного бара //+-------------------------------------------------------------------------------------+ //| Функция инициализации эксперта | //+-------------------------------------------------------------------------------------+ 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 (TPPercent < 10) { Comment("Значение TPPercent не может быть меньше 10. Советник отключен!"); Print("Значение TPPercent не может быть меньше 10. Советник отключен!"); return(0); } // - 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); } //+-------------------------------------------------------------------------------------+ //| Генерация сигналов покупки и продажи | //+-------------------------------------------------------------------------------------+ bool GetSignal() { // - 1 - ================================ Инициализация перменных ======================= FN = 0; SN = 0; TN = 0; // - 1 - ==================================== Окончание блока =========================== for (int i = 3; i < Bars && TN == 0; i++) { // - 2 - ================================ Поиск "верхних" фракталов ===================== double Fr = iFractals(NULL, 0, MODE_UPPER, i); // Получение значения "верхнего" // фрактала на свече №i if (Fr != 0)// Если фрактал найден, то необходимо найти место для записи его значений if (FN == 0) // Является ли найденный фрактал первым? { // Если является, то FN = i; // запоминается номер бара, FV = Fr; // значение цены FT = 1; // и тип фрактала continue; // если на этом баре существует "нижний" фрактал, он игнорируется } else // Найденный фрактал уже не первый if (SN == 0) // Может быть этот фрактал второй? { // Если этот фрактал второй, то if (FT == 0) // согласно стратегии нужно проверить, был ли первый фрактал { // "нижним". Если это действительно так, то все в порядке и можно SN = i; // запомнить номер бара SV = Fr; // цену формирования ST = 1; // и тип фрактала continue; // если на этом баре существует "нижний" фрактал, он игнорируется } else // первый фрактал был "верхним", что не вписывается в правила стратегии return(False); // подается сигнал о том, что канал не сформирован } else // Найденный фрактал даже не второй if (TN == 0) // Тогда может это третий фрактал? { // Если этот фрактал третий, то if (FT == 1 && ST == 0) // Первый - должен был быть "верхним", второй - { // "нижним". Только в таком случае производится запись фрактала TN = i; // Сохранение номере бара, TV = Fr; // цены формирования фрактала и TT = 1; // его типа continue;//если на этом баре существует "нижний" фрактал, он игнорируется } else // Третий фрактал не соответствует стратегии return(False); // подается сигнал о том, что канал не сформирован } else break; // Все фракталы найдены, следует прервать цикл поиска // - 2 - ==================================== Окончание блока =========================== // - 3 - ================================ Поиск "нижних" фракталов ====================== Fr = iFractals(NULL, 0, MODE_LOWER, i); // Получение значения "нижнего" // фрактала на свече №i if (Fr != 0)// Если фрактал найден, то необходимо найти место для записи его значений if (FN == 0) // Является ли найденный фрактал первым? { // Если является, то FN = i; // запоминается номер бара, FV = Fr; // значение цены FT = 0; // и тип фрактала } else // Найденный фрактал уже не первый if (SN == 0) // Может быть этот фрактал второй? { // Если этот фрактал второй, то if (FT == 1 && FN != i) // согласно стратегии нужно проверить, был ли первый { // фрактал "верхним". Если это действительно так, то все в порядке и можно SN = i; // запомнить номер бара SV = Fr; // цену формирования ST = 0; // и тип фрактала } else // первый фрактал был "нижним", что не вписывается в правила стратегии return(False); // Не выполнено условие чередования фракталов, канал не } // сформирован else // Найденный фрактал даже не второй if (TN == 0) // Тогда может это третий фрактал? { // Если этот фрактал третий, то if (FT == 0 && ST == 1) // Первый - должен был быть "нижним", второй - { // "верхним". Только в таком случае производится запись фрактала TN = i; // Сохранение номере бара, TV = Fr; // цены формирования фрактала и TT = 0; // его типа } else // Третий фрактал не соответствует стратегии return(False); // подается сигнал о том, что канал не сформирован } else break; // Все фракталы найдены, следует прервать цикл поиска } // - 3 - ==================================== Окончание блока =========================== // - 4 - ================================ Генерация сигнала продажи ===================== if (FT == 1) // Если первый фрактал - "верхний", то молния вниз, сигнал в ту же сторону { if (FV > (TV + SV)/2) // Первый фрактал не должен быть выше середины первой волны return(False); // иначе это не соответствует правилам стратегии if (Close[FN-1] >= Close[FN]) // Свеча после формирования второй волны не должна return(False); // закрыться выше закрытия свечи второй волны for (i = TT-1; i >= ST; i--) // Проверка всех баров первой волны // средняя на протяжении первой волны должна быть нисходящей if (iMA(NULL, 0, 2, 0, MODE_EMA, PRICE_CLOSE, i) > iMA(NULL, 0, 2, 0, MODE_EMA, PRICE_CLOSE, i+1)) return(False); // Если замечен рост - молния не подходит } // - 4 - ==================================== Окончание блока =========================== // - 5 - ================================ Генерация сигнала покупки ===================== else // Если первый фрактал - "нижний", то молния вверх, сигнал в ту же сторону { if (FV < (TV + SV)/2) // Первый фрактал не должен быть ниже середины первой волны return(False); if (Close[FN-1] <= Close[FN]) // Свеча после формирования второй волны не должна return(False); // закрыться ниже закрытия свечи второй волны for (i = TT-1; i >= ST; i--) // Проверка всех баров первой волны // средняя на протяжении первой волны должна быть восходящей if (iMA(NULL, 0, 2, 0, MODE_EMA, PRICE_CLOSE, i) < iMA(NULL, 0, 2, 0, MODE_EMA, PRICE_CLOSE, i+1)) return(False); // Если замечен спад - молния не подходит } // - 5 - ==================================== Окончание блока =========================== return(True); // Сигнал успешно сгенерирован } //+-------------------------------------------------------------------------------------+ //| Закрытие заданного рыночного ордера | //+-------------------------------------------------------------------------------------+ 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 DeleteOrder(int Ticket) { if (OrderSelect(Ticket, SELECT_BY_TICKET) && // Существует ордер с заданным тикетом и OrderCloseTime() == 0) // ордер не закрыт if (WaitForTradeContext()) // Свободен ли торговый поток? { RefreshRates(); if (OrderType() == OP_BUYSTOP) // Если следует удалить BUYSTOP if (OrderOpenPrice() - Ask <= FreezeLevel) // то проверяется удаленность цены от return(False); // ASK if (OrderType() == OP_SELLSTOP) // Если следует удалить SELLSTOP if (Bid - OrderOpenPrice() <= FreezeLevel) // то проверяется удаленность цены от return(False); // BID if (!OrderDelete(OrderTicket())) // Если сделку не удалось закрыть, return(False); // то результат функции - False } return(True); // Удаление прошло успешно } //+-------------------------------------------------------------------------------------+ //| Открытие позиций и установка ордеров | //+-------------------------------------------------------------------------------------+ bool Trade() { // - 1 - ============================= Открытие длинной позиции ========================= if (FT == 0) // Сигнал BUY { double Price = NP(SV + Spread + Tick); double SL = NP(FV - Tick); double TP = NP(SV + (FV - TV)*TPPercent/100); if (High[iHighest(NULL, 0, MODE_HIGH, FN)] >= TP) return(True); if (BuyType == OP_BUYSTOP) // Присутствует BuyStop if (OrderSelect(BuyTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Проверка цены открытия, стопа и профита if (MathAbs(OrderOpenPrice() - Price) >= Tick) if (WaitForTradeContext()) { RefreshRates(); if (Price - Ask > StopLevel && Price - SL > StopLevel && TP - Price > StopLevel) if (!OrderModify(BuyTicket, Price, SL, TP, 0)) return(False); } else return(False); return(True); } if (BuyType == OP_BUY) // Присутствует Buy if (OrderSelect(BuyTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Проверка стопа и профита if (MathAbs(OrderStopLoss() - SL) >= Tick || MathAbs(OrderTakeProfit() - TP) >= Tick) if (WaitForTradeContext()) { RefreshRates(); if (Bid - SL > StopLevel && TP - Bid > StopLevel) if (!OrderModify(BuyTicket, 0, SL, TP, 0)) return(False); } else return(False); return(True); } if (SellType > 0) // Если есть SELL или SELLSTOP { // Удаляем или закрываем if (SellType == OP_SELL) if (!CloseOrder(SellTicket)) return(False); if (SellType == OP_SELLSTOP) if (!DeleteOrder(SellTicket)) return(False); } if (OpenOrderCorrect(OP_BUYSTOP, Lots, Price, // Открытие BUY SL, TP) != 0) return(False); } // - 1 - =============================== Окончание блока ================================ // - 2 - =========================== Открытие короткой позиции ========================== if (FT == 1) // Сигнал SELL { Price = NP(SV - Tick); SL = NP(FV + Spread + Tick); TP = NP(SV - (TV - FV)*TPPercent/100); if (Low[iLowest(NULL, 0, MODE_LOW, FN)] <= TP - Spread) return(True); if (SellType == OP_SELLSTOP) // Присутствует SellStop if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Проверка цены открытия, стопа и профита if (MathAbs(OrderOpenPrice() - Price) >= Tick) if (WaitForTradeContext()) { RefreshRates(); if (Bid - Price > StopLevel && SL - Price > StopLevel && Price - TP > StopLevel) if (!OrderModify(SellTicket, Price, SL, TP, 0)) return(False); } else return(False); return(True); } if (SellType == OP_SELL) // Присутствует SELL if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) { // Проверка стопа и профита if (MathAbs(OrderStopLoss() - SL) >= Tick || MathAbs(OrderTakeProfit() - TP) >= Tick) if (WaitForTradeContext()) { RefreshRates(); if (SL - Ask > StopLevel && Ask - TP > StopLevel) if (!OrderModify(SellTicket, 0, SL, TP, 0)) return(False); } else return(False); return(True); } if (BuyType >= 0) // Если есть Buy или BuyStop { // Удаляем или закрываем if (BuyType == OP_BUY) if (!CloseOrder(BuyTicket)) return(False); if (BuyType == OP_BUYSTOP) if (!DeleteOrder(BuyTicket)) return(False); } if (OpenOrderCorrect(OP_SELLSTOP, Lots, Price, // Открытие SELL SL, TP) != 0) return(False); } // - 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 - ========================== Расчет сигналов открытия и закрытия ================= if (GetSignal()) // Если молния найдена { // то производится попытка установки ордера if (!Trade()) return(0); } // - 4 - ================================ Окончание блока =============================== LastBar = Time[0]; return(0); }