//+------------------------------------------------------------------+ //| Expert Advisor: Price_MA_Cross_ATR_Filter | //| Сигналы: цена выше/ниже MA, фильтр по мин. ATR, множитель лота | //| при убытке, трейлинг-стоп с безубытком, вывод информации | //+------------------------------------------------------------------+ #property strict // === Внешние параметры === input double Lots = 0.1; input int StopLoss = 300; input int TakeProfit = 300; input int MA_Period = 14; input int MA_Shift = 0; input int MA_Method = MODE_SMA; input bool ReverseSignal = false; input bool UseLotMultiplier = true; input double LotMultiplier = 2.0; input double MinLot = 0.01; input double MaxLot = 1.0; input bool UseTrailing = true; input int TrailingStart = 20; input int TrailingStop = 10; input int ATR_Period = 14; input double MinATR = 20.0; // мин. ATR в пунктах (0 - отключить) // === Глобальные переменные === static double lastLotMultiplier = 1.0; static int lastOrdersTotal = 0; static ulong lastTicket = 0; //+------------------------------------------------------------------+ //| Вывод информации на график | //+------------------------------------------------------------------+ void ShowInfo() { double atr = iATR(NULL, 0, ATR_Period, 1); if(atr == 0) return; double atrPoints = atr / Point; string info = ""; info += "========== СОВЕТНИК ==========\n"; info += "Сигнал: цена vs MA (период " + (string)MA_Period + ")\n"; info += "Фильтр ATR: " + (MinATR > 0 ? "ВКЛ (мин. " + DoubleToString(MinATR,1) + " пунктов)" : "ВЫКЛ") + "\n"; info += "Текущий ATR = " + DoubleToString(atrPoints,1) + " пунктов (" + DoubleToString(atr,5) + ")\n"; info += "Множитель лота: x" + DoubleToString(lastLotMultiplier,2) + "\n"; info += "Следующий лот: " + DoubleToString(Lots * lastLotMultiplier,2) + "\n"; info += "Открытых позиций: " + (string)OrdersTotal() + "\n"; if(MinATR > 0 && atrPoints < MinATR) info += "!!! ATR НИЖЕ МИНИМУМА - ВХОД ЗАПРЕЩЁН !!!\n"; else if(MinATR > 0) info += "ATR выше минимума - вход разрешён\n"; info += "================================\n"; Comment(info); } //+------------------------------------------------------------------+ //| Проверка последнего закрытого ордера | //+------------------------------------------------------------------+ void CheckLastClosedOrder() { int total = OrdersHistoryTotal(); if(total == 0) return; for(int i = total-1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { if(OrderSymbol() == Symbol() && OrderMagicNumber() == 0) { if(OrderTicket() == lastTicket) break; lastTicket = OrderTicket(); double profit = OrderProfit() + OrderSwap() + OrderCommission(); if(profit < 0) { if(UseLotMultiplier) lastLotMultiplier *= LotMultiplier; } else { lastLotMultiplier = 1.0; } double newLot = Lots * lastLotMultiplier; if(newLot > MaxLot) lastLotMultiplier = MaxLot / Lots; if(newLot < MinLot) lastLotMultiplier = MinLot / Lots; break; } } } } //+------------------------------------------------------------------+ //| Трейлинг-стоп и перевод в безубыток (с проверкой ошибок) | //+------------------------------------------------------------------+ void TrailPositions() { if(!UseTrailing) return; for(int i = 0; i < OrdersTotal(); i++) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderSymbol() != Symbol()) continue; if(OrderMagicNumber() != 0) continue; double openPrice = OrderOpenPrice(); double sl = OrderStopLoss(); double point = Point; if(OrderType() == OP_BUY) { double currentProfit = (Bid - openPrice) / point; if(currentProfit >= TrailingStart) { // --- Передвинуть стоп вслед за ценой --- double newSL = Bid - TrailingStop * point; if(sl == 0 || newSL > sl) { if(!OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrNONE)) Print("Ошибка трейлинга BUY: ", GetLastError()); else Print("Трейлинг BUY: SL передвинут на ", newSL); } // --- Перевод в безубыток (если стоп ещё ниже цены открытия) --- if(sl < openPrice && currentProfit >= TrailingStart) { double breakEven = openPrice + (int)(TrailingStart/2) * point; if(!OrderModify(OrderTicket(), openPrice, breakEven, OrderTakeProfit(), 0, clrNONE)) Print("Ошибка безубытка BUY: ", GetLastError()); else Print("Безубыток BUY"); } } } else if(OrderType() == OP_SELL) { double currentProfit = (openPrice - Ask) / point; if(currentProfit >= TrailingStart) { double newSL = Ask + TrailingStop * point; if(sl == 0 || newSL < sl) { if(!OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrNONE)) Print("Ошибка трейлинга SELL: ", GetLastError()); else Print("Трейлинг SELL: SL передвинут на ", newSL); } if(sl > openPrice && currentProfit >= TrailingStart) { double breakEven = openPrice - (int)(TrailingStart/2) * point; if(!OrderModify(OrderTicket(), openPrice, breakEven, OrderTakeProfit(), 0, clrNONE)) Print("Ошибка безубытка SELL: ", GetLastError()); else Print("Безубыток SELL"); } } } } } } //+------------------------------------------------------------------+ //| Открытие ордера | //+------------------------------------------------------------------+ void OpenOrder(int type) { double price = (type == OP_BUY) ? Ask : Bid; double sl, tp; double currentLot = Lots * lastLotMultiplier; if(currentLot < MinLot) currentLot = MinLot; if(currentLot > MaxLot) currentLot = MaxLot; if(type == OP_BUY) { sl = price - StopLoss * Point; tp = price + TakeProfit * Point; } else { sl = price + StopLoss * Point; tp = price - TakeProfit * Point; } int ticket = OrderSend(Symbol(), type, currentLot, price, 3, sl, tp, "MA_Cross_ATR", 0, 0, clrBlue); if(ticket < 0) Print("Ошибка открытия ордера #", ticket, " : ", GetLastError()); else Print("Открыт ордер #", ticket, " лотом ", currentLot); } //+------------------------------------------------------------------+ //| Основная функция OnTick() | //+------------------------------------------------------------------+ void OnTick() { // Выводим информацию на график всегда (для настройки) ShowInfo(); // --- 1) Проверка закрытых ордеров --- int total = OrdersTotal(); if(total != lastOrdersTotal) { CheckLastClosedOrder(); lastOrdersTotal = total; } // --- 2) Трейлинг --- TrailPositions(); // --- 3) Новый сигнал только если нет позиций --- if(total > 0) return; // --- 4) Фильтр по ATR --- if(MinATR > 0) { double atr = iATR(NULL, 0, ATR_Period, 1); if(atr == 0) return; if(atr / Point < MinATR) return; // вход запрещён } // --- 5) Сигнал по MA --- double ma = iMA(NULL, 0, MA_Period, MA_Shift, MA_Method, PRICE_CLOSE, 1); double price = Close[1]; bool buySignal = (price > ma); bool sellSignal = (price < ma); if(ReverseSignal) { bool tmp = buySignal; buySignal = sellSignal; sellSignal = tmp; } if(buySignal) OpenOrder(OP_BUY); if(sellSignal) OpenOrder(OP_SELL); } //+------------------------------------------------------------------+