untitled

Pasted Oct 16, 2026, 10:46 AM1 file435 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgsv51p7fje1t1x
untitled
//+------------------------------------------------------------------+
//|                                           SmartTrailAlert_v2.mq5 |
//|           Ultra-Light Trailing Alerts + Auto Pre-TP/SL Detector  |
//+------------------------------------------------------------------+
#property copyright "Custom Trading Tool"
#property version   "2.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//--- حالت‌های تریل شدن آلارت سقف و کف
enum ENUM_TRAIL_MODE
  {
   TRAIL_EXTREME_PULLBACK = 0, // ۱. هوشمند: تعقیب سقف/کف جدید و مسلح شدن در پولبک
   TRAIL_STEP             = 1, // ۲. پله‌ای: جابجایی فوری به اندازه گام مشخص پس از برخورد
   TRAIL_FRACTAL_ONLY     = 2  // ۳. فرکتالی: آپدیت شدن فقط با تشکیل سقف/کف فرکتال جدید
  };

//--- تنظیمات ورودی (Inputs)
input group "=== ۱. تنظیمات خطوط سقف و کف (High/Low) ==="
input ENUM_TRAIL_MODE InpTrailMode        = TRAIL_EXTREME_PULLBACK; // نوع تریل شدن خطوط
input int             InpInitDistPoints   = 150;   // فاصله اولیه خطوط از قیمت (Points)
input int             InpPullbackPoints   = 30;    // مقدار پولبک برای قفل شدن سقف/کف جدید (Points)
input int             InpOffsetPoints     = 5;     // فاصله اطمینان از سقف/کف جدید (Buffer Points)
input bool            InpTrailWithFractal = false; // تنگ‌تر شدن خودکار خطوط با فرکتال‌های جدید؟

input group "=== ۲. تنظیمات هشدار قبل از TP و SL ==="
input bool            InpEnablePreTPSL    = true;  // فعال‌سازی هشدار خودکار قبل از TP و SL؟
input int             InpPreAlertPoints   = 100;   // فاصله هشدار قبل از TP/SL به پوینت (100 پوینت = 10 پیپ)
input bool            InpShowPreLines     = true;  // نمایش خطوط هشدار قبل از TP/SL روی چارت؟
input color           InpColorPreTP       = clrMediumSeaGreen; // رنگ خط هشدار قبل از TP
input color           InpColorPreSL       = clrOrangeRed;      // رنگ خط هشدار قبل از SL

input group "=== ۳. تنظیمات صداها و نوتیفیکیشن ==="
input string          InpSoundHigh        = "alert.wav";   // صدای شکست سقف (High)
input string          InpSoundLow         = "timeout.wav"; // صدای شکست کف (Low)
input string          InpSoundPreTP       = "ok.wav";      // صدای نزدیک شدن به TP
input string          InpSoundPreSL       = "alert2.wav";  // صدای نزدیک شدن به SL
input bool            InpPopupAlert       = false;         // نمایش پنجره پاپ‌آپ (Alert Window)؟
input bool            InpSendPush         = false;         // ارسال نوتیفیکیشن به موبایل (Push)؟

input group "=== ۴. تنظیمات ظاهری و پنل ==="
input color           InpColorHigh        = clrDodgerBlue; // رنگ خط بالا (سقف)
input color           InpColorLow         = clrCrimson;    // رنگ خط پایین (کف)
input int             InpLineWidth        = 2;             // ضخامت خطوط اصلی
input bool            InpShowPanel        = true;          // نمایش دکمه‌های کنترلی روی چارت؟

//--- ثابت‌های نام اشیاء گرافیکی
#define PREFIX         "STA_"
#define PREFIX_PRE     "STA_PRE_"
#define LINE_HIGH      "STA_Line_High"
#define LINE_LOW       "STA_Line_Low"
#define BTN_RESET      "STA_Btn_Reset"
#define BTN_MUTE       "STA_Btn_Mute"
#define LBL_STATUS     "STA_Lbl_Status"

//--- ساختار کش کردن وضعیت پوزیشن‌ها در رم (برای جلوگیری از کندی متاتریدر)
struct SPositionState
  {
   ulong             ticket;
   long              type;       // POSITION_TYPE_BUY یا POSITION_TYPE_SELL
   double            tp;
   double            sl;
   double            preTpPrice; // قیمت خط هشدار قبل از TP
   double            preSlPrice; // قیمت خط هشدار قبل از SL
   bool              tpArmed;    // آیا هشدار TP مسلح است؟
   bool              slArmed;    // آیا هشدار SL مسلح است؟
  };

SPositionState g_posCache[];

//--- متغیرهای وضعیت داخلی
double   g_priceHigh     = 0.0;
double   g_priceLow      = 0.0;
bool     g_trackHigh     = false;
bool     g_trackLow      = false;
bool     g_isMuted       = false;
ulong    g_lastAlertHigh = 0;
ulong    g_lastAlertLow  = 0;
ulong    g_lastPosScan   = 0;
datetime g_lastBarTime   = 0;

//+------------------------------------------------------------------+
//| مقداردهی اولیه اندیکاتور                                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   // حفظ موقعیت خطوط اصلی در صورت تغییر تایم‌فریم
   if(ObjectFind(0, LINE_HIGH) >= 0 && ObjectFind(0, LINE_LOW) >= 0)
     {
      g_priceHigh = ObjectGetDouble(0, LINE_HIGH, OBJPROP_PRICE, 0);
      g_priceLow  = ObjectGetDouble(0, LINE_LOW, OBJPROP_PRICE, 0);
     }
   else
     {
      g_priceHigh = NormalizeDouble(bid + InpInitDistPoints * _Point, _Digits);
      g_priceLow  = NormalizeDouble(bid - InpInitDistPoints * _Point, _Digits);
      CreateOrUpdateLine(LINE_HIGH, g_priceHigh, InpColorHigh, STYLE_SOLID, InpLineWidth, true, "High Alert (Armed)");
      CreateOrUpdateLine(LINE_LOW,  g_priceLow,  InpColorLow,  STYLE_SOLID, InpLineWidth, true, "Low Alert (Armed)");
     }

   if(InpShowPanel)
      CreateControlPanel();

   if(InpEnablePreTPSL)
      SyncPositionsToCache(bid, SymbolInfoDouble(_Symbol, SYMBOL_ASK));

   UpdateStatusLabel(bid);
   ChartRedraw(0);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| پاکسازی اشیاء گرافیکی                                            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // خطوط پیش از TP/SL همیشه در بازخوانی مجدد ساخته می‌شوند
   ObjectsDeleteAll(0, PREFIX_PRE);

   // خطوط اصلی فقط در صورت حذف اندیکاتور یا بستن چارت پاک می‌شوند
   if(reason == REASON_REMOVE || reason == REASON_CHARTCLOSE)
     {
      ObjectsDeleteAll(0, PREFIX);
     }
   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| حلقه اصلی محاسبات                                                |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   if(rates_total < 5)
      return(0);

   int curr = rates_total - 1;
   double bid = close[curr];
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double offset = InpOffsetPoints * _Point;
   double pullback = InpPullbackPoints * _Point;
   bool needRedraw = false;

   //--- ۱. بررسی باز شدن کندل جدید (فرکتال‌ها و سلامت خطوط)
   if(time[curr] != g_lastBarTime)
     {
      g_lastBarTime = time[curr];

      if(ObjectFind(0, LINE_HIGH) < 0)
         CreateOrUpdateLine(LINE_HIGH, g_priceHigh, InpColorHigh, g_trackHigh ? STYLE_DOT : STYLE_SOLID, InpLineWidth, true, "High Alert");
      if(ObjectFind(0, LINE_LOW) < 0)
         CreateOrUpdateLine(LINE_LOW, g_priceLow, InpColorLow, g_trackLow ? STYLE_DOT : STYLE_SOLID, InpLineWidth, true, "Low Alert");

      if((InpTrailWithFractal || InpTrailMode == TRAIL_FRACTAL_ONLY) && rates_total > 5)
        {
         int f = rates_total - 3;
         if(high[f] > high[f-1] && high[f] > high[f-2] && high[f] >= high[f+1] && high[f] > bid)
           {
            double newHigh = NormalizeDouble(high[f] + offset, _Digits);
            if(newHigh < g_priceHigh || InpTrailMode == TRAIL_FRACTAL_ONLY)
              {
               g_priceHigh = newHigh;
               g_trackHigh = false;
               CreateOrUpdateLine(LINE_HIGH, g_priceHigh, InpColorHigh, STYLE_SOLID, InpLineWidth, true, "High Alert (Swing)");
               needRedraw = true;
              }
           }
         if(low[f] < low[f-1] && low[f] < low[f-2] && low[f] <= low[f+1] && low[f] < bid)
           {
            double newLow = NormalizeDouble(low[f] - offset, _Digits);
            if(newLow > g_priceLow || InpTrailMode == TRAIL_FRACTAL_ONLY)
              {
               g_priceLow = newLow;
               g_trackLow = false;
               CreateOrUpdateLine(LINE_LOW, g_priceLow, InpColorLow, STYLE_SOLID, InpLineWidth, true, "Low Alert (Swing)");
               needRedraw = true;
              }
           }
        }
     }

   //--- ۲. منطق خط بالا (سقف قیمت)
   if(!g_trackHigh)
     {
      if(bid >= g_priceHigh)
        {
         TriggerCustomAlert(InpSoundHigh, "HIGH BROKEN (سقف شکسته شد)", bid, true, false);

         if(InpTrailMode == TRAIL_STEP)
           {
            g_priceHigh = NormalizeDouble(bid + pullback, _Digits);
            ObjectSetDouble(0, LINE_HIGH, OBJPROP_PRICE, 0, g_priceHigh);
           }
         else
           {
            g_trackHigh = true;
            g_priceHigh = NormalizeDouble(MathMax(bid, high[curr]) + offset, _Digits);
            CreateOrUpdateLine(LINE_HIGH, g_priceHigh, InpColorHigh, STYLE_DOT, InpLineWidth, true, "Tracking New High...");
           }
         needRedraw = true;
        }
     }
   else
     {
      if(bid + offset > g_priceHigh)
        {
         g_priceHigh = NormalizeDouble(bid + offset, _Digits);
         ObjectSetDouble(0, LINE_HIGH, OBJPROP_PRICE, 0, g_priceHigh);
         needRedraw = true;
        }
      else if(g_priceHigh - bid >= pullback + offset)
        {
         g_trackHigh = false;
         CreateOrUpdateLine(LINE_HIGH, g_priceHigh, InpColorHigh, STYLE_SOLID, InpLineWidth, true, "High Alert (Armed)");
         needRedraw = true;
        }
     }

   //--- ۳. منطق خط پایین (کف قیمت)
   if(!g_trackLow)
     {
      if(bid <= g_priceLow)
        {
         TriggerCustomAlert(InpSoundLow, "LOW BROKEN (کف شکسته شد)", bid, false, true);

         if(InpTrailMode == TRAIL_STEP)
           {
            g_priceLow = NormalizeDouble(bid - pullback, _Digits);
            ObjectSetDouble(0, LINE_LOW, OBJPROP_PRICE, 0, g_priceLow);
           }
         else
           {
            g_trackLow = true;
            g_priceLow = NormalizeDouble(MathMin(bid, low[curr]) - offset, _Digits);
            CreateOrUpdateLine(LINE_LOW, g_priceLow, InpColorLow, STYLE_DOT, InpLineWidth, true, "Tracking New Low...");
           }
         needRedraw = true;
        }
     }
   else
     {
      if(bid - offset < g_priceLow)
        {
         g_priceLow = NormalizeDouble(bid - offset, _Digits);
         ObjectSetDouble(0, LINE_LOW, OBJPROP_PRICE, 0, g_priceLow);
         needRedraw = true;
        }
      else if(bid - g_priceLow >= pullback + offset)
        {
         g_trackLow = false;
         CreateOrUpdateLine(LINE_LOW, g_priceLow, InpColorLow, STYLE_SOLID, InpLineWidth, true, "Low Alert (Armed)");
         needRedraw = true;
        }
     }

   //--- ۴. بررسی پوزیشن‌ها و هشدارهای Pre-TP و Pre-SL
   if(InpEnablePreTPSL)
     {
      ulong now = GetTickCount64();
      if(now - g_lastPosScan >= 500)
        {
         g_lastPosScan = now;
         if(SyncPositionsToCache(bid, ask))
            needRedraw = true;
        }

      if(CheckPreTPSLAlerts(bid, ask, pullback))
         needRedraw = true;
     }

   if(InpShowPanel)
      UpdateStatusLabel(bid);

   if(needRedraw)
      ChartRedraw(0);

   return(rates_total);
  }

//+------------------------------------------------------------------+
//| همگام‌سازی پوزیشن‌های باز نماد فعلی با حافظه کش                   |
//+------------------------------------------------------------------+
bool SyncPositionsToCache(double bid, double ask)
  {
   bool changed = false;
   double preDist = InpPreAlertPoints * _Point;

   int total = PositionsTotal();
   SPositionState newCache[];
   ArrayResize(newCache, 0);

   for(int i = 0; i < total; i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0)
         continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol)
         continue;

      long   type = PositionGetInteger(POSITION_TYPE);
      double tp   = NormalizeDouble(PositionGetDouble(POSITION_TP), _Digits);
      double sl   = NormalizeDouble(PositionGetDouble(POSITION_SL), _Digits);

      double checkPrice = (type == POSITION_TYPE_BUY) ? bid : ask;
      double preTp = 0.0;
      double preSl = 0.0;

      if(tp > 0.0)
         preTp = NormalizeDouble((type == POSITION_TYPE_BUY) ? (tp - preDist) : (tp + preDist), _Digits);
      if(sl > 0.0)
         preSl = NormalizeDouble((type == POSITION_TYPE_BUY) ? (sl + preDist) : (sl - preDist), _Digits);

      int oldIdx = -1;
      int oldSize = ArraySize(g_posCache);
      for(int j = 0; j < oldSize; j++)
        {
         if(g_posCache[j].ticket == ticket)
           {
            oldIdx = j;
            break;
           }
        }

      int idx = ArraySize(newCache);
      ArrayResize(newCache, idx + 1);
      newCache[idx].ticket     = ticket;
      newCache[idx].type       = type;
      newCache[idx].tp         = tp;
      newCache[idx].sl         = sl;
      newCache[idx].preTpPrice = preTp;
      newCache[idx].preSlPrice = preSl;

      if(oldIdx >= 0)
        {
         if(MathAbs(g_posCache[oldIdx].tp - tp) < _Point * 0.5)
            newCache[idx].tpArmed = g_posCache[oldIdx].tpArmed;
         else
           {
            newCache[idx].tpArmed = IsOutsidePreZone(type, true, checkPrice, preTp);
            changed = true;
           }

         if(MathAbs(g_posCache[oldIdx].sl - sl) < _Point * 0.5)
            newCache[idx].slArmed = g_posCache[oldIdx].slArmed;
         else
           {
            newCache[idx].slArmed = IsOutsidePreZone(type, false, checkPrice, preSl);
            changed = true;
           }
        }
      else
        {
         newCache[idx].tpArmed = IsOutsidePreZone(type, true, checkPrice, preTp);
         newCache[idx].slArmed = IsOutsidePreZone(type, false, checkPrice, preSl);
         changed = true;
        }

      if(InpShowPreLines)
        {
         string tpLineName = PREFIX_PRE + "TP_" + IntegerToString(ticket);
         string slLineName = PREFIX_PRE + "SL_" + IntegerToString(ticket);

         if(preTp > 0.0)
            CreateOrUpdateLine(tpLineName, preTp, InpColorPreTP, newCache[idx].tpArmed ? STYLE_DASH : STYLE_DOT, 1, false, "Pre-TP Alert #" + IntegerToString(ticket));
         else if(ObjectFind(0, tpLineName) >= 0)
            ObjectDelete(0, tpLineName);

         if(preSl > 0.0)
            CreateOrUpdateLine(slLineName, preSl, InpColorPreSL, newCache[idx].slArmed ? STYLE_DASH : STYLE_DOT, 1, false, "Pre-SL Alert #" + IntegerToString(ticket));
         else if(ObjectFind(0, slLineName) >= 0)
            ObjectDelete(0, slLineName);
        }
     }

   int oldSize = ArraySize(g_posCache);
   for(int j = 0; j < oldSize; j++)
     {
      bool stillOpen = false;
      for(int k = 0; k < ArraySize(newCache); k++)
        {
         if(g_posCache[j].ticket == newCache[k].ticket)
           {
            stillOpen = true;
            break;
           }
        }
      if(!stillOpen)
        {
         ObjectDelete(0, PREFIX_PRE + "TP_" + IntegerToString(g_posCache[j].ticket));
         ObjectDelete(0, PREFIX_PRE + "SL_" + IntegerToString(g_posCache[j].ticket));
         changed = true;
        }
     }

   ArrayCopy(g_posCache, newCache);
   if(ArraySize(newCache) == 0 && oldSize > 0)
      ArrayResize(g_posCache, 0);

   return(changed);
  }

//+------------------------------------------------------------------+
//| بررسی لحظه‌ای برخورد قیمت با خطوط هشدار Pre-TP و Pre-SL          |
//+------------------------------------------------------------------+
bool CheckPreTPSLAlerts(double bid, double ask, double pullback)
  {
   bool changed = false;
   int size = ArraySize(g_posCache);

   for(int i = 0; i < size; i++)
     {
      double checkPrice = (g_posCache[i].type == POSITION_TYPE_BUY) ? bid : ask;
      string tpLineName = PREFIX_PRE + "TP_" + IntegerToString(g_posCache[i].ticket);
      string slLineName = PREFIX_PRE + "SL_" + IntegerToString(g_posCache[i].ticket);

      if(g_posCache[i].preTpPrice > 0.0)
        {
         if(g_posCache[i].tpArmed)
           {
            bool hitPreTP = (g_posCache[i].type == POSITION_TYPE_BUY) ?
                            (checkPrice >= g_posCache

16,214 chars · codepastes.com