Скрипты: Net^atom

 

Net^atom:

Сетка отложенных limit-ордеров с учётом текущей убыточной позиции (последняя версия серии Net, LimitNet+Stop, LimitNet+Stop^atom).

Author: Артур

 

качну, проверю.

Спасибо

[Удален]  

Спасибо, отличный скрипт, приятно работать. Автору респект.

[Удален]  
так, на первый взгляд, много повторяющейся лажи... в общем, дооптить, и может это и будет хорошим коммерческим проектом...
В смысле, всегда рекомендую подумать на возможностью продавать то, что вы творите, тогда и качество заметно подымется
 

Или советник не под тот билд писался или тупо куча ошибок

 
Aliaksei Karalkou:

Или советник не под тот билд писался или тупо куча ошибок

8.5 лет ветке...

 
исправленный код советника . 
//+---------------------------------------------------------------------------------+
//|                                                        Netgatom_Modern.mq4      |
//|                                                        Обновлённая версия 2025  |
//|                                                        Исправлены ошибки,       |
//|                                                        добавлены кнопки         |
//+---------------------------------------------------------------------------------+
#property copyright "Verdi, updated 2025"
#property link      "nemo811@mail.ru"
#property version   "2.00"
#property strict

// Входные параметры
input int    Magic       = 0;      // Уникальный номер ордеров сетки (кроме Stop-ордера)
input double Lot         = 0.01;   // Объём первого рыночного ордера и первого limit-ордера
input int    delta       = 37;     // Шаг сетки (в пунктах)
input int    MaxOrders   = 2;      // Количество limit-ордеров сетки
input int    takeprofit  = 52;     // Уровень TP (0 - не использовать)
input int    zero_tp     = 1;      // Коэффициент поправки TP (0 или 1)
input int    stoploss    = 0;      // Уровень SL (0 - не использовать)
input int    Proskalz    = 3;      // Проскальзывание (в пунктах)

// Глобальные переменные
double SL, TP, Price, DeltaProfitR, DeltaProfitL, LotR, LotL, LotS;
bool   gridSet = false;            // Флаг: сетка уже установлена

// Структура для хранения рыночной информации (чтобы избежать множественных вызовов MarketInfo)
struct MarketInfoStruct
{
   double point;
   double tickSize;
   double tickValue;
   double nominalPoint;
};

//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
{
   // Включаем события мыши для кнопок
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, 1);

   // Создаём кнопки
   CreateButton("btnBuy",  "BUY",  100, 50, clrGreen);
   CreateButton("btnSell", "SELL", 200, 50, clrRed);
   CreateButton("btnCancel", "CANCEL", 300, 50, clrGray);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Удаляем кнопки
   ObjectDelete("btnBuy");
   ObjectDelete("btnSell");
   ObjectDelete("btnCancel");
}

//+------------------------------------------------------------------+
//| Обработка событий графика (нажатие кнопок)                       |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(sparam == "btnBuy")
      {
         // Визуальная обратная связь
         FlashButton("btnBuy", clrBlue);
         // Запускаем логику BUY
         if(!gridSet) ProcessTrading(true);
         else Alert("Сетка уже установлена. Нажмите CANCEL, чтобы сбросить.");
      }
      else if(sparam == "btnSell")
      {
         FlashButton("btnSell", clrBlue);
         if(!gridSet) ProcessTrading(false);
         else Alert("Сетка уже установлена. Нажмите CANCEL, чтобы сбросить.");
      }
      else if(sparam == "btnCancel")
      {
         FlashButton("btnCancel", clrOrange);
         CloseAllOrders();
         gridSet = false;
         Alert("Все ордера удалены.");
      }
   }
}

//+------------------------------------------------------------------+
//| Основная логика установки сетки (вызывается из кнопок)          |
//+------------------------------------------------------------------+
void ProcessTrading(bool isBuy)
{
   // Проверяем наличие отложенных ордеров
   if(CountPendingOrders() > 0)
   {
      Alert("Отмена! Удалите отложенные ордера вручную или нажмите CANCEL.");
      return;
   }

   // Проверяем наличие позиций (рыночных ордеров)
   double buyVolume = GetMarketVolume(OP_BUY);
   double sellVolume = GetMarketVolume(OP_SELL);

   if(buyVolume > 0 || sellVolume > 0)
   {
      // Если объёмы равны – это lock
      if(MathAbs(buyVolume - sellVolume) < 0.0001)
      {
         Alert("Отмена! Позиция Lock (объёмы Buy и Sell равны).");
         return;
      }

      // Проверка направления (чтобы не открывать против большей позиции)
      if(isBuy && buyVolume < sellVolume)
      {
         Alert("Отмена Buy! Объём Sell больше.");
         return;
      }
      if(!isBuy && sellVolume < buyVolume)
      {
         Alert("Отмена Sell! Объём Buy больше.");
         return;
      }
   }

   // Получаем рыночную информацию (тик, пункт и т.д.)
   MarketInfoStruct mi;
   mi.point       = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   mi.tickSize    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   mi.tickValue   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   mi.nominalPoint = mi.tickValue * mi.point / mi.tickSize;

   // Расчёт общего убытка по открытым позициям
   double totalProfit = GetTotalProfit();
   double dy = MathAbs(buyVolume - sellVolume);
   double n = (totalProfit * (-1)) / mi.nominalPoint;

   // Если нет открытых позиций – используем базовый Lot
   if(buyVolume == 0 && sellVolume == 0)
   {
      PlaceInitialOrders(isBuy, 0, Lot);
   }
   else
   {
      PlaceInitialOrders(isBuy, dy, Lot, n);
   }

   gridSet = true; // Помечаем, что сетка установлена
}

//+------------------------------------------------------------------+
//| Размещение начального рыночного ордера и сетки лимитных         |
//+------------------------------------------------------------------+
void PlaceInitialOrders(bool isBuy, double dy, double baseLot, double n = 0)
{
   // --- Рыночный ордер ---
   double lotR = (dy > 0) ? dy : baseLot;
   double tp = 0, sl = 0;
   double priceMarket = (isBuy) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double deltaProfitR = (n != 0) ? n / (dy + dy) : 0;

   if(takeprofit != 0)
   {
      if(isBuy)
         tp = NormalizeDouble(priceMarket + (takeprofit + deltaProfitR * zero_tp) * _Point, _Digits);
      else
         tp = NormalizeDouble(priceMarket - (takeprofit + deltaProfitR * zero_tp) * _Point, _Digits);
   }
   if(stoploss != 0)
   {
      if(isBuy)
         sl = NormalizeDouble(priceMarket - stoploss * _Point, _Digits);
      else
         sl = NormalizeDouble(priceMarket + stoploss * _Point, _Digits);
   }

   // Открываем рыночный ордер
   OpenOrder(isBuy ? OP_BUY : OP_SELL, lotR, priceMarket, sl, tp, "R");

   // --- Лимитные ордера ---
   double sumi = 0;
   for(int i = 1; i <= MaxOrders; i++)
   {
      double lotL = (dy > 0) ? dy * i : baseLot * i;
      sumi += delta * (2 * dy + dy * (i - 2)) * (i - 1) / 2;
      double deltaProfitL = (n != 0) ? (sumi + n + 2 * dy * delta * i) / ((2 * dy + dy * (i - 1)) * i / 2 + 2 * dy) : (sumi + i * delta * baseLot) / ((2 * baseLot + baseLot * (i - 1)) * i / 2 + baseLot);

      double priceLimit;
      if(isBuy)
         priceLimit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - delta * i * _Point, _Digits);
      else
         priceLimit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) + delta * i * _Point, _Digits);

      if(takeprofit != 0)
      {
         if(isBuy)
            tp = NormalizeDouble(priceLimit + (takeprofit + deltaProfitL * zero_tp) * _Point, _Digits);
         else
            tp = NormalizeDouble(priceLimit - (takeprofit + deltaProfitL * zero_tp) * _Point, _Digits);
      }
      if(stoploss != 0)
      {
         if(isBuy)
            sl = NormalizeDouble(priceLimit - stoploss * _Point, _Digits);
         else
            sl = NormalizeDouble(priceLimit + stoploss * _Point, _Digits);
      }

      OpenOrder(isBuy ? OP_BUYLIMIT : OP_SELLLIMIT, lotL, priceLimit, sl, tp, StringFormat("№%d", i));
   }

   // --- Стоп-ордер для локкирования ---
   double lotS = (dy > 0) ? (2 * dy + dy * (MaxOrders - 1)) * MaxOrders / 2 + 2 * dy : (2 * baseLot + baseLot * (MaxOrders - 1)) * MaxOrders / 2 + baseLot;
   double priceStop;
   if(isBuy)
      priceStop = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + delta * (MaxOrders + 1) * _Point, _Digits);
   else
      priceStop = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - delta * (MaxOrders + 1) * _Point, _Digits);

   OpenOrder(isBuy ? OP_SELLSTOP : OP_BUYSTOP, lotS, priceStop, 0, 0, "Lock", 0); // Magic = 0 для лок-ордера
}

//+------------------------------------------------------------------+
//| Универсальная функция открытия ордера (с повторными попытками)  |
//+------------------------------------------------------------------+
void OpenOrder(int type, double volume, double price, double sl, double tp, string comment, int magic = -1)
{
   if(magic == -1) magic = Magic; // по умолчанию используем глобальный Magic

   int ticket = -1;
   int attempts = 0;
   while(ticket < 0 && attempts < 10)
   {
      RefreshRates();
      if(type == OP_BUY)
         ticket = OrderSend(_Symbol, type, volume, SymbolInfoDouble(_Symbol, SYMBOL_ASK), Proskalz, sl, tp, comment, magic, 0, clrGreen);
      else if(type == OP_SELL)
         ticket = OrderSend(_Symbol, type, volume, SymbolInfoDouble(_Symbol, SYMBOL_BID), Proskalz, sl, tp, comment, magic, 0, clrRed);
      else
         ticket = OrderSend(_Symbol, type, volume, price, Proskalz, sl, tp, comment, magic, 0, clrYellow);

      if(ticket < 0)
      {
         Print("Ошибка отправки ордера: ", GetLastError());
         attempts++;
         Sleep(500);
      }
   }
   if(ticket < 0)
      Alert("Не удалось открыть ордер после 10 попыток.");
   else
      Print("Ордер открыт: ", comment);
}

//+------------------------------------------------------------------+
//| Подсчёт отложенных ордеров по символу                           |
//+------------------------------------------------------------------+
int CountPendingOrders()
{
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol && OrderType() > OP_SELL)
            count++;
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| Суммарный объём рыночных ордеров заданного типа                  |
//+------------------------------------------------------------------+
double GetMarketVolume(int opType)
{
   double vol = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol && OrderType() == opType)
            vol += OrderLots();
      }
   }
   return vol;
}

//+------------------------------------------------------------------+
//| Суммарная прибыль по всем рыночным ордерам символа               |
//+------------------------------------------------------------------+
double GetTotalProfit()
{
   double profit = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol && (OrderType() == OP_BUY || OrderType() == OP_SELL))
            profit += OrderProfit();
      }
   }
   return profit;
}

//+------------------------------------------------------------------+
//| Удаление всех ордеров по символу                                 |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == _Symbol)
         {
            if(OrderType() <= OP_SELL) // рыночный ордер
               OrderClose(OrderTicket(), OrderLots(), OrderType() == OP_BUY ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK), Proskalz, clrWhite);
            else // отложенный
               OrderDelete(OrderTicket());
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Создание кнопки                                                  |
//+------------------------------------------------------------------+
void CreateButton(string name, string text, int x, int y, color bgColor)
{
   ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, 80);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, 30);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bgColor);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrBlack);
   ChartRedraw();
}

//+------------------------------------------------------------------+
//| Мигание кнопки при нажатии                                       |
//+------------------------------------------------------------------+
void FlashButton(string name, color flashColor)
{
   color original = (color)ObjectGetInteger(0, name, OBJPROP_BGCOLOR);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, flashColor);
   ChartRedraw();
   Sleep(300);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, original);
   ChartRedraw();
}

//+------------------------------------------------------------------+
//| OnTick – пустой, так как логика только по кнопкам               |
//+------------------------------------------------------------------+
void OnTick()
{
   // Ничего не делаем
}
//+------------------------------------------------------------------+