Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgsv51p7fje1t1xuntitledPlain Text
//+------------------------------------------------------------------+
//| 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