#property strict
#property indicator_chart_window
#property indicator_plots   0
#property indicator_buffers 0

//---------------- فلتر الإشارات ----------------
enum ENUM_SIGNAL_FILTER
{
   FILTER_ALL = 0,     // عرض كل الإشارات
   FILTER_NQH,         // عرض NQH فقط
   FILTER_NQF,         // عرض NQF فقط
   FILTER_NQKH         // عرض NQKH فقط
};

//---------------- زاوية عرض لوحة المعلومات (الداشبورد) ----------------
enum ENUM_DASH_CORNER
{
   DASH_TOP_LEFT = 0,     // أعلى اليسار
   DASH_TOP_RIGHT = 1,    // أعلى اليمين
   DASH_BOTTOM_LEFT = 2,  // أسفل اليسار
   DASH_BOTTOM_RIGHT = 3  // أسفل اليمين
};

//---------------- الإعدادات العامة ----------------
input bool   InpEnableIndicator   = true;     // تفعيل / تعطيل المؤشر بالكامل
input bool   InpShowPrimaryGroup  = true;     // تفعيل/تعطيل مجموعة "فريمك الحالي" فقط (الصندوق + الفيبو + مسار المتجه + أسهم NQ) — مستقلة عن الداشبورد وسلّم التوافق وMTF والمسار الكسري والوحدات الإضافية، والتي تستمر بالعمل حتى لو أُطفئت هذه
input int    InpUnitsToShow       = 20;       // عدد الوحدات السابقة المكتملة المطلوب رسمها
input int    InpLayersEachSide    = 2;        // عدد طبقات الفيبوناتشي أعلى وأسفل (لكل جهة)

// خطوط مستويات الفيبو: مفتاح رئيسي + تحكم كامل ومستقل لكل مجموعة (إظهار، سماكة، نمط)
input bool             InpShowFibLevels = true;       // مفتاح رئيسي: إظهار/إخفاء كل خطوط الفيبوناتشي دفعة واحدة
input int    InpVLineWidth        = 1;        // سماكة الخط الرأسي (حدود كل وحدة)

// الشفافية: 0 = صلب تمامًا (كما هو أصليًا)، 100 = شفاف جدًا (يمتزج مع الخلفية)
input int    InpOpacityPercent    = 70;       // شفافية الخطوط (0 = صلب، 100 = شفاف جدًا)
input int    InpTextOpacityPercent = 0;       // شفافية نص النسب المئوية (0 = صلب، 100 = شفاف جدًا) — مستقلة عن شفافية الخطوط
input color  InpBgColor           = clrBlack; // لون الخلفية المستخدم في حساب الشفافية

// ألوان الفيبوناتشي (يتم مزجها مع الشفافية أعلاه)
input color  InpColor_0_100       = clrDodgerBlue;  // لون مستويي 0 و 100
input color  InpColor_23_76       = clrMagenta;     // لون مستويي 23.6 و 76.4
input color  InpColor_38_61       = clrGold;        // لون مستويي 38.2 و 61.8
input color  InpColor_50          = clrTeal;        // لون مستوى 50
input color  InpColor_Ext         = clrMagenta;     // لون الامتدادات -23.6 و 123.6 (وطبقاتها)

// تحكم كامل ومستقل لكل مجموعة (إظهار / سماكة / نمط)
input bool             InpShow_0_100    = true;        // إظهار مستويي 0 و 100
input int               InpWidth_0_100   = 2;           // سماكة خطي 0 و 100
input ENUM_LINE_STYLE   InpStyle_0_100   = STYLE_SOLID;  // نمط خطي 0 و 100

input bool             InpShow_23_76    = true;        // إظهار مستويي 23.6 و 76.4
input int               InpWidth_23_76   = 1;           // سماكة خطي 23.6 و 76.4
input ENUM_LINE_STYLE   InpStyle_23_76   = STYLE_DOT;    // نمط خطي 23.6 و 76.4

input bool             InpShow_38_61    = true;        // إظهار مستويي 38.2 و 61.8
input int               InpWidth_38_61   = 1;           // سماكة خطي 38.2 و 61.8
input ENUM_LINE_STYLE   InpStyle_38_61   = STYLE_DOT;    // نمط خطي 38.2 و 61.8

input bool             InpShow_50       = true;        // إظهار مستوى 50
input int               InpWidth_50      = 1;           // سماكة خط 50
input ENUM_LINE_STYLE   InpStyle_50      = STYLE_DOT;    // نمط خط 50

input bool             InpShow_Ext      = true;        // إظهار الامتدادات (-23.6 و 123.6 وما بعدها)
input int               InpWidth_Ext     = 1;           // سماكة خطوط الامتدادات
input ENUM_LINE_STYLE   InpStyle_Ext     = STYLE_DOT;    // نمط خطوط الامتدادات

// النصوص والتسميات
input bool   InpShowLevelText     = true;    // إظهار أرقام النسب على الخطوط
input bool   InpShowUnitName      = true;    // إظهار اسم الوحدة (مثل D(4)، D(0)...)
input bool   InpShowPipsText      = true;    // إظهار عدد النقاط (0 إلى 100) أسفل الوحدات المكتملة

// التحكم في نص مستويات الفيبو
input int    InpLevelTextFontSize = 10;      // حجم خط نص المستويات
input int    InpLevelTextSide     = 0;       // جهة عرض النص: 0 = يسار الوحدة، 1 = يمين الوحدة
input int    InpLevelTextTimeShiftBars = 0;  // إزاحة النص داخل الوحدة (بعدد الشموع) لتفادي التداخل
input double InpLevelTextOffsetPips = 3.0;   // إزاحة رأسية (بالنقاط) لرفع النص فوق خطه بدل أن يكون عليه

// التحكم اليدوي في العناصر
input bool   InpAllowManualEdit   = false;   // السماح بتحريك/تعديل العناصر يدويًا؟

// رصد نقاط الارتداد (الإشارات NQH / NQF / NQKH)
input bool   InpEnablePivots      = true;    // تفعيل رصد نقاط الارتداد (المفتاح الرئيسي للإشارات)
input bool   InpShowPivotArrows   = true;    // إظهار أسهم الإشارات
input bool   InpShowPivotText     = true;    // إظهار نص الإشارات بجانب الأسهم
input ENUM_SIGNAL_FILTER InpSignalFilter = FILTER_ALL; // نوع الإشارات المطلوب عرضها والتنبيه لها
input int    InpMaxAlerts         = 1;       // أقصى عدد تنبيهات لكل حساب (يفضّل 1: الأحدث فقط)
input bool   InpSoundAlert        = true;    // تشغيل صوت تنبيه للإشارة الأحدث فقط
input bool   InpPushAlert         = false;   // إرسال إشعار Push (اختياري)
input bool   InpPopupAlert        = true;    // إظهار نافذة تنبيه Alert()

// التنبيه فقط عند إغلاق الوحدة
input bool   InpAlertsOnlyOnUnitClose = true; // إن كان مفعّلاً: لا تنبيه إلا عند تحوّل D0 إلى D1

// وحدة D0 (الحالية) تستخدم فيبو الوحدة السابقة
input bool   InpD0UsesPrevFixedFib = true;   // إظهار مستويات فيبو الوحدة السابقة فوق فترة الوحدة الحالية D0
input bool   InpD0FullFuture       = true;   // رسم D0 كامتداد مستقبلي كامل (يمتد حتى نهاية الوحدة نظريًا، بنفس شكل D1)

// إزالة فجوات نهاية الأسبوع بمحاذاة حدود الوحدة مع الشموع الفعلية
input bool   InpSnapToTradingBars = true;    // محاذاة حدود الوحدة مع الشموع الفعلية المتداولة (لإصلاح فجوات نهاية الأسبوع)

// قواعد التسامح
input double InpTolPercent        = 2.0;     // نسبة التسامح المسموحة (% من نطاق الوحدة بالنقاط)

// المسافات البصرية
input double InpArrowGapPips      = 2.0;     // مسافة السهم عن المستوى (بالنقاط)
input double InpTextGapPips       = 0.5;     // مسافة النص عن السهم (بالنقاط)
input double InpTouchEpsPips      = 0.10;    // هامش تسامح اللمس لإشارة NQH (بالنقاط)

// أسلوب تأكيد نقطة الارتداد
input int    InpLookbackBarsV     = 3;       // نموذج V يعتمد على 3 شموع (ثابت)
input double InpMinReboundPips    = 1.0;     // أقل مسافة ارتداد/ابتعاد عن المستوى (بالنقاط)

// الأداء
input int    InpPivotScanMaxBars  = 300;     // فحص آخر X شمعة فقط داخل كل وحدة (وضع أداء خفيف)

//---------------- فلاتر التأكيد: موفينج افريدج (MA) و مؤشر القوة النسبية (RSI) ----------------
// عند التفعيل، لا يظهر سهم البيع/الشراء ولا يُرسل تنبيه إلا إذا وافق الفلتر المفعّل
// اتجاه الإشارة، لكل شمعة على حدة (بدون أي تغيير في قوانين اللمس/التصنيف نفسها).
input bool   InpUseMAFilter   = true;    // تفعيل فلتر الموفينج افريدج كشرط تأكيد للإشارات
input int    InpMAPeriod      = 14;      // فترة الموفينج افريدج (بسيط Simple - سعر الإغلاق Close)
input bool   InpUseRSIFilter  = true;    // تفعيل فلتر RSI كشرط تأكيد للإشارات
input int    InpRSIPeriod     = 14;      // فترة مؤشر RSI (سعر الإغلاق)
input double InpRSIBuyLevel   = 50.0;    // حد RSI لتأكيد الشراء (RSI أعلى منه = زخم صاعد)
input double InpRSISellLevel  = 50.0;    // حد RSI لتأكيد البيع (RSI أسفله = زخم هابط)

//---------------- مسار المتجه Vector Path (بشكل افتراضي: صعود = أصفر، هبوط = أبيض) ----------------
// القاعدة لكل وحدة: تتم مقارنة سعر إغلاق الوحدة بمستوى مرجعي من مستويات الفيبو الخاصة
// بها (23.6% أو 50% أو 76.4%). إن كان الإغلاق أعلى من المستوى المرجعي -> خط صاعد يلتصق
// بمستوى 0.0 لتلك الوحدة. إن كان أقل أو يساويه -> خط هابط يلتصق بمستوى 100.0 لتلك الوحدة.
// تُربط الوحدات المتتالية بخطوط عمودية لتكوين مسار متدرج.
// ✅ يمكن الآن تشغيل ثلاثة مسارات مستقلة في نفس الوقت (عند 23.6% و 50% و 76.4%)، لكل
// منها مفتاح إظهار/إخفاء خاص به، ولكل منها لونا صعود/هبوط خاصان به (مختلفان عن
// الأصفر والأبيض)، بحيث لا تتداخل عناصرها على الشارت أبدًا.
input bool             InpShowVectorPath           = true;        // المفتاح الرئيسي: إظهار/إخفاء كل مسارات المتجه دفعة واحدة
input bool             InpVectorPathIncludeCurrent = true;        // امتداد المسار داخل الوحدة الحالية (قيد التكوّن) باستخدام آخر سعر إغلاق

input bool             InpShowVP_236   = false;       // إظهار مسار المتجه عند مستوى 23.6%
input bool             InpShowVP_500   = true;        // إظهار مسار المتجه عند مستوى 50%
input bool             InpShowVP_618   = false;       // إظهار مسار المتجه عند مستوى 61.8%
input bool             InpShowVP_764   = false;       // إظهار مسار المتجه عند مستوى 76.4%

// ألوان كل مسار على حدة (صعود / هبوط) — مختلفة عن الأصفر والأبيض
input color             InpVPColorUp_236   = clrLime;        // لون الصعود لمسار 23.6%
input color             InpVPColorDown_236 = clrOrange;      // لون الهبوط لمسار 23.6%

input color             InpVPColorUp_500   = clrAqua;        // لون الصعود لمسار 50%
input color             InpVPColorDown_500 = clrFuchsia;     // لون الهبوط لمسار 50%

input color             InpVPColorUp_618   = clrYellowGreen; // لون الصعود لمسار 61.8%
input color             InpVPColorDown_618 = clrDarkOrange;  // لون الهبوط لمسار 61.8%

input color             InpVPColorUp_764   = clrDodgerBlue;  // لون الصعود لمسار 76.4%
input color             InpVPColorDown_764 = clrTomato;      // لون الهبوط لمسار 76.4%

// إعدادات مشتركة للسماكة/النمط ولإظهار الاتجاه (تطبَّق على المسارات الأربعة معًا)
input bool             InpVPShowUp     = true;        // إظهار أجزاء الصعود (لكل المسارات)
input int              InpVPWidthUp    = 2;           // سماكة خط الصعود (لكل المسارات)
input ENUM_LINE_STYLE  InpVPStyleUp    = STYLE_SOLID;  // نمط خط الصعود (لكل المسارات)

input bool             InpVPShowDown   = true;        // إظهار أجزاء الهبوط (لكل المسارات)
input int              InpVPWidthDown  = 2;           // سماكة خط الهبوط (لكل المسارات)
input ENUM_LINE_STYLE  InpVPStyleDown  = STYLE_SOLID;  // نمط خط الهبوط (لكل المسارات)

//---------------- مسار المتجه متعدد الفريمات (Multi-Timeframe Vector Path) ----------------
// طبقة مستقلة تمامًا عن فريم الشارت الحالي: تحسب وترسم مسار المتجه (بنفس منطق
// المؤشر تمامًا: إغلاق الوحدة أعلى من النسبة المرجعية = صاعد يلتصق بـ 0%، وإلا
// = هابط يلتصق بـ 100%) لكل فريم قياسي على حدة (M1 حتى MN1)، بقراءة بيانات ذلك
// الفريم مباشرة عبر iBarShift/iTime/iClose الخاصة به — بصرف النظر عمّا إذا كان
// فريم الشارت الحالي أسرع أو أبطأ من فريم الوحدة. هذا يحل مشكلة عدم ظهور مسار
// 50% على الوحدات الصغيرة (ربع الساعة/7.5 دقيقة) أو على الفريمات البطيئة
// (كالساعة) التي كانت مقيّدة سابقًا بشرط periodSec < X داخل عائلات الوحدات
// الإضافية. كل فريم قياسي يُعامل الآن ككيان مستقل تمامًا، ويُطلق تنبيهًا عند
// افتتاح وحدة جديدة (أي عند إغلاق الوحدة السابقة وتشكّل عقدة جديدة).
input bool             InpEnableMTFVectorPath = true;   // المفتاح الرئيسي: تفعيل مسار المتجه على كل الفريمات معًا
input double           InpMTFVPRatio          = 0.50;   // النسبة المرجعية المستخدمة على كل الفريمات (0.50 = 50%)
input int              InpMTFUnitsToShow      = 30;     // عدد الوحدات التاريخية المرسومة لكل فريم
input color            InpMTFColorUp          = clrAqua;
input color            InpMTFColorDown        = clrFuchsia;
input int              InpMTFVPWidth          = 2;      // سماكة خطوط مسار المتجه متعدد الفريمات
input ENUM_LINE_STYLE  InpMTFVPStyle          = STYLE_SOLID;

input bool   InpMTFShow_M1  = true;   // تفعيل المسار على فريم M1  (وحدة الساعة)
input bool   InpMTFShow_M5  = true;   // تفعيل المسار على فريم M5  (وحدة 3 ساعات)
input bool   InpMTFShow_M15 = true;   // تفعيل المسار على فريم M15 (وحدة 6 ساعات)
input bool   InpMTFShow_M30 = true;   // تفعيل المسار على فريم M30 (وحدة 12 ساعة)
input bool   InpMTFShow_H1  = true;   // تفعيل المسار على فريم H1  (وحدة اليوم)
input bool   InpMTFShow_H4  = true;   // تفعيل المسار على فريم H4  (وحدة الأسبوع)
input bool   InpMTFShow_D1  = true;   // تفعيل المسار على فريم D1  (وحدة الشهر)
input bool   InpMTFShow_W1  = true;   // تفعيل المسار على فريم W1  (وحدة 6 أشهر)
input bool   InpMTFShow_MN1 = true;   // تفعيل المسار على فريم MN1 (وحدة السنة)

input bool   InpMTFAlertOnNewNode = true;   // تنبيه عند افتتاح وحدة جديدة (تشكّل عقدة جديدة) في أي فريم مفعّل
input bool   InpMTFAlertPopup     = true;   // إظهار نافذة Alert() عند التنبيه
input bool   InpMTFAlertSound     = true;   // تشغيل صوت عند التنبيه
input string InpMTFSoundUp        = "aibfx_up.wav";    // ملف الصوت عند فتح عقدة صاعدة
input string InpMTFSoundDown      = "aibfx_down.wav";  // ملف الصوت عند فتح عقدة هابطة

//---------------- التنبيه الرئيسي الموحّد (Master Alert) ----------------
// يجمع كل الطبقات النشطة (MA/RSI/مسار المتجه بفريمك/Overlay/MTF/الوحدات الإضافية)
// في "تصويت" واحد، ويُصدر نتيجة نهائية واضحة: شراء مؤكد / بيع مؤكد / لا يوجد دخول
// مؤكد — بدل خمسة أصوات منفصلة لا تتنسق فيما بينها. يظهر أيضًا على لوحة المعلومات.
input bool   InpMasterAlertEnable      = false;  // تفعيل التنبيه الرئيسي الموحّد
input bool   InpMasterMuteOtherAlerts  = true;   // كتم الأصوات/النوافذ المنفصلة (الإشارات NQ + Overlay + MTF) والاكتفاء بهذا التنبيه فقط
input double InpMasterConfirmThreshold = 0.70;   // نسبة اتفاق الطبقات المفعّلة المطلوبة لاعتبار الإشارة "مؤكدة" (0.70 = 70%)
input bool   InpMasterAlertPopup       = true;   // إظهار نافذة Alert() عند تغيّر التأكيد الموحّد
input bool   InpMasterAlertSound       = true;   // تشغيل صوت عند تغيّر التأكيد الموحّد
input string InpMasterSoundUp          = "aibfx_up.wav";
input string InpMasterSoundDown        = "aibfx_down.wav";

//---------------- عائلة وحدات الدقيقة (تعمل فوق فريم الدقيقة وما دونه — حتى 4 وحدات معًا) ----------------
// المجموعة الأساسية (المطابقة لفريم الشارت الحالي، مثل وحدة الساعة على شارت الدقيقة)
// تُدار عبر الإعدادات العامة في أعلى الملف وتبقى كما هي دائمًا. هذه أربع مجموعات
// إضافيتان مستقلتان تمامًا يمكن تفعيلهما معًا فوق نفس الفريم (فمثلاً على شارت
// الدقيقة: وحدة الساعة + 45 دقيقة + نصف الساعة + ربع الساعة + 7.5 دقيقة معًا). كل مجموعة تتحكم
// بكل عنصر من عناصرها بشكل منفصل (الصندوق، الاسم، النقاط، خطوط الفيبو، مسار
// المتجه)، وتستخدم نفس مستويات ونفس ألوان مسار المتجه المضبوطة أعلاه (23.6% /
// 50% / 61.8% / 76.4%) حتى تكون طريقة "إغلاق الوحدة" موحّدة على كل الأحجام.
// ملاحظة: كل وحدة تحتاج فريم شارت أصغر منها (وإلا فلن تحتوي على أكثر من شمعة واحدة).

input bool   InpShowAuxM45         = false;   // تفعيل وحدة "45 دقيقة" كطبقة إضافية على الشارت الحالي
input int    InpAuxM45UnitsToShow  = 20;      // عدد وحدات 45 دقيقة السابقة المطلوب رسمها
input bool   InpAuxM45ShowBox      = true;    // إظهار حدود صندوق وحدة 45 دقيقة (الخطوط الرأسية)
input bool   InpAuxM45ShowLabel    = true;    // إظهار اسم وحدة 45 دقيقة (مثل m45(1)، m45(0)...)
input bool   InpAuxM45ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة 45 دقيقة مكتملة
input bool   InpAuxM45ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة 45 دقيقة
input bool   InpAuxM45ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة 45 دقيقة
input color  InpAuxM45ColorUp      = clrDeepSkyBlue; // لون مسار 45 دقيقة الصاعد (مستقل عن كل المسارات الأخرى)
input color  InpAuxM45ColorDown    = clrCoral;       // لون مسار 45 دقيقة الهابط
input int    InpAuxM45Width        = 2;              // سماكة مسار 45 دقيقة
input ENUM_LINE_STYLE InpAuxM45Style = STYLE_SOLID;   // نمط مسار 45 دقيقة

input bool   InpShowAuxM30         = false;   // تفعيل وحدة "نصف الساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxM30UnitsToShow  = 20;      // عدد وحدات نصف الساعة السابقة المطلوب رسمها
input bool   InpAuxM30ShowBox      = true;    // إظهار حدود صندوق وحدة نصف الساعة (الخطوط الرأسية)
input bool   InpAuxM30ShowLabel    = true;    // إظهار اسم وحدة نصف الساعة (مثل m30(1)، m30(0)...)
input bool   InpAuxM30ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة نصف ساعة مكتملة
input bool   InpAuxM30ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة نصف الساعة
input bool   InpAuxM30ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة نصف الساعة
input color  InpAuxM30ColorUp      = clrTurquoise;   // لون مسار نصف الساعة الصاعد
input color  InpAuxM30ColorDown    = clrFireBrick;   // لون مسار نصف الساعة الهابط
input int    InpAuxM30Width        = 2;
input ENUM_LINE_STYLE InpAuxM30Style = STYLE_SOLID;

input bool   InpShowAuxM15         = false;   // تفعيل وحدة "ربع الساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxM15UnitsToShow  = 20;      // عدد وحدات ربع الساعة السابقة المطلوب رسمها
input bool   InpAuxM15ShowBox      = true;    // إظهار حدود صندوق وحدة ربع الساعة (الخطوط الرأسية)
input bool   InpAuxM15ShowLabel    = true;    // إظهار اسم وحدة ربع الساعة (مثل m15(1)، m15(0)...)
input bool   InpAuxM15ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة ربع ساعة مكتملة
input bool   InpAuxM15ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة ربع الساعة
input bool   InpAuxM15ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة ربع الساعة
input color  InpAuxM15ColorUp      = clrSpringGreen; // لون مسار ربع الساعة الصاعد
input color  InpAuxM15ColorDown    = clrCrimson;     // لون مسار ربع الساعة الهابط
input int    InpAuxM15Width        = 2;
input ENUM_LINE_STYLE InpAuxM15Style = STYLE_SOLID;

input bool   InpShowAuxM7H         = false;   // تفعيل وحدة "7.5 دقيقة" (نصف الربع ساعة) كطبقة إضافية
input int    InpAuxM7HUnitsToShow  = 20;      // عدد وحدات 7.5 دقيقة السابقة المطلوب رسمها
input bool   InpAuxM7HShowBox      = true;    // إظهار حدود صندوق وحدة 7.5 دقيقة (الخطوط الرأسية)
input bool   InpAuxM7HShowLabel    = true;    // إظهار اسم وحدة 7.5 دقيقة (مثل m7h(1)، m7h(0)...)
input bool   InpAuxM7HShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة 7.5 دقيقة مكتملة
input bool   InpAuxM7HShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة 7.5 دقيقة
input bool   InpAuxM7HShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة 7.5 دقيقة
input color  InpAuxM7HColorUp      = clrCyan;        // لون مسار 7.5 دقيقة الصاعد
input color  InpAuxM7HColorDown    = clrOrangeRed;   // لون مسار 7.5 دقيقة الهابط
input int    InpAuxM7HWidth        = 2;
input ENUM_LINE_STYLE InpAuxM7HStyle = STYLE_SOLID;

//---------------- وحدتان إضافيتان جديدتان (تركيبيتان، من بيانات الشارت الحالي) ----------------
input bool   InpShowAuxH18         = false;   // تفعيل وحدة "18 ساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxH18UnitsToShow  = 20;      // عدد وحدات 18 ساعة السابقة المطلوب رسمها
input bool   InpAuxH18ShowBox      = true;    // إظهار حدود صندوق وحدة 18 ساعة
input bool   InpAuxH18ShowLabel    = true;    // إظهار اسم وحدة 18 ساعة
input bool   InpAuxH18ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة 18 ساعة مكتملة
input bool   InpAuxH18ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة 18 ساعة
input bool   InpAuxH18ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة 18 ساعة
input color  InpAuxH18ColorUp      = clrCadetBlue;   // لون مسار 18 ساعة الصاعد
input color  InpAuxH18ColorDown    = clrMaroon;      // لون مسار 18 ساعة الهابط
input int    InpAuxH18Width        = 2;
input ENUM_LINE_STYLE InpAuxH18Style = STYLE_SOLID;

input bool   InpShowAuxH4_5        = false;   // تفعيل وحدة "4.5 ساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxH4_5UnitsToShow = 20;      // عدد وحدات 4.5 ساعة السابقة المطلوب رسمها
input bool   InpAuxH4_5ShowBox     = true;    // إظهار حدود صندوق وحدة 4.5 ساعة
input bool   InpAuxH4_5ShowLabel   = true;    // إظهار اسم وحدة 4.5 ساعة
input bool   InpAuxH4_5ShowPips    = false;   // إظهار عدد النقاط أسفل كل وحدة 4.5 ساعة مكتملة
input bool   InpAuxH4_5ShowFib     = true;    // إظهار خطوط مستويات الفيبو لوحدة 4.5 ساعة
input bool   InpAuxH4_5ShowVP      = true;    // إظهار مسار المتجه (Vector Path) لوحدة 4.5 ساعة
input color  InpAuxH4_5ColorUp     = clrTeal;        // لون مسار 4.5 ساعة الصاعد
input color  InpAuxH4_5ColorDown   = clrBrown;       // لون مسار 4.5 ساعة الهابط
input int    InpAuxH4_5Width       = 2;
input ENUM_LINE_STYLE InpAuxH4_5Style = STYLE_SOLID;

//---------------- عائلة الوحدات اليومية (تعمل على فريمات M5 / M15 / H1 — حتى 5 وحدات معًا) ----------------
// نفس المنطق تمامًا: تقسيم اليوم إلى 24 ساعة / 12 ساعة / 6 ساعات / 3 ساعات /
// ساعة ونصف، بنفس آلية "إغلاق الوحدة تحت أو فوق 23.6/50/61.8/76.4 يرسم مسارًا
// هابطًا أو صاعدًا" المطبّقة على وحدات الدقيقة أعلاه بالضبط.

input bool   InpShowAuxD24         = false;   // تفعيل وحدة "24 ساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxD24UnitsToShow  = 20;      // عدد وحدات 24 ساعة السابقة المطلوب رسمها
input bool   InpAuxD24ShowBox      = true;    // إظهار حدود صندوق وحدة 24 ساعة (الخطوط الرأسية)
input bool   InpAuxD24ShowLabel    = true;    // إظهار اسم وحدة 24 ساعة (مثل d24(1)، d24(0)...)
input bool   InpAuxD24ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة 24 ساعة مكتملة
input bool   InpAuxD24ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة 24 ساعة
input bool   InpAuxD24ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة 24 ساعة
input color  InpAuxD24ColorUp      = clrSeaGreen;    // لون مسار 24 ساعة الصاعد
input color  InpAuxD24ColorDown    = clrSienna;      // لون مسار 24 ساعة الهابط
input int    InpAuxD24Width        = 2;
input ENUM_LINE_STYLE InpAuxD24Style = STYLE_SOLID;

input bool   InpShowAuxD12         = false;   // تفعيل وحدة "12 ساعة" كطبقة إضافية على الشارت الحالي
input int    InpAuxD12UnitsToShow  = 20;      // عدد وحدات 12 ساعة السابقة المطلوب رسمها
input bool   InpAuxD12ShowBox      = true;    // إظهار حدود صندوق وحدة 12 ساعة (الخطوط الرأسية)
input bool   InpAuxD12ShowLabel    = true;    // إظهار اسم وحدة 12 ساعة (مثل d12(1)، d12(0)...)
input bool   InpAuxD12ShowPips     = false;   // إظهار عدد النقاط أسفل كل وحدة 12 ساعة مكتملة
input bool   InpAuxD12ShowFib      = true;    // إظهار خطوط مستويات الفيبو لوحدة 12 ساعة
input bool   InpAuxD12ShowVP       = true;    // إظهار مسار المتجه (Vector Path) لوحدة 12 ساعة
input color  InpAuxD12ColorUp      = clrRoyalBlue;   // لون مسار 12 ساعة الصاعد
input color  InpAuxD12ColorDown    = clrChocolate;   // لون مسار 12 ساعة الهابط
input int    InpAuxD12Width        = 2;
input ENUM_LINE_STYLE InpAuxD12Style = STYLE_SOLID;

input bool   InpShowAuxD6          = false;   // تفعيل وحدة "6 ساعات" كطبقة إضافية على الشارت الحالي
input int    InpAuxD6UnitsToShow   = 20;      // عدد وحدات 6 ساعات السابقة المطلوب رسمها
input bool   InpAuxD6ShowBox       = true;    // إظهار حدود صندوق وحدة 6 ساعات (الخطوط الرأسية)
input bool   InpAuxD6ShowLabel     = true;    // إظهار اسم وحدة 6 ساعات (مثل d6(1)، d6(0)...)
input bool   InpAuxD6ShowPips      = false;   // إظهار عدد النقاط أسفل كل وحدة 6 ساعات مكتملة
input bool   InpAuxD6ShowFib       = true;    // إظهار خطوط مستويات الفيبو لوحدة 6 ساعات
input bool   InpAuxD6ShowVP        = true;    // إظهار مسار المتجه (Vector Path) لوحدة 6 ساعات
input color  InpAuxD6ColorUp       = clrSteelBlue;   // لون مسار 6 ساعات الصاعد
input color  InpAuxD6ColorDown     = clrDarkSalmon;  // لون مسار 6 ساعات الهابط
input int    InpAuxD6Width         = 2;
input ENUM_LINE_STYLE InpAuxD6Style = STYLE_SOLID;

input bool   InpShowAuxD3          = false;   // تفعيل وحدة "3 ساعات" كطبقة إضافية على الشارت الحالي
input int    InpAuxD3UnitsToShow   = 20;      // عدد وحدات 3 ساعات السابقة المطلوب رسمها
input bool   InpAuxD3ShowBox       = true;    // إظهار حدود صندوق وحدة 3 ساعات (الخطوط الرأسية)
input bool   InpAuxD3ShowLabel     = true;    // إظهار اسم وحدة 3 ساعات (مثل d3(1)، d3(0)...)
input bool   InpAuxD3ShowPips      = false;   // إظهار عدد النقاط أسفل كل وحدة 3 ساعات مكتملة
input bool   InpAuxD3ShowFib       = true;    // إظهار خطوط مستويات الفيبو لوحدة 3 ساعات
input bool   InpAuxD3ShowVP        = true;    // إظهار مسار المتجه (Vector Path) لوحدة 3 ساعات
input color  InpAuxD3ColorUp       = clrLightSeaGreen; // لون مسار 3 ساعات الصاعد
input color  InpAuxD3ColorDown     = clrSalmon;        // لون مسار 3 ساعات الهابط
input int    InpAuxD3Width         = 2;
input ENUM_LINE_STYLE InpAuxD3Style = STYLE_SOLID;

input bool   InpShowAuxD1H30         = false;  // تفعيل وحدة "ساعة ونصف" كطبقة إضافية على الشارت الحالي
input int    InpAuxD1H30UnitsToShow  = 20;     // عدد وحدات الساعة والنصف السابقة المطلوب رسمها
input bool   InpAuxD1H30ShowBox      = true;   // إظهار حدود صندوق وحدة الساعة والنصف (الخطوط الرأسية)
input bool   InpAuxD1H30ShowLabel    = true;   // إظهار اسم وحدة الساعة والنصف (مثل d1h30(1)...)
input bool   InpAuxD1H30ShowPips     = false;  // إظهار عدد النقاط أسفل كل وحدة ساعة ونصف مكتملة
input bool   InpAuxD1H30ShowFib      = true;   // إظهار خطوط مستويات الفيبو لوحدة الساعة والنصف
input bool   InpAuxD1H30ShowVP       = true;   // إظهار مسار المتجه (Vector Path) لوحدة الساعة والنصف
input color  InpAuxD1H30ColorUp      = clrMediumSpringGreen; // لون مسار الساعة والنصف الصاعد
input color  InpAuxD1H30ColorDown    = clrIndianRed;         // لون مسار الساعة والنصف الهابط
input int    InpAuxD1H30Width        = 2;
input ENUM_LINE_STYLE InpAuxD1H30Style = STYLE_SOLID;

//---------------- طبقة خطوط 50% لأطر M5 / M15 / H1 / H4 / D1 (مخصصة للعمل على شارت سريع مثل M1) ----------------
input bool   InpShowLevelsOverlay = true;   // المفتاح الرئيسي لكل طبقة الإظهار (أوقفه لتحسين الأداء)

input bool             InpShow_M5   = true;       // إظهار خط 50% لإطار M5
input color             InpColor_M5  = clrAqua;    // لون خط M5
input int               InpWidth_M5  = 1;          // سماكة خط M5
input ENUM_LINE_STYLE   InpStyle_M5  = STYLE_DASH;  // نمط خط M5

input bool             InpShow_M15  = true;       // إظهار خط 50% لإطار M15
input color             InpColor_M15 = clrOrange;  // لون خط M15
input int               InpWidth_M15 = 1;          // سماكة خط M15
input ENUM_LINE_STYLE   InpStyle_M15 = STYLE_DASH;  // نمط خط M15

input bool             InpShow_H1   = true;       // إظهار خط 50% لإطار H1 (صندوق اليوم)
input color             InpColor_H1  = clrLime;    // لون خط H1
input int               InpWidth_H1  = 1;          // سماكة خط H1
input ENUM_LINE_STYLE   InpStyle_H1  = STYLE_DASH;  // نمط خط H1

input bool             InpShow_H4   = true;       // إظهار خط 50% لإطار H4 (صندوق الأسبوع)
input color             InpColor_H4  = clrMagenta; // لون خط H4
input int               InpWidth_H4  = 1;          // سماكة خط H4
input ENUM_LINE_STYLE   InpStyle_H4  = STYLE_DASH;  // نمط خط H4

input bool             InpShow_D1   = true;       // إظهار خط 50% لإطار D1 (صندوق الشهر)
input color             InpColor_D1  = clrRed;     // لون خط D1
input int               InpWidth_D1  = 2;          // سماكة خط D1
input ENUM_LINE_STYLE   InpStyle_D1  = STYLE_DASH;  // نمط خط D1

input bool   InpAlertOnCross     = true;        // التنبيه عند اختراق السعر لأي مستوى ظاهر (يعمل فقط للخطوط الظاهرة)
input bool   InpAlertPopup       = true;        // إظهار نافذة تنبيه Alert()
input bool   InpAlertSpeech      = true;        // تشغيل ملف صوت منطوق
input string InpSoundFile_Up     = "aibfx_up.wav";    // ملف الصوت عند الاختراق للأعلى (ضعه داخل MQL5/Sounds)
input string InpSoundFile_Down   = "aibfx_down.wav";  // ملف الصوت عند الاختراق للأسفل (ضعه داخل MQL5/Sounds)

//---------------- سلّم التوافق متعدد الفريمات (Confluence Ladder) ----------------
// هذا هو المحرك الفعلي لقرار "شراء مؤكد جدًا جدًا / بيع مؤكد جدًا جدًا" في اللوحة:
// يبدأ من أصغر وحدة (7.5 دقيقة) كإشارة مبكرة، ثم يتطلب توافق الفلاتر الأساسية
// (MA/RSI)، ثم توافق "الحد الأدنى" من الوحدات (7.5د، ربع ساعة، نصف ساعة، 45
// دقيقة، ساعة، 4 ساعات) — فإن تحقق كل ذلك يكون القرار "مؤكد". فإن توافق أيضًا
// "الحد الأقصى" (اليومي والأسبوعي) معهم، يُرقّى القرار إلى "مؤكد جدًا جدًا".
// كل وحدة من M15/M30/H1/H4/D1/W1 تُقرأ من بياناتها الحقيقية (فريمها الفعلي في
// المنصة)، بينما 7.5 دقيقة و45 دقيقة (لا يوجد لهما فريم قياسي في MT5) تُحسبان من
// بيانات الشارت الحالي مباشرة — لذا يُفضَّل تفعيلهما فقط على شارت M1 أو M5.
input bool   InpEnableConfluenceLadder = true;    // تفعيل سلّم التوافق (المحرك الرئيسي لقرار اللوحة)
input double InpConfluenceRatio        = 0.50;    // النسبة المرجعية المستخدمة في كل طبقات السلّم (0.50 = 50%)
input bool   InpConfRequireMAandRSI    = false;   // النمط الصارم (اختياري): اشتراط توافق MA وRSI كبوابة إلزامية قبل قبول أي اتجاه. معطّل افتراضيًا — MA/RSI الآن صوتان في التصويت لا بوابة، لتفادي تأخير الدخول المبكر بانتظار أبطأ مؤشرين في النظام

input bool   InpConfShow_m7h = true;   // الطبقة 1  (حد أدنى): 7.5 دقيقة — من بيانات الشارت الحالي
input bool   InpConfShow_m15 = true;   // الطبقة 2  (حد أدنى): ربع ساعة — بيانات M15 الحقيقية
input bool   InpConfShow_m30 = true;   // الطبقة 3  (حد أدنى): نصف ساعة — بيانات M30 الحقيقية
input bool   InpConfShow_m45 = true;   // الطبقة 4  (حد أدنى): 45 دقيقة — من بيانات الشارت الحالي
input bool   InpConfShow_H1  = true;   // الطبقة 5  (حد أدنى): ساعة واحدة — بيانات H1 الحقيقية
input bool   InpConfShow_H4  = true;   // الطبقة 6  (حد أدنى): 4 ساعات — بيانات H4 الحقيقية
input bool   InpConfShow_H4_5= false;  // الطبقة 7  (حد أدنى): 4.5 ساعة — من بيانات الشارت الحالي (تركيبية، معطّلة افتراضيًا)
input bool   InpConfShow_H18 = false;  // الطبقة 8  (حد أدنى): 18 ساعة — من بيانات الشارت الحالي (تركيبية، معطّلة افتراضيًا)
input bool   InpConfShow_D1  = true;   // الطبقة 9  (حد أقصى): يومي — بيانات D1 الحقيقية
input bool   InpConfShow_W1  = true;   // الطبقة 10 (حد أقصى): أسبوعي — بيانات W1 الحقيقية

input double InpConfMinTierThreshold = 0.75;   // نسبة الاتفاق المطلوبة داخل الحد الأدنى لاعتباره "مؤكد" (0.75 = 75% — مخفّضة من 100% الافتراضية القديمة لتفادي تأخر الإشارة حتى الاتفاق التام؛ ارفعها يدويًا إن أردت الصرامة الكاملة كالسابق)
input int    InpConfMinTierMinCount  = 4;      // أقل عدد طبقات متاحة في الحد الأدنى ليُعتمد التوافق (من أصل حتى 12 طبقة الآن)
input double InpConfMaxTierThreshold = 1.00;   // نسبة الاتفاق المطلوبة داخل الحد الأقصى لترقية القرار إلى "مؤكد جدًا جدًا" (تبقى 100% عمدًا — أقوى درجة يجب أن تظل صارمة)
input int    InpConfMaxTierMinCount  = 2;      // أقل عدد طبقات متاحة في الحد الأقصى (كلا D1 وW1 افتراضيًا) لازمة للترقية

input bool   InpConfShowPerLayerLines = true;  // إظهار سطر ملوّن مستقل لكل طبقة (أوقفه لعرض مختصر: النسب فقط)

//---------------- المسار الكسري داخل الوحدة (Intra-Unit Fractional Path) ----------------
// يقسّم الوحدة الأساسية الحالية (أيًا كان فريم الشارت المفتوح: M5 حتى MN1) إلى
// أربع نقاط تفتيش عند 1/8 و1/4 و1/2 و3/4 من مدتها الحقيقية الفعلية (محسوبة من
// حدود الوحدة الحقيقية وليس بتقريب ثوانٍ ثابت — فتعمل بدقة تامة حتى على الشهر
// والأسبوع المتغيّري الطول). يُعطي هذا إشارة اتجاه مبكرة قبل إغلاق الوحدة كاملة
// — تعميم لنفس فكرة تقسيم الساعة على شارت الدقيقة، لكن تلقائيًا على أي فريم.
// كل مفتاح هنا يتحكم بالرسم على الشارت وبدخول هذه الطبقة في تصويت سلّم التوافق معًا.
input bool             InpShowFracPath   = true;    // المفتاح الرئيسي لكل المسار الكسري
input double           InpFracPathRatio  = 0.50;    // النسبة المرجعية (من مدى الوحدة السابقة) التي تُقارَن بها كل نقطة تفتيش
input bool             InpShowFrac18     = true;    // نقطة التفتيش عند 1/8 من الوحدة (أبكر إشارة)
input bool             InpShowFrac14     = true;    // نقطة التفتيش عند 1/4 من الوحدة
input bool             InpShowFrac12     = true;    // نقطة التفتيش عند 1/2 من الوحدة
input bool             InpShowFrac34     = true;    // نقطة التفتيش عند 3/4 من الوحدة (أقرب لإغلاق الوحدة)
input color            InpFracColorUp    = clrLime;    // لون نقاط/خطوط الاتجاه الصاعد
input color            InpFracColorDown  = clrRed;     // لون نقاط/خطوط الاتجاه الهابط
input int              InpFracWidth      = 2;          // سماكة خطوط المسار الكسري
input ENUM_LINE_STYLE  InpFracStyle      = STYLE_SOLID; // نمط خطوط المسار الكسري

//---------------- لوحة المعلومات (الداشبورد) ----------------
// لوحة حيّة تُحدَّث كل حركة سعر: تعرض السعر الحالي، موقعه بين مستويات الفيبو
// الحالية، قيمة الموفينج افريدج واتجاهها، قيمة RSI وحالتها، والتأكيد الإجمالي
// (شراء / بيع / غير مؤكد) بناءً على الفلترين المفعّلين أعلاه.
input bool             InpShowDashboard    = true;           // إظهار لوحة المعلومات (الداشبورد) على الشارت
input ENUM_DASH_CORNER InpDashCorner       = DASH_TOP_RIGHT;  // زاوية عرض اللوحة على الشارت
input int              InpDashX            = 300;              // إزاحة أفقية للوحة من الزاوية (بكسل)
input int              InpDashY            = 25;              // إزاحة رأسية للوحة من الزاوية (بكسل)
input int              InpDashFontSize     = 9;               // حجم خط اللوحة
input string           InpDashFontName     = "Consolas";      // نوع خط اللوحة
input color            InpDashColorNeutral = clrSilver;       // لون النص للبيانات المحايدة
input color            InpDashColorUp      = clrLime;         // لون نص حالة الصعود / تأكيد الشراء
input color            InpDashColorDown    = clrTomato;       // لون نص حالة الهبوط / تأكيد البيع
input color            InpDashBgColor      = clrBlack;        // لون خلفية اللوحة (يُستخدم فقط عند تفعيل InpDashShowBackground)
input int              InpDashBgOpacity    = 30;               // شفافية خلفية اللوحة (0=صلب، 100=شفاف جدًا)
input bool             InpDashShowBackground = false;          // إظهار خلفية خلف نصوص اللوحة (افتراضيًا بلا خلفية لمظهر أنظف)
input bool             InpDashShowDetails    = true;           // إظهار سطر تفصيلي لكل طبقة مفعّلة (MA/RSI/مسار المتجه/Overlay/MTF/الوحدات الإضافية/آخر إشارة)
input int              InpDashVerdictFontSize = 18;            // حجم خط سطر القرار النهائي (شراء مؤكد / بيع مؤكد / لا يوجد دخول مؤكد) — كبير وواضح
input int              InpDashLineSpacing     = 0;             // مسافة إضافية بالبكسل بين أسطر اللوحة (للتحكم الكامل بالتنسيق)
input bool             InpShowVPSignalBlock   = true;           // إظهار كتلة إشارات مسار الوحدة (أساسية/اختراق/ارتداد قوي/توافق 7.5) على اللوحة

//---------------- الدخول المبكر (Early Entry) — مخرج مستقل تمامًا عن قوة التأكيد ----------------
// بناءً على المراجعة: يجب ألا ينتظر الدخول المبكر أبطأ عنصرين في المؤشر (MA/RSI)
// ولا سلّم التوافق الكامل (الذي يخلط سرعات مختلفة تمامًا في تصويت واحد). هذا
// المخرج يعتمد فقط على أسرع المكوّنات: طبقتي 7.5 دقيقة و15 دقيقة من سلّم
// التوافق، نقطتي 1/8 و1/4 من المسار الكسري، والإشارة المبكرة لمسار الوحدة
// (أقرب مستوى مفعّل فقط، وليس شرط الإغلاق فوق كل المستويات). تصويت أغلبية بسيط
// بلا أي بوابة أو شرط مسبق — الهدف هو عدم تفويت الفرصة المبكرة إطلاقًا.
input bool   InpShowEarlyEntry        = true;   // إظهار مخرج الدخول المبكر على اللوحة (مستقل عن القرار الموحّد وسلّم التوافق)
input int    InpEarlyEntryMinCount    = 2;      // أقل عدد من المكوّنات الخمسة السريعة يجب أن يكون متاحًا لاعتماد الدخول المبكر
input bool   InpEarlyEntryAlertEnable = true;   // تفعيل تنبيه مستقل خاص بالدخول المبكر (منفصل عن التنبيه الرئيسي الموحّد)
input bool   InpEarlyEntryAlertPopup  = true;
input bool   InpEarlyEntryAlertSound  = true;
input string InpEarlyEntrySoundUp     = "aibfx_up.wav";
input string InpEarlyEntrySoundDown   = "aibfx_down.wav";

//---------------- دليل الألوان (لوحة مقابلة تشرح معنى كل لون على الشارت) ----------------
input bool             InpShowColorLegend      = false;            // إظهار لوحة دليل الألوان
input ENUM_DASH_CORNER InpLegendCorner         = DASH_TOP_LEFT;   // زاوية عرض دليل الألوان (افتراضيًا الجهة المقابلة لللوحة الرئيسية)
input int              InpLegendX              = 15;              // إزاحة أفقية لدليل الألوان من الزاوية
input int              InpLegendY              = 25;              // إزاحة رأسية لدليل الألوان من الزاوية
input int              InpLegendFontSize       = 9;               // حجم خط دليل الألوان
input bool             InpLegendShowBackground = false;           // إظهار خلفية خلف دليل الألوان

//---------------- ثوابت ----------------
string  PFX = "AIBV2F_";
double  FibRatios[9] = {-0.236, 0.0, 0.236, 0.382, 0.50, 0.618, 0.764, 1.0, 1.236};
double  FibPerc [9]  = {-23.6,   0.0, 23.6,  38.2,  50.0, 61.8,  76.4, 100.0, 123.6};

//---------------- الحالة الداخلية ----------------
datetime g_lastAlertTime = 0;
string   g_lastAlertKey  = "";
datetime g_lastUnitStart = 0;
bool     g_allowAlertsNow = false;

datetime g_lastRedrawBarTime = 0;
bool     g_forceRedraw = true;

enum DirState { DIR_UNKNOWN=0, DIR_UP=1, DIR_DOWN=2 };
#define LVL_COUNT 5
DirState g_prevState[LVL_COUNT]; // M5, M15, H1, H4, D1 — بهذا الترتيب
bool     g_levels_initialized = false; // لا تنبيه عند أول فحص بعد التحميل

// حالة مسار المتجه متعدد الفريمات (9 فريمات: M1,M5,M15,M30,H1,H4,D1,W1,MN1)
#define MTF_COUNT 9
DirState g_mtfPrevDir[MTF_COUNT];
bool     g_mtfInitialized[MTF_COUNT];
DirState g_mtfCurrentDir[MTF_COUNT];  // القراءة الحيّة الحالية لكل فريم — تُحدَّث دائمًا بصرف النظر عن إعدادات التنبيه، وتُستخدم في لوحة المعلومات

// اتجاه كل نقطة تفتيش في المسار الكسري داخل الوحدة الحالية (1/8، 1/4، 1/2، 3/4)
DirState g_fracDir[4];

// آخر إشارة ارتداد (NQH/NQF/NQKH) — معلوماتية فقط، لا تدخل في تصويت التأكيد الموحّد
string   g_lastSignalText  = "";
datetime g_lastSignalTime  = 0;
color    g_lastSignalColor = clrSilver;
DirState g_lastSignalDir   = DIR_UNKNOWN;

// حالة التنبيه الرئيسي الموحّد (Master Alert)
DirState g_masterPrevDir      = DIR_UNKNOWN;
int      g_masterPrevTier     = 0;    // 0=لا تأكيد، 1=مؤكد، 2=مؤكد جدًا جدًا
bool     g_masterInitialized  = false;

// حالة تنبيه الدخول المبكر (مستقلة تمامًا عن التنبيه الرئيسي الموحّد)
DirState g_earlyPrevDir     = DIR_UNKNOWN;
bool     g_earlyInitialized = false;

#define DASH_MAX_LINES 40

int g_rates_total = 0;

// مقابض مؤشري الموفينج افريدج و RSI (لفلاتر التأكيد وللوحة المعلومات)
int g_maHandle  = INVALID_HANDLE;
int g_rsiHandle = INVALID_HANDLE;

// مخازن مؤقتة لقيم MA/RSI أثناء فحص نقاط الارتداد داخل وحدة واحدة
// (تُملأ مرة واحدة لكل وحدة عبر PrepareFilterBuffers بدل إعادة القراءة لكل مستوى فيبو)
double g_pivotMaBuf[];
double g_pivotRsiBuf[];
int    g_pivotBufShift0 = 0;
bool   g_pivotMaReady  = false;
bool   g_pivotRsiReady = false;

// مخزن الوحدات — حتى 10 مجموعات مستقلة تمامًا يمكن رسمها معًا على نفس الشارت:
// 0 = المجموعة الأساسية (مطابقة لفريم الشارت الحالي، بها الأسهم والتنبيهات)
// عائلة الدقيقة (تعمل على فريم الدقيقة وما دونها من فريمات سريعة):
//   1 = 45 دقيقة   2 = نصف ساعة   3 = ربع ساعة   4 = 7.5 دقيقة (نصف الربع ساعة)
// عائلة اليوم (تعمل على فريمات M5 / M15 / H1):
//   5 = 24 ساعة   6 = 12 ساعة   7 = 6 ساعات   8 = 3 ساعات   9 = ساعة ونصف
// مجموعات إضافية مخصصة لمسار المتجه متعدد الفريمات (Multi-Timeframe Vector Path):
// كل فريم قياسي (M1..MN1) له مجموعة تخزين مستقلة خاصة به (لا علاقة لها بفريم
// الشارت الحالي)، بحيث تعمل جميعها معًا في نفس اللحظة دون أي تصادم في التخزين.
#define GRP_MTF_M1    10
#define GRP_MTF_M5    11
#define GRP_MTF_M15   12
#define GRP_MTF_M30   13
#define GRP_MTF_H1    14
#define GRP_MTF_H4    15
#define GRP_MTF_D1    16
#define GRP_MTF_W1    17
#define GRP_MTF_MN1   18

#define GRP_COUNT     21
#define GRP_PRIMARY   0
#define GRP_AUX_M45   1
#define GRP_AUX_M30   2
#define GRP_AUX_M15   3
#define GRP_AUX_M7H   4
#define GRP_AUX_D24   5
#define GRP_AUX_D12   6
#define GRP_AUX_D6    7
#define GRP_AUX_D3    8
#define GRP_AUX_D1H30 9
#define GRP_AUX_H18   19   // وحدة 18 ساعة الإضافية (تركيبية)
#define GRP_AUX_H4_5  20   // وحدة 4.5 ساعة الإضافية (تركيبية)

datetime g_unitStart[GRP_COUNT][256];
datetime g_unitEnd  [GRP_COUNT][256];
double   g_unitLo   [GRP_COUNT][256];
double   g_unitHi   [GRP_COUNT][256];
bool     g_unitValid[GRP_COUNT][256];
int      g_unitsDrawnFixed[GRP_COUNT];

//====================================================
// تعريف الوحدة
//====================================================
enum UnitType
{
   UNIT_NONE = 0,
   UNIT_YEAR,
   UNIT_6MONTH,
   UNIT_MONTH,
   UNIT_WEEK,
   UNIT_DAY,
   UNIT_12H,
   UNIT_6H,
   UNIT_3H,
   UNIT_4H,    // وحدة جديدة: 4 ساعات فعلية (محاذاة تقويمية 0/4/8/12/16/20) — لسلّم التوافق
   UNIT_1H,
   UNIT_30M,   // وحدة إضافية: نصف ساعة (تُستخدم فقط في طبقات الوحدات الإضافية)
   UNIT_15M,   // وحدة إضافية: ربع ساعة (تُستخدم فقط في طبقات الوحدات الإضافية)
   UNIT_45M,   // وحدة إضافية: 45 دقيقة
   UNIT_HALFQ, // وحدة إضافية: 7.5 دقيقة (نصف الربع ساعة)
   UNIT_1H30,  // وحدة إضافية: ساعة ونصف (90 دقيقة) — لعائلة الوحدات اليومية
   UNIT_18H,   // وحدة إضافية: 18 ساعة (تركيبية، من بيانات الشارت الحالي)
   UNIT_4_5H   // وحدة إضافية: 4.5 ساعة (تركيبية، من بيانات الشارت الحالي)
};

//====================================================
// دوال مساعدة للألوان
//====================================================
int GetR(color c){ return (int)(c & 0xFF); }
int GetG(color c){ return (int)((c >> 8) & 0xFF); }
int GetB(color c){ return (int)((c >> 16) & 0xFF); }

color RGBc(int r,int g,int b)
{
   r = (int)MathMax(0, MathMin(255,r));
   g = (int)MathMax(0, MathMin(255,g));
   b = (int)MathMax(0, MathMin(255,b));
   return (color)(r | (g<<8) | (b<<16));
}

color ApplyOpacity(color c, int opacityPercent, color bg)
{
   double a = (double)opacityPercent / 100.0;
   int r = (int)MathRound(GetR(c)*(1.0-a) + GetR(bg)*a);
   int g = (int)MathRound(GetG(c)*(1.0-a) + GetG(bg)*a);
   int b = (int)MathRound(GetB(c)*(1.0-a) + GetB(bg)*a);
   return RGBc(r,g,b);
}

double PipSize()
{
   if(_Digits==5 || _Digits==3) return (_Point*10.0);
   return _Point;
}
double ToPips(double priceDiff){ return MathAbs(priceDiff) / PipSize(); }

//====================================================
// دوال العناصر الرسومية
//====================================================
bool ObjExists(string name){ return (ObjectFind(0,name) >= 0); }

void ObjDelByPrefix(string pfx, string excludeContains="")
{
   int total = ObjectsTotal(0,0,-1);
   for(int i=total-1; i>=0; i--)
   {
      string nm = ObjectName(0,i,0,-1);
      if(StringFind(nm, pfx, 0) == 0)
      {
         if(excludeContains!="" && StringFind(nm, excludeContains, 0) >= 0) continue;
         ObjectDelete(0,nm);
      }
   }
}

void ApplyEditProps(string name)
{
   ObjectSetInteger(0,name,OBJPROP_SELECTABLE, InpAllowManualEdit ? 1 : 0);
   ObjectSetInteger(0,name,OBJPROP_HIDDEN,     InpAllowManualEdit ? 0 : 1);
}

void SetLineStyle(string name, color col, int width, int style)
{
   ObjectSetInteger(0,name,OBJPROP_COLOR, col);
   ObjectSetInteger(0,name,OBJPROP_WIDTH, width);
   ObjectSetInteger(0,name,OBJPROP_STYLE, style);
   ObjectSetInteger(0,name,OBJPROP_BACK,  0);
   ApplyEditProps(name);
}

void CreateVLine(string name, datetime t, color col, int width, int style)
{
   if(!ObjExists(name))
      ObjectCreate(0,name, OBJ_VLINE, 0, t, 0.0);

   ObjectMove(0,name, 0, t, 0.0);
   SetLineStyle(name, col, width, style);
}

void CreateHSeg(string name, datetime t1, double price, datetime t2, color col, int width, int style)
{
   if(!ObjExists(name))
      ObjectCreate(0,name, OBJ_TREND, 0, t1, price, t2, price);
   else
   {
      ObjectMove(0,name, 0, t1, price);
      ObjectMove(0,name, 1, t2, price);
   }
   ObjectSetInteger(0,name,OBJPROP_RAY, 0);
   SetLineStyle(name, col, width, style);
}

void CreateVSeg(string name, datetime t, double price1, double price2, color col, int width, int style)
{
   if(!ObjExists(name))
      ObjectCreate(0,name, OBJ_TREND, 0, t, price1, t, price2);
   else
   {
      ObjectMove(0,name, 0, t, price1);
      ObjectMove(0,name, 1, t, price2);
   }
   ObjectSetInteger(0,name,OBJPROP_RAY, 0);
   SetLineStyle(name, col, width, style);
}

void CreateText(string name, datetime t, double price, string txt, color col, int fontSize, int anchor)
{
   if(!ObjExists(name))
      ObjectCreate(0,name, OBJ_TEXT, 0, t, price);
   else
      ObjectMove(0,name, 0, t, price);

   ObjectSetString(0,name,OBJPROP_TEXT, txt);
   ObjectSetString(0,name,OBJPROP_FONT, "Arial");
   ObjectSetInteger(0,name,OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0,name,OBJPROP_COLOR, col);
   ObjectSetInteger(0,name,OBJPROP_ANCHOR, anchor);
   ObjectSetInteger(0,name,OBJPROP_BACK, 0);

   ApplyEditProps(name);
}

void CreateArrow(string name, datetime t, double price, int arrowCode, color col)
{
   if(!ObjExists(name))
      ObjectCreate(0,name, OBJ_ARROW, 0, t, price);
   else
      ObjectMove(0,name, 0, t, price);

   ObjectSetInteger(0,name,OBJPROP_ARROWCODE, arrowCode);
   ObjectSetInteger(0,name,OBJPROP_COLOR, col);
   ObjectSetInteger(0,name,OBJPROP_WIDTH, 1);
   ObjectSetInteger(0,name,OBJPROP_BACK, 0);
   ApplyEditProps(name);
}

//====================================================
// ربط الوحدة بالإطار الزمني
//====================================================
UnitType GetUnitType()
{
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)_Period;

   if(tf == PERIOD_MN1) return UNIT_YEAR;
   if(tf == PERIOD_W1)  return UNIT_6MONTH;
   if(tf == PERIOD_D1)  return UNIT_MONTH;
   if(tf == PERIOD_H4)  return UNIT_WEEK;
   if(tf == PERIOD_H1)  return UNIT_DAY;
   if(tf == PERIOD_M30) return UNIT_12H;
   if(tf == PERIOD_M15) return UNIT_6H;
   if(tf == PERIOD_M5)  return UNIT_3H;
   if(tf == PERIOD_M1)  return UNIT_1H;
   return UNIT_NONE;
}

string UnitTag(UnitType ut)
{
   if(ut==UNIT_YEAR)   return "Y";
   if(ut==UNIT_6MONTH) return "S6";
   if(ut==UNIT_MONTH)  return "M";
   if(ut==UNIT_WEEK)   return "W";
   if(ut==UNIT_DAY)    return "D";
   if(ut==UNIT_12H)    return "H12";
   if(ut==UNIT_6H)     return "H6";
   if(ut==UNIT_3H)     return "H3";
   if(ut==UNIT_4H)     return "H4";
   if(ut==UNIT_1H)     return "H";
   if(ut==UNIT_30M)    return "m30";
   if(ut==UNIT_15M)    return "m15";
   if(ut==UNIT_45M)    return "m45";
   if(ut==UNIT_HALFQ)  return "m7h";
   if(ut==UNIT_1H30)   return "d1h30";
   if(ut==UNIT_18H)    return "H18";
   if(ut==UNIT_4_5H)   return "H4.5";
   return "?";
}

//====================================================
// محاذاة عامة لأي وحدة ذات طول ثابت بالثواني (لا تتوافق بالضرورة مع حقول
// الدقائق/الساعات الصحيحة، مثل 7.5 دقيقة أو 45 دقيقة أو 90 دقيقة). تُحسب
// من ثواني اليوم منذ منتصف الليل، بحيث تكون المحاذاة ثابتة كل يوم بنفس التوقيت.
//====================================================
datetime FloorToFixedSeconds(datetime t, int unitSeconds)
{
   // شبكة مطلقة من نقطة الصفر الزمنية (Unix epoch)، وليست إعادة ضبط عند كل منتصف
   // ليل. هذا يعمل بشكل صحيح لأي مدة — حتى تلك التي لا تقسّم اليوم (86400 ثانية)
   // بالتساوي (مثل 18 ساعة أو 4.5 ساعة)، أو حتى المدد الأطول من يوم كامل. بالنسبة
   // لكل المدد الحالية التي تقسّم 86400 بالتساوي (45د/30د/15د/7.5د/1.5س/3س/6س/12س)،
   // تُعطي هذه الطريقة نفس النتيجة تمامًا التي كانت تُعطيها الطريقة القديمة —
   // فلا يوجد أي تغيير سلوكي لأي وحدة تعمل بشكل صحيح حاليًا.
   if(unitSeconds <= 0) return t;
   long ts = (long)t;
   long blockStart = ts - (ts % (long)unitSeconds);
   return (datetime)blockStart;
}

datetime FloorToUnitStart(datetime t, UnitType ut)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);

   if(ut==UNIT_YEAR)
   {
      dt.mon=1; dt.day=1; dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_6MONTH)
   {
      int half = (dt.mon<=6)?1:2;
      dt.mon = (half==1)?1:7;
      dt.day=1; dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_MONTH)
   {
      dt.day=1; dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_WEEK)
   {
      // بداية الأسبوع = أول دقيقة من يوم الاثنين (00:00) بالتقويم — لكل الرموز.
      dt.hour=0; dt.min=0; dt.sec=0;
      datetime dayStart = StructToTime(dt);
      int dow = dt.day_of_week; // 0=أحد،1=اثنين...
      int shift = (dow==0)?6:(dow-1); // البداية من الاثنين
      return (dayStart - (datetime)(shift*86400));
   }

   if(ut==UNIT_DAY)
   {
      dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_12H)
   {
      dt.min=0; dt.sec=0;
      dt.hour = (dt.hour<12)?0:12;
      return StructToTime(dt);
   }

   if(ut==UNIT_6H)
   {
      dt.min=0; dt.sec=0;
      dt.hour = (dt.hour/6)*6;
      return StructToTime(dt);
   }

   if(ut==UNIT_3H)
   {
      dt.min=0; dt.sec=0;
      dt.hour = (dt.hour/3)*3;
      return StructToTime(dt);
   }

   if(ut==UNIT_4H)
   {
      dt.min=0; dt.sec=0;
      dt.hour = (dt.hour/4)*4;
      return StructToTime(dt);
   }

   if(ut==UNIT_1H)
   {
      dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_30M)
   {
      dt.sec=0;
      dt.min = (dt.min/30)*30;
      return StructToTime(dt);
   }

   if(ut==UNIT_15M)
   {
      dt.sec=0;
      dt.min = (dt.min/15)*15;
      return StructToTime(dt);
   }

   if(ut==UNIT_45M)   return FloorToFixedSeconds(t, 2700); // 45 دقيقة
   if(ut==UNIT_HALFQ) return FloorToFixedSeconds(t, 450);  // 7.5 دقيقة
   if(ut==UNIT_1H30)  return FloorToFixedSeconds(t, 5400); // 90 دقيقة
   if(ut==UNIT_18H)   return FloorToFixedSeconds(t, 64800); // 18 ساعة
   if(ut==UNIT_4_5H)  return FloorToFixedSeconds(t, 16200); // 4.5 ساعة

   return t;
}

datetime AddUnit(datetime start, UnitType ut, int step)
{
   MqlDateTime dt; TimeToStruct(start, dt);

   if(ut==UNIT_YEAR){ dt.year += step; return StructToTime(dt); }

   if(ut==UNIT_6MONTH)
   {
      int m = dt.mon + 6*step;
      while(m>12){ m-=12; dt.year++; }
      while(m<1){  m+=12; dt.year--; }
      dt.mon=m; dt.day=1; dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   if(ut==UNIT_MONTH)
   {
      int m = dt.mon + step;
      while(m>12){ m-=12; dt.year++; }
      while(m<1){  m+=12; dt.year--; }
      dt.mon=m; dt.day=1; dt.hour=0; dt.min=0; dt.sec=0;
      return StructToTime(dt);
   }

   // ملاحظة: خطوة UNIT_WEEK يتم التعامل معها بشكل خاص عبر AddUnitEx
   // (5 أيام = 7200 دقيقة لأدوات السوق العادية، 7 أيام كاملة للعملات الرقمية)
   if(ut==UNIT_WEEK)  return (start + (datetime)step*7*86400);
   if(ut==UNIT_DAY)   return (start + (datetime)step*86400);
   if(ut==UNIT_12H)   return (start + (datetime)step*12*3600);
   if(ut==UNIT_6H)    return (start + (datetime)step*6*3600);
   if(ut==UNIT_3H)    return (start + (datetime)step*3*3600);
   if(ut==UNIT_4H)    return (start + (datetime)step*4*3600);
   if(ut==UNIT_1H)    return (start + (datetime)step*3600);
   if(ut==UNIT_30M)   return (start + (datetime)step*1800);
   if(ut==UNIT_15M)   return (start + (datetime)step*900);
   if(ut==UNIT_45M)   return (start + (datetime)step*2700);
   if(ut==UNIT_HALFQ) return (start + (datetime)step*450);
   if(ut==UNIT_1H30)  return (start + (datetime)step*5400);
   if(ut==UNIT_18H)   return (start + (datetime)step*64800);
   if(ut==UNIT_4_5H)  return (start + (datetime)step*16200);

   return start;
}

//====================================================
// تصنيف نوع الرمز
//====================================================
bool IsCryptoSymbol(string sym)
{
   string tags[] = {"BTC","ETH","LTC","XRP","BCH","ADA","DOT","SOL","DOGE","BNB",
                     "USDT","USDC","MATIC","AVAX","LINK","TRX","ATOM","XLM","SHIB","UNI","ETC"};
   for(int i=0; i<ArraySize(tags); i++)
      if(StringFind(sym, tags[i]) >= 0) return true;
   return false;
}

//====================================================
// حساب حدود وحدة الأسبوع بالدقيقة (مبسّط وحتمي، بدون اكتشاف فجوات):
//
//   - أدوات السوق العادية (فوركس/أسهم/معادن...): الأسبوع = 5 أيام كاملة بالدقيقة،
//     من أول دقيقة يوم الاثنين (00:00) إلى آخر دقيقة يوم الجمعة (23:59)، أي طول
//     الوحدة = 7200 دقيقة بالضبط. بما أنه لا توجد بيانات تداول في السبت والأحد،
//     فإن الأسبوع التالي يبدأ مباشرة من صباح الاثنين — أي الجمعة هي اليوم السابق
//     مباشرة للاثنين ضمن حسابات الوحدة.
//   - العملات الرقمية: الأسبوع = 7 أيام كاملة (10080 دقيقة)، من اثنين إلى اثنين،
//     لأن التداول عليها مستمر 24/7 بلا توقف.
//
// بداية الوحدة (الاثنين 00:00) واحدة لكل الرموز وتُحسب دائمًا بالتقويم مباشرة.
//====================================================
datetime FloorToUnitStartEx(string sym, ENUM_TIMEFRAMES tf, datetime t, UnitType ut)
{
   return FloorToUnitStart(t, ut); // نفس القاعدة التقويمية لكل الرموز، بما فيها الأسبوع
}

datetime AddUnitEx(string sym, ENUM_TIMEFRAMES tf, datetime start, UnitType ut, int step)
{
   if(ut==UNIT_WEEK)
   {
      int days = IsCryptoSymbol(sym) ? 7 : 5;   // 7 أيام كاملة للعملات الرقمية، 5 أيام (اثنين-جمعة) لغيرها
      return start + (datetime)((long)days*86400*step);
   }
   return AddUnit(start, ut, step);
}

//====================================================
// محاذاة حدود الوحدة مع الشموع الفعلية
//====================================================
bool SnapUnitToBars(datetime t1, datetime t2, datetime &out1, datetime &out2)
{
   out1 = t1; out2 = t2;
   if(!InpSnapToTradingBars) return true;
   if(g_rates_total < 10) return false;

   int idxS = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t1, false);
   if(idxS < 0) return false;

   datetime ts = iTime(_Symbol, (ENUM_TIMEFRAMES)_Period, idxS);
   if(ts < t1 && idxS > 0) idxS--;

   out1 = iTime(_Symbol, (ENUM_TIMEFRAMES)_Period, idxS);

   int idxE = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   if(idxE < 0) return false;

   out2 = iTime(_Symbol, (ENUM_TIMEFRAMES)_Period, idxE) + (datetime)PeriodSeconds(_Period);
   if(out2 <= out1) return false;
   return true;
}

//====================================================
// الحصول على القمة/القاع داخل [t1, t2)
//====================================================
bool GetRange(datetime t1, datetime t2, double &lo, double &hi)
{
   lo = DBL_MAX; hi = -DBL_MAX;
   if(t2<=t1) return false;

   int i1 = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   int i2 = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t1,   false);
   if(i1<0 || i2<0) return false;
   if(i2 < i1){ int tmp=i1; i1=i2; i2=tmp; }

   for(int i=i1; i<=i2; i++)
   {
      datetime bt = iTime(_Symbol, (ENUM_TIMEFRAMES)_Period, i);
      if(bt < t1) continue;
      if(bt >= t2) continue;

      double L = iLow (_Symbol, (ENUM_TIMEFRAMES)_Period, i);
      double H = iHigh(_Symbol, (ENUM_TIMEFRAMES)_Period, i);
      if(L < lo) lo = L;
      if(H > hi) hi = H;
   }
   if(lo==DBL_MAX || hi==-DBL_MAX) return false;
   return (hi>lo);
}

//====================================================
// سعر إغلاق آخر شمعة داخل [t1, t2)
//====================================================
double GetCloseAtEnd(datetime t1, datetime t2)
{
   if(t2<=t1) return EMPTY_VALUE;

   int i = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   if(i<0) return EMPTY_VALUE;

   datetime bt = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
   if(bt < t1) return EMPTY_VALUE; // لا توجد شمعة فعلية داخل هذا النطاق بعد

   return iClose(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
}

//====================================================
// دوال مساعدة لنقاط الارتداد
//====================================================
bool IsVBuy(int idx)
{
   if(idx<1) return false;
   if(idx+1 >= g_rates_total) return false;

   double L0 = iLow(_Symbol,(ENUM_TIMEFRAMES)_Period,idx);
   double L1 = iLow(_Symbol,(ENUM_TIMEFRAMES)_Period,idx+1);
   double Lm = iLow(_Symbol,(ENUM_TIMEFRAMES)_Period,idx-1);
   return (L0 < L1 && L0 < Lm);
}

bool IsVSell(int idx)
{
   if(idx<1) return false;
   if(idx+1 >= g_rates_total) return false;

   double H0 = iHigh(_Symbol,(ENUM_TIMEFRAMES)_Period,idx);
   double H1 = iHigh(_Symbol,(ENUM_TIMEFRAMES)_Period,idx+1);
   double Hm = iHigh(_Symbol,(ENUM_TIMEFRAMES)_Period,idx-1);
   return (H0 > H1 && H0 > Hm);
}

bool CoveredAfterBuy(datetime t1, datetime t2, int pivotIdx, double level, double eps)
{
   int newerStart = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   if(newerStart < 0) return true;

   for(int i=pivotIdx-1; i>=newerStart; i--)
   {
      datetime bt = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      if(bt < t1 || bt >= t2) continue;

      double L = iLow(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      if(L < level - eps) return true; // تمت تغطيته بقاع أدنى بعد نقطة الارتداد
   }
   return false;
}

bool CoveredAfterSell(datetime t1, datetime t2, int pivotIdx, double level, double eps)
{
   int newerStart = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   if(newerStart < 0) return true;

   for(int i=pivotIdx-1; i>=newerStart; i--)
   {
      datetime bt = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      if(bt < t1 || bt >= t2) continue;

      double H = iHigh(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      if(H > level + eps) return true; // تمت تغطيته بقمة أعلى بعد نقطة الارتداد
   }
   return false;
}

//====================================================
// فلترة الإشارات
//====================================================
bool PassSignalFilter(const string tag)
{
   if(InpSignalFilter == FILTER_ALL) return true;
   if(InpSignalFilter == FILTER_NQH  && tag=="NQH")  return true;
   if(InpSignalFilter == FILTER_NQF  && tag=="NQF")  return true;
   if(InpSignalFilter == FILTER_NQKH && tag=="NQKH") return true;
   return false;
}

//====================================================
// تصنيف NQ (بدون أي تغيير في المنطق الأصلي — قوانين اللمس كما هي)
// القيمة المُعادة: 0=NQH, 1=NQF, 2=NQKH, 99=تجاهل
//====================================================
int ClassifyBuy_NQ(double level, double candleLow, double epsTouch, double tolPips)
{
   // NQH: لمس (ضمن الهامش) دون كسر أسفل المستوى
   if(MathAbs(candleLow - level) <= epsTouch && candleLow >= level - epsTouch)
      return 0;

   // NQF: لم يُلمس، لكنه أغلق فوق المستوى ضمن التسامح
   if(candleLow > level)
   {
      double dist = ToPips(candleLow - level);
      if(dist <= tolPips) return 1;
      return 99;
   }

   // NQKH: خدش أسفل المستوى لكن ضمن التسامح
   if(candleLow < level)
   {
      double cross = ToPips(level - candleLow);
      if(cross <= tolPips) return 2;
      return 99;
   }

   return 99;
}

int ClassifySell_NQ(double level, double candleHigh, double epsTouch, double tolPips)
{
   // NQH: لمس (ضمن الهامش) دون كسر أعلى المستوى
   if(MathAbs(candleHigh - level) <= epsTouch && candleHigh <= level + epsTouch)
      return 0;

   // NQF: لم يُلمس، لكنه أغلق أسفل المستوى ضمن التسامح
   if(candleHigh < level)
   {
      double dist = ToPips(level - candleHigh);
      if(dist <= tolPips) return 1;
      return 99;
   }

   // NQKH: خدش أعلى المستوى لكن ضمن التسامح
   if(candleHigh > level)
   {
      double cross = ToPips(candleHigh - level);
      if(cross <= tolPips) return 2;
      return 99;
   }

   return 99;
}

//====================================================
// التنبيهات
//====================================================
// عندما يكون التنبيه الرئيسي الموحّد مفعّلاً وخيار الكتم مفعّلاً معه، تُكتم أصوات/نوافذ
// التنبيهات الفرعية (NQ + Overlay + MTF) بينما يبقى الرسم البصري (الأسهم/الخطوط) كما هو تمامًا.
bool MasterMuting()
{
   return (InpMasterAlertEnable && InpMasterMuteOtherAlerts);
}

void TryAlert(string side, string unitCode, int unitIndexFixed, string tag, double level, double basePerc, datetime signalTime, int &alertsSent)
{
   if(!PassSignalFilter(tag)) return;

   if(alertsSent >= InpMaxAlerts) return;
   if(unitIndexFixed != 1) return;
   if(InpAlertsOnlyOnUnitClose && !g_allowAlertsNow) return;

   string key = _Symbol+"_"+side+"_"+unitCode+"_"+tag+"_"+DoubleToString(level,_Digits)+"_"+DoubleToString(basePerc,1);
   if(signalTime <= g_lastAlertTime && key==g_lastAlertKey) return;

   g_lastAlertTime = signalTime;
   g_lastAlertKey  = key;

   string msg = "AIBV2F "+_Symbol+" "+side+" "+unitCode+" "+tag+" "+DoubleToString(basePerc,1)+" @ "+DoubleToString(level,_Digits);

   if(InpPopupAlert && !MasterMuting()) Alert(msg);
   if(InpSoundAlert && !MasterMuting()) PlaySound("alert.wav");
   if(InpPushAlert)  SendNotification(msg);

   alertsSent++;
}

//====================================================
// رصد ورسم نقاط الارتداد (فحص خفيف)
//====================================================
void DetectAndDrawPivots(datetime t1, datetime t2, int unitIndexFixed, string unitCode,
                         double level, double epsCover, double tolPips, double basePerc, int &alertsSent)
{
   if(basePerc==0.0 || basePerc==100.0) return;

   int newerIdx = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   int olderIdx = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t1,   false);
   if(newerIdx<0 || olderIdx<0) return;
   if(olderIdx < newerIdx){ int tmp=newerIdx; newerIdx=olderIdx; olderIdx=tmp; }

   if(InpPivotScanMaxBars > 0)
      olderIdx = MathMin(olderIdx, newerIdx + InpPivotScanMaxBars);

   double gap      = InpArrowGapPips * PipSize();
   double txtGap   = InpTextGapPips  * PipSize();
   double minReb   = InpMinReboundPips * PipSize();
   double epsTouch = InpTouchEpsPips * PipSize();

   for(int i=newerIdx; i<=olderIdx; i++)
   {
      datetime bt = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      if(bt < t1 || bt >= t2) continue;

      double c = iClose(_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      double h = iHigh (_Symbol,(ENUM_TIMEFRAMES)_Period,i);
      double l = iLow  (_Symbol,(ENUM_TIMEFRAMES)_Period,i);

      // قيم فلاتر التأكيد (MA/RSI) عند هذه الشمعة بالذات
      double maAtI = EMPTY_VALUE, rsiAtI = EMPTY_VALUE;
      int bufIdx = i - g_pivotBufShift0;
      if(g_pivotMaReady  && bufIdx>=0 && bufIdx<ArraySize(g_pivotMaBuf))  maAtI  = g_pivotMaBuf[bufIdx];
      if(g_pivotRsiReady && bufIdx>=0 && bufIdx<ArraySize(g_pivotRsiBuf)) rsiAtI = g_pivotRsiBuf[bufIdx];

      bool maOkBuy   = (!InpUseMAFilter)  || (maAtI!=EMPTY_VALUE  && c > maAtI);
      bool maOkSell  = (!InpUseMAFilter)  || (maAtI!=EMPTY_VALUE  && c < maAtI);
      bool rsiOkBuy  = (!InpUseRSIFilter) || (rsiAtI!=EMPTY_VALUE && rsiAtI > InpRSIBuyLevel);
      bool rsiOkSell = (!InpUseRSIFilter) || (rsiAtI!=EMPTY_VALUE && rsiAtI < InpRSISellLevel);

      // BUY
      int clsB = ClassifyBuy_NQ(level, l, epsTouch, tolPips);
      if(clsB != 99 && maOkBuy && rsiOkBuy)
      {
         bool reboundBuy = (c >= level + minReb);
         bool vBuy = IsVBuy(i);

         if(reboundBuy || vBuy)
         {
            if(!CoveredAfterBuy(t1,t2,i,level,epsCover))
            {
               string tagB = (clsB==0 ? "NQH" : (clsB==1 ? "NQF" : "NQKH"));
               if(!PassSignalFilter(tagB)) continue;

               string nmA  = PFX+"AR_"+unitCode+"_"+IntegerToString(unitIndexFixed)+"_B_"+DoubleToString(level,_Digits)+"_"+IntegerToString(i);
               string nmT  = PFX+"TX_"+unitCode+"_"+IntegerToString(unitIndexFixed)+"_B_"+DoubleToString(level,_Digits)+"_"+IntegerToString(i);

               // BUY: السهم أسفل، والنص أسفل السهم
               double yA = level - gap;
               double yT = yA   - txtGap;

               if(InpShowPivotArrows) CreateArrow(nmA, bt, yA, 233, clrDodgerBlue);
               if(InpShowPivotText)   CreateText (nmT, bt, yT, tagB+" "+DoubleToString(basePerc,1), clrWhite, 9, ANCHOR_CENTER);

               // تحديث "آخر إشارة" للوحة المعلومات — معلوماتي فقط، لا يدخل في تصويت التأكيد
               if(bt >= g_lastSignalTime)
               {
                  g_lastSignalText  = tagB+" شراء ("+unitCode+")";
                  g_lastSignalTime  = bt;
                  g_lastSignalColor = InpDashColorUp;
                  g_lastSignalDir   = DIR_UP;
               }

               TryAlert("BUY", unitCode, unitIndexFixed, tagB, level, basePerc, bt, alertsSent);
            }
         }
      }

      // SELL
      int clsS = ClassifySell_NQ(level, h, epsTouch, tolPips);
      if(clsS != 99 && maOkSell && rsiOkSell)
      {
         bool reboundSell = (c <= level - minReb);
         bool vSell = IsVSell(i);

         if(reboundSell || vSell)
         {
            if(!CoveredAfterSell(t1,t2,i,level,epsCover))
            {
               string tagS = (clsS==0 ? "NQH" : (clsS==1 ? "NQF" : "NQKH"));
               if(!PassSignalFilter(tagS)) continue;

               string nmAS = PFX+"AR_"+unitCode+"_"+IntegerToString(unitIndexFixed)+"_S_"+DoubleToString(level,_Digits)+"_"+IntegerToString(i);
               string nmTS = PFX+"TX_"+unitCode+"_"+IntegerToString(unitIndexFixed)+"_S_"+DoubleToString(level,_Digits)+"_"+IntegerToString(i);

               // SELL: السهم أعلى، والنص أسفل السهم
               double yA = level + gap;
               double yT = yA   - txtGap;

               if(InpShowPivotArrows) CreateArrow(nmAS, bt, yA, 234, clrRed);
               if(InpShowPivotText)   CreateText (nmTS, bt, yT, tagS+" "+DoubleToString(basePerc,1), clrWhite, 9, ANCHOR_CENTER);

               // تحديث "آخر إشارة" للوحة المعلومات — معلوماتي فقط، لا يدخل في تصويت التأكيد
               if(bt >= g_lastSignalTime)
               {
                  g_lastSignalText  = tagS+" بيع ("+unitCode+")";
                  g_lastSignalTime  = bt;
                  g_lastSignalColor = InpDashColorDown;
                  g_lastSignalDir   = DIR_DOWN;
               }

               TryAlert("SELL", unitCode, unitIndexFixed, tagS, level, basePerc, bt, alertsSent);
            }
         }
      }
   }
}

//====================================================
// ربط الألوان بالمستوى
//====================================================
color ColorByBasePerc(double basePerc)
{
   if(basePerc==0.0 || basePerc==100.0) return InpColor_0_100;
   if(basePerc==23.6 || basePerc==76.4) return InpColor_23_76;
   if(basePerc==38.2 || basePerc==61.8) return InpColor_38_61;
   if(basePerc==50.0) return InpColor_50;
   return InpColor_Ext;
}

int WidthByBasePerc(double basePerc)
{
   if(basePerc==0.0 || basePerc==100.0) return InpWidth_0_100;
   if(basePerc==23.6 || basePerc==76.4) return InpWidth_23_76;
   if(basePerc==38.2 || basePerc==61.8) return InpWidth_38_61;
   if(basePerc==50.0) return InpWidth_50;
   return InpWidth_Ext;
}

ENUM_LINE_STYLE StyleByBasePerc(double basePerc)
{
   if(basePerc==0.0 || basePerc==100.0) return InpStyle_0_100;
   if(basePerc==23.6 || basePerc==76.4) return InpStyle_23_76;
   if(basePerc==38.2 || basePerc==61.8) return InpStyle_38_61;
   if(basePerc==50.0) return InpStyle_50;
   return InpStyle_Ext;
}

bool ShowByBasePerc(double basePerc)
{
   if(basePerc==0.0 || basePerc==100.0) return InpShow_0_100;
   if(basePerc==23.6 || basePerc==76.4) return InpShow_23_76;
   if(basePerc==38.2 || basePerc==61.8) return InpShow_38_61;
   if(basePerc==50.0) return InpShow_50;
   return InpShow_Ext;
}

//====================================================
// رسم نطاق الفيبو لوحدة واحدة
//====================================================
//====================================================
// تجهيز مخازن MA/RSI لنطاق [t1,t2) مرة واحدة قبل فحص كل مستويات الفيبو
// في هذه الوحدة (بدل إعادة القراءة 7 مرات لكل مستوى فيبو).
//====================================================
void PrepareFilterBuffers(datetime t1, datetime t2)
{
   g_pivotMaReady  = false;
   g_pivotRsiReady = false;
   ArrayResize(g_pivotMaBuf,0);
   ArrayResize(g_pivotRsiBuf,0);

   int newerIdx = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t2-1, false);
   int olderIdx = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, t1,   false);
   if(newerIdx<0 || olderIdx<0) return;
   if(olderIdx < newerIdx){ int tmp=newerIdx; newerIdx=olderIdx; olderIdx=tmp; }

   if(InpPivotScanMaxBars > 0)
      olderIdx = MathMin(olderIdx, newerIdx + InpPivotScanMaxBars);

   int count = olderIdx - newerIdx + 1;
   if(count <= 0) return;
   g_pivotBufShift0 = newerIdx;

   if(InpUseMAFilter && g_maHandle != INVALID_HANDLE)
   {
      ArraySetAsSeries(g_pivotMaBuf, true);
      if(CopyBuffer(g_maHandle, 0, newerIdx, count, g_pivotMaBuf) == count)
         g_pivotMaReady = true;
   }
   if(InpUseRSIFilter && g_rsiHandle != INVALID_HANDLE)
   {
      ArraySetAsSeries(g_pivotRsiBuf, true);
      if(CopyBuffer(g_rsiHandle, 0, newerIdx, count, g_pivotRsiBuf) == count)
         g_pivotRsiReady = true;
   }
}

void DrawFibSpan(datetime t1, datetime t2, double lo, double hi, string unitCode, int idx,
                 bool fixedUnit, bool drawUnitLabel, bool drawLevels, bool drawPivots,
                 bool snapRight = true, bool drawBox = true, bool showPipsText = true)
{
   datetime a1=t1, a2=t2;
   if(!SnapUnitToBars(t1,t2,a1,a2)) { a1=t1; a2=t2; }
   if(!snapRight) a2=t2;   // امتداد مستقبلي: يبقى النهاية النظرية، لا آخر شمعة متداولة

   if(drawBox)
   {
      color vcol = ApplyOpacity(clrWhite, InpOpacityPercent, InpBgColor);
      CreateVLine(PFX+"VL_"+unitCode+"_"+IntegerToString(idx)+"_L", a1, vcol, InpVLineWidth, STYLE_DOT);
      CreateVLine(PFX+"VL_"+unitCode+"_"+IntegerToString(idx)+"_R", a2, vcol, InpVLineWidth, STYLE_DOT);
   }

   double rng = hi - lo;
   if(rng <= 0) return;

   // ملاحظة: drawUnitLabel/showPipsText يصلان جاهزَين من المستدعي (مجموعة الوحدة)،
   // بحيث تتحكم كل مجموعة (أساسية أو إضافية) في ظهور اسمها ونقاطها بشكل مستقل تمامًا.
   if(drawUnitLabel)
   {
      datetime mid = (datetime)((a1 + a2)/2);
      double y = hi + (0.12*rng);

      string nm = PFX+"UN_"+unitCode+"_"+IntegerToString(idx);
      string txt = (fixedUnit ? unitCode + "(" + IntegerToString(idx) + ")" : unitCode + "(0)");

      CreateText(nm, mid, y, txt, clrWhite, 14, ANCHOR_CENTER);
   }

   if(showPipsText && fixedUnit && drawLevels)
   {
      double p = ToPips(rng);
      datetime midp = (datetime)((a1 + a2)/2);
      CreateText(PFX+"PP_"+unitCode+"_"+IntegerToString(idx), midp, lo - 6*PipSize(), DoubleToString(p,1), clrDodgerBlue, 10, ANCHOR_CENTER);
   }

   if(!drawLevels) return;

   datetime levelTextTime = a1;
   int shiftBars = MathMax(0, InpLevelTextTimeShiftBars);
   if(shiftBars > 0)
   {
      int barAtT1 = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, a1, false);
      int targetBar = MathMax(0, barAtT1 - shiftBars);
      levelTextTime = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,targetBar);
      if(levelTextTime < a1) levelTextTime = a1;
      if(levelTextTime >= a2) levelTextTime = a1;
   }
   if(InpLevelTextSide == 1)
      levelTextTime = (datetime)(a2 - (datetime)PeriodSeconds(_Period)*2);

   double textOffset = InpLevelTextOffsetPips * PipSize();

   // ملاحظة: كان هنا فحص إضافي على المتغير العام InpShowFibLevels يُطبَّق على كل
   // المجموعات معًا (الأساسية والإضافية)، مما يمنع فصلها عن بعضها. أُزيل هذا
   // الفحص المكرر لأن معامل drawLevels الممرَّر لكل مجموعة على حدة (من مفتاحها
   // الخاص: InpShowFibLevels للأساسية، أو InpAuxXXShowFib لكل وحدة إضافية) كافٍ
   // بذاته ومستقل تمامًا الآن — فكل مجموعة تُشغَّل/تُطفَأ فيبوها بمعزل عن غيرها.
   {
      for(int layer=-InpLayersEachSide; layer<=InpLayersEachSide; layer++)
      {
         for(int k=0; k<9; k++)
         {
            double basePerc = FibPerc[k];
            if(!ShowByBasePerc(basePerc)) continue; // هذه المجموعة مخفية — تجاوزها بالكامل

            double rr = FibRatios[k] + layer;
            double price = lo + rng*rr;

            color lineCol = ApplyOpacity(ColorByBasePerc(basePerc), InpOpacityPercent, InpBgColor);
            color textCol = ApplyOpacity(ColorByBasePerc(basePerc), InpTextOpacityPercent, InpBgColor);

            double shownPerc = basePerc + layer*100.0;

            string ln = PFX+"HL_"+unitCode+"_"+IntegerToString(idx)+"_"+IntegerToString(layer)+"_"+IntegerToString(k);

            CreateHSeg(ln, a1, price, a2, lineCol, WidthByBasePerc(basePerc), StyleByBasePerc(basePerc));

            if(InpShowLevelText)
            {
               string tx = PFX+"TXL_"+unitCode+"_"+IntegerToString(idx)+"_"+IntegerToString(layer)+"_"+IntegerToString(k);
               int anch = (InpLevelTextSide==1 ? ANCHOR_RIGHT : ANCHOR_LEFT);
               CreateText(tx, levelTextTime, price + textOffset, DoubleToString(shownPerc,1), textCol, InpLevelTextFontSize, anch);
            }
         }
      }
   }

   if(InpEnablePivots && drawPivots)
   {
      double unitPips = ToPips(rng);
      double tolPips  = unitPips * (InpTolPercent/100.0);
      double epsCover  = 0.2*PipSize(); // لفحص "تمت تغطيته بعد" (فحص خفيف)

      PrepareFilterBuffers(a1, a2); // تجهيز MA/RSI مرة واحدة لهذه الوحدة

      int alertsSent = 0;
      for(int k2=0; k2<9; k2++)
      {
         double basePerc2 = FibPerc[k2];
         if(basePerc2==0.0 || basePerc2==100.0) continue;

         double price2 = lo + rng*FibRatios[k2];
         DetectAndDrawPivots(a1,a2,idx,unitCode,price2,epsCover,tolPips,basePerc2,alertsSent);
      }
   }
}

//====================================================
// رسم مسار المتجه (Vector Path)
// القاعدة لكل وحدة:
//   إغلاق أعلى من مستوى refRatio -> خط صاعد -> يلتصق بمستوى 0.0
//   إغلاق أقل أو يساوي مستوى refRatio -> خط هابط -> يلتصق بمستوى 100.0
// تُربط الوحدات من الأقدم إلى الأحدث بخطوط أفقية وعمودية لتكوين مسار متدرج.
// ✅ lvlTag يجعل كل مستوى مرجعي (236 / 500 / 764) يرسم في مجموعة عناصر خاصة به،
// بحيث تعمل حتى 3 مسارات معًا دون أن يمحو أحدها الآخر.
//====================================================
void DrawVPConnector(string code, string lvlTag, string tag, datetime t, double priceOld, double priceNew,
                     color colOld, int widthOld, ENUM_LINE_STYLE styleOld, bool showOld)
{
   if(!showOld) return;
   string vname = PFX+"VP_V_"+lvlTag+"_"+code+"_"+tag;
   CreateVSeg(vname, t, priceOld, priceNew, colOld, widthOld, styleOld);
}

void DrawVectorPath(int grp, UnitType ut, string code, double refRatio, string lvlTag, color colUp, color colDown,
                    int widthUp = -1, int widthDown = -1, ENUM_LINE_STYLE styleUp = STYLE_SOLID, ENUM_LINE_STYLE styleDown = STYLE_SOLID,
                    bool useCustomStyle = false)
{
   // ملاحظة: أُزيل هنا الفحص المباشر على InpShowVectorPath (كان يُطفئ مسار
   // الوحدات الإضافية أيضًا بصرف النظر عن مفتاحها الخاص). الآن يُطبَّق هذا الفحص
   // فقط داخل DrawAllVectorPaths (غلاف المجموعة الأساسية)، بينما كل وحدة إضافية
   // تعتمد فقط على مفتاحها المستقل (مثل InpAuxM45ShowVP) — استقلال تام بين المجموعات.
   if(g_unitsDrawnFixed[grp] < 1) return;

   // إن لم يُمرَّر عرض/نمط مخصّص (المجموعة الأساسية كما كانت)، استخدم المتغيرات
   // العامة تمامًا كما في الأصل — لا تغيير في سلوك المجموعة الأساسية إطلاقًا.
   int wUp    = useCustomStyle ? widthUp   : InpVPWidthUp;
   int wDown  = useCustomStyle ? widthDown : InpVPWidthDown;
   ENUM_LINE_STYLE sUp   = useCustomStyle ? styleUp   : InpVPStyleUp;
   ENUM_LINE_STYLE sDown = useCustomStyle ? styleDown : InpVPStyleDown;

   double          prevNode  = 0.0;
   color           prevCol   = clrNONE;
   int             prevWidth = 1;
   ENUM_LINE_STYLE prevStyle = STYLE_SOLID;
   bool            prevShow  = true;
   bool            havePrev  = false;

   // من الأقدم (idx = g_unitsDrawnFixed[grp]) إلى الأحدث (idx = 1) — لهذه المجموعة فقط
   for(int idx = g_unitsDrawnFixed[grp]; idx >= 1; idx--)
   {
      if(!g_unitValid[grp][idx]) continue;

      datetime t1 = g_unitStart[grp][idx];
      datetime t2 = g_unitEnd[grp][idx];

      double lo = g_unitLo[grp][idx];
      double hi = g_unitHi[grp][idx];
      double rng = hi - lo;
      if(rng <= 0) continue;

      // صندوق المرجع = الوحدة السابقة (idx+1) — يُستخدم فقط لحساب عتبة المقارنة عند
      // refRatio. أما موضع العقدة نفسها فيبقى على نطاق هذه الوحدة (0/100 الخاصين بها).
      double refLo=0, refHi=0;
      bool haveRef = false;

      if(idx+1 <= g_unitsDrawnFixed[grp] && g_unitValid[grp][idx+1])
      {
         refLo = g_unitLo[grp][idx+1];
         refHi = g_unitHi[grp][idx+1];
         haveRef = true;
      }
      else
      {
         datetime prevStart = AddUnitEx(_Symbol, (ENUM_TIMEFRAMES)_Period, t1, ut, -1);
         haveRef = GetRange(prevStart, t1, refLo, refHi);
      }

      if(!haveRef) continue;

      double refRng = refHi - refLo;
      if(refRng <= 0) continue;

      double closeP = GetCloseAtEnd(t1, t2);
      if(closeP == EMPTY_VALUE) continue;

      double mid  = refLo + refRng*refRatio;
      bool   isUp = (closeP > mid);
      double node = isUp ? lo : hi;

      color           segCol   = isUp ? colUp   : colDown;
      int             segWidth = isUp ? wUp   : wDown;
      ENUM_LINE_STYLE segStyle = isUp ? sUp    : sDown;
      bool            segShow  = isUp ? InpVPShowUp    : InpVPShowDown;

      datetime a1=t1, a2=t2;
      if(!SnapUnitToBars(t1,t2,a1,a2)) { a1=t1; a2=t2; }

      if(havePrev)
         DrawVPConnector(code, lvlTag, IntegerToString(idx), a1, prevNode, node,
                          prevCol, prevWidth, prevStyle, prevShow);

      if(segShow)
      {
         string hname = PFX+"VP_H_"+lvlTag+"_"+code+"_"+IntegerToString(idx);
         CreateHSeg(hname, a1, node, a2, segCol, segWidth, segStyle);
      }

      prevNode  = node;
      prevCol   = segCol;
      prevWidth = segWidth;
      prevStyle = segStyle;
      prevShow  = segShow;
      havePrev  = true;
   }

   // امتداد المسار داخل الوحدة الحالية (قيد التكوّن) باستخدام آخر سعر إغلاق
   if(InpVectorPathIncludeCurrent && havePrev)
   {
      datetime now    = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,0);
      datetime start0 = FloorToUnitStartEx(_Symbol, (ENUM_TIMEFRAMES)_Period, now, ut);

      double lo0, hi0;
      bool have0 = false;

      if(InpD0UsesPrevFixedFib && g_unitValid[grp][1])
      {
         lo0 = g_unitLo[grp][1]; hi0 = g_unitHi[grp][1]; have0 = true;
      }
      else
      {
         have0 = GetRange(start0, now, lo0, hi0);
      }

      if(have0)
      {
         double rng0 = hi0 - lo0;
         if(rng0 > 0)
         {
            double liveClose = iClose(_Symbol,(ENUM_TIMEFRAMES)_Period,0);
            double mid0  = lo0 + rng0*refRatio;
            bool   isUp0 = (liveClose > mid0);
            double node0 = isUp0 ? lo0 : hi0;

            color           segCol0   = isUp0 ? colUp   : colDown;
            int             segWidth0 = isUp0 ? wUp   : wDown;
            ENUM_LINE_STYLE segStyle0 = isUp0 ? sUp    : sDown;
            bool            segShow0  = isUp0 ? InpVPShowUp    : InpVPShowDown;

            datetime a1 = start0;
            datetime a2live = now + (datetime)PeriodSeconds(_Period);

            DrawVPConnector(code, lvlTag, "0", a1, prevNode, node0,
                             prevCol, prevWidth, prevStyle, prevShow);

            if(segShow0)
            {
               string hname = PFX+"VP_H_"+lvlTag+"_"+code+"_0";
               CreateHSeg(hname, a1, node0, a2live, segCol0, segWidth0, segStyle0);
            }
         }
      }
   }
}

//====================================================
// رسم المسارات الثلاثة (23.6% / 50% / 76.4%) لمجموعة وحدة واحدة، كل مستوى بمفتاحه ولونيه الخاصين.
// نفس المستويات الثلاثة ونفس الألوان تُطبَّق على أي مجموعة (أساسية أو إضافية) تستدعيها —
// التمييز بينها بصريًا يأتي من حجم الصندوق نفسه (ساعة/نصف ساعة/ربع ساعة) واسم كودها.
// (هذه هي النسخة الأصلية بلا أي تغيير — تُستخدم فقط للمجموعة الأساسية GRP_PRIMARY)
//====================================================
void DrawAllVectorPaths(int grp, UnitType ut, string code)
{
   if(!InpShowVectorPath) return; // بوابة خاصة بمسار المجموعة الأساسية فقط — لا تؤثر على أي وحدة إضافية
   if(InpShowVP_236) DrawVectorPath(grp, ut, code, 0.236, "236", InpVPColorUp_236, InpVPColorDown_236);
   if(InpShowVP_500) DrawVectorPath(grp, ut, code, 0.500, "500", InpVPColorUp_500, InpVPColorDown_500);
   if(InpShowVP_618) DrawVectorPath(grp, ut, code, 0.618, "618", InpVPColorUp_618, InpVPColorDown_618);
   if(InpShowVP_764) DrawVectorPath(grp, ut, code, 0.764, "764", InpVPColorUp_764, InpVPColorDown_764);
}

//====================================================
// نسخة موازية لأي مجموعة إضافية (وحدات فرعية): تستخدم لونًا وسماكة ونمطًا
// مستقلين تمامًا لهذه المجموعة بذاتها — بصرف النظر عن أي نسبة فيبو (23.6/50/
// 61.8/76.4) تُرسم لها، فكل نسب هذه المجموعة تشترك في نفس اللون والسماكة
// والنمط الخاصين بها، لتمييزها بصريًا عن كل المجموعات الأخرى على الشارت.
//====================================================
void DrawAllVectorPathsGroup(int grp, UnitType ut, string code, color colUp, color colDown, int width, ENUM_LINE_STYLE style)
{
   if(InpShowVP_236) DrawVectorPath(grp, ut, code, 0.236, "236", colUp, colDown, width, width, style, style, true);
   if(InpShowVP_500) DrawVectorPath(grp, ut, code, 0.500, "500", colUp, colDown, width, width, style, style, true);
   if(InpShowVP_618) DrawVectorPath(grp, ut, code, 0.618, "618", colUp, colDown, width, width, style, style, true);
   if(InpShowVP_764) DrawVectorPath(grp, ut, code, 0.764, "764", colUp, colDown, width, width, style, style, true);
}

// يُعيد لون/سماكة/نمط المسار الخاص بكل مجموعة إضافية على حدة (11 مجموعة، كل واحدة مستقلة تمامًا)
void GetGroupVPStyle(int grp, color &colUp, color &colDown, int &width, ENUM_LINE_STYLE &style)
{
   switch(grp)
   {
      case GRP_AUX_M45:   colUp=InpAuxM45ColorUp;   colDown=InpAuxM45ColorDown;   width=InpAuxM45Width;   style=InpAuxM45Style;   break;
      case GRP_AUX_M30:   colUp=InpAuxM30ColorUp;   colDown=InpAuxM30ColorDown;   width=InpAuxM30Width;   style=InpAuxM30Style;   break;
      case GRP_AUX_M15:   colUp=InpAuxM15ColorUp;   colDown=InpAuxM15ColorDown;   width=InpAuxM15Width;   style=InpAuxM15Style;   break;
      case GRP_AUX_M7H:   colUp=InpAuxM7HColorUp;   colDown=InpAuxM7HColorDown;   width=InpAuxM7HWidth;   style=InpAuxM7HStyle;   break;
      case GRP_AUX_D1H30: colUp=InpAuxD1H30ColorUp; colDown=InpAuxD1H30ColorDown; width=InpAuxD1H30Width; style=InpAuxD1H30Style; break;
      case GRP_AUX_D3:    colUp=InpAuxD3ColorUp;    colDown=InpAuxD3ColorDown;    width=InpAuxD3Width;    style=InpAuxD3Style;    break;
      case GRP_AUX_D6:    colUp=InpAuxD6ColorUp;    colDown=InpAuxD6ColorDown;    width=InpAuxD6Width;    style=InpAuxD6Style;    break;
      case GRP_AUX_D12:   colUp=InpAuxD12ColorUp;   colDown=InpAuxD12ColorDown;   width=InpAuxD12Width;   style=InpAuxD12Style;   break;
      case GRP_AUX_D24:   colUp=InpAuxD24ColorUp;   colDown=InpAuxD24ColorDown;   width=InpAuxD24Width;   style=InpAuxD24Style;   break;
      case GRP_AUX_H18:   colUp=InpAuxH18ColorUp;   colDown=InpAuxH18ColorDown;   width=InpAuxH18Width;   style=InpAuxH18Style;   break;
      case GRP_AUX_H4_5:  colUp=InpAuxH4_5ColorUp;  colDown=InpAuxH4_5ColorDown;  width=InpAuxH4_5Width;  style=InpAuxH4_5Style;  break;
      default:            colUp=InpVPColorUp_500;   colDown=InpVPColorDown_500;   width=InpVPWidthUp;     style=InpVPStyleUp;     break;
   }
}

//====================================================
// بناء ورسم مجموعة وحدة واحدة بالكامل (الصندوق + الاسم + النقاط + الفيبو + مسار
// المتجه)، لأي نوع وحدة (UnitType) وأي عدد مجموعات — هذه الدالة تُستدعى مرة
// لكل مجموعة (الأساسية دائمًا، وحتى 9 مجموعات إضافية اختيارية)، بحيث يمكن
// عرض حتى 10 وحدات مختلفة الحجم معًا على نفس الشارت دون أي تصادم في الرسم.
//
// grp:          رقم المجموعة (GRP_PRIMARY / GRP_AUX_M30 / GRP_AUX_M15)
// allowPivots:  تفعيل رصد أسهم الارتداد + التنبيهات لهذه المجموعة (المجموعة
//               الأساسية فقط عادةً — الوحدات الإضافية عرض بصري فقط)
//====================================================
void BuildAndDrawUnitGroup(int grp, UnitType ut, string code, int unitsToShow,
                           bool showBox, bool showLabel, bool showPips, bool showFib, bool showVP,
                           bool allowPivots)
{
   for(int i=0;i<256;i++) g_unitValid[grp][i] = false;
   g_unitsDrawnFixed[grp] = 0;

   datetime now    = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,0);
   datetime start0 = FloorToUnitStartEx(_Symbol, (ENUM_TIMEFRAMES)_Period, now, ut);
   datetime end0   = AddUnitEx(_Symbol, (ENUM_TIMEFRAMES)_Period, start0, ut, 1);

   if(grp==GRP_PRIMARY)
   {
      if(g_lastUnitStart == 0) g_lastUnitStart = start0;
      g_allowAlertsNow = false;

      if(InpAlertsOnlyOnUnitClose)
      {
         if(start0 != g_lastUnitStart)
         {
            g_allowAlertsNow = true;
            g_lastUnitStart  = start0;
         }
      }
      else g_allowAlertsNow = true;
   }

   // الوحدة السابقة المكتملة (D1)
   datetime prevStart = AddUnitEx(_Symbol, (ENUM_TIMEFRAMES)_Period, start0, ut, -1);
   datetime prevEnd   = start0;

   double loPrev, hiPrev;
   bool okPrev = GetRange(prevStart, prevEnd, loPrev, hiPrev);

   if(okPrev)
   {
      g_unitStart[grp][1]=prevStart; g_unitEnd[grp][1]=prevEnd;
      g_unitLo[grp][1]=loPrev; g_unitHi[grp][1]=hiPrev;
      g_unitValid[grp][1]=true;
      g_unitsDrawnFixed[grp]=1;
   }

   // D0: overlay/امتداد مستقبلي — الأسهم تُرسم عليها أيضًا (عند allowPivots=true)
   // بنفس قوانين اللمس والتصنيف تمامًا، دون أي تعديل عليها.
   if(InpD0UsesPrevFixedFib && okPrev)
   {
      if(InpD0FullFuture)
         DrawFibSpan(start0, end0, loPrev, hiPrev, code, 0, true, showLabel, showFib, allowPivots, false, showBox, showPips);
      else
         DrawFibSpan(start0, end0, loPrev, hiPrev, code, 0, false, showLabel, showFib, allowPivots, true, showBox, showPips);
   }
   else
   {
      double lo0, hi0;
      if(GetRange(start0, now, lo0, hi0))
         DrawFibSpan(start0, end0, lo0, hi0, code, 0, false, showLabel, showFib, allowPivots, true, showBox, showPips);
   }

   // الوحدات الثابتة الأقدم: (1)..(N)
   int drawn = 0;
   datetime fixedEnd = start0;

   int safety = unitsToShow * 10;
   while(drawn < unitsToShow && safety > 0)
   {
      safety--;
      datetime fixedStart = AddUnitEx(_Symbol, (ENUM_TIMEFRAMES)_Period, fixedEnd, ut, -1);

      double lo, hi;
      if(GetRange(fixedStart, fixedEnd, lo, hi))
      {
         drawn++;
         if(drawn < 256)
         {
            g_unitStart[grp][drawn]=fixedStart; g_unitEnd[grp][drawn]=fixedEnd;
            g_unitLo[grp][drawn]=lo; g_unitHi[grp][drawn]=hi;
            g_unitValid[grp][drawn]=true;
            g_unitsDrawnFixed[grp]=drawn;
         }

         DrawFibSpan(fixedStart, fixedEnd, lo, hi, code, drawn, true, showLabel, showFib, allowPivots, true, showBox, showPips);
      }
      fixedEnd = fixedStart;
   }

   if(showVP)
   {
      if(grp==GRP_PRIMARY)
      {
         DrawAllVectorPaths(grp, ut, code); // المجموعة الأساسية: بلا أي تغيير عن الأصل
      }
      else
      {
         color gcUp, gcDown; int gWidth; ENUM_LINE_STYLE gStyle;
         GetGroupVPStyle(grp, gcUp, gcDown, gWidth, gStyle);
         DrawAllVectorPathsGroup(grp, ut, code, gcUp, gcDown, gWidth, gStyle);
      }
   }
}

//====================================================
// رسم كل شيء: المجموعة الأساسية دائمًا (مطابقة لفريم الشارت الحالي، بها الأسهم
// والتنبيهات)، ثم — عند التفعيل — حتى 9 مجموعات إضافية: عائلة الدقيقة (45د/30د/
// 15د/7.5د) وعائلة اليوم (24س/12س/6س/3س/ساعة ونصف)، كل منها فوق أي فريم أسرع منها.
//====================================================
void DrawAll()
{
   if(!InpEnableIndicator)
   {
      ObjDelByPrefix(PFX);
      return;
   }

   UnitType ut = GetUnitType();
   if(ut==UNIT_NONE)
   {
      ObjDelByPrefix(PFX);
      return;
   }

   ObjDelByPrefix(PFX, "DASH_");

   string code = UnitTag(ut);

   // ملاحظة مهمة: نستدعي BuildAndDrawUnitGroup دائمًا (بصرف النظر عن InpShowPrimaryGroup)
   // لأن بيانات نطاق الوحدة (g_unitLo/Hi) التي تحسبها ضرورية لأنظمة أخرى مستقلة
   // تمامًا (المسار الكسري، كتلة إشارات الوحدة، موقع السعر في الداشبورد، سلّم
   // التوافق) — تعطيل هذه المجموعة يجب أن يُخفي الرسم المرئي فقط، لا يوقف
   // البيانات التي تعتمد عليها أنظمة أخرى. لذا نمرّر كل مفاتيح العرض كـ false
   // عند التعطيل بدل تخطّي الاستدعاء بالكامل.
   bool pShowBox   = InpShowPrimaryGroup;
   bool pShowLabel = InpShowPrimaryGroup && InpShowUnitName;
   bool pShowPips  = InpShowPrimaryGroup && InpShowPipsText;
   bool pShowFib   = InpShowPrimaryGroup && InpShowFibLevels; // InpShowFibLevels أصبحت الآن خاصة بالمجموعة الأساسية حصرًا
   bool pShowVP    = InpShowPrimaryGroup; // البوابة الفعلية لمسار المتجه (InpShowVectorPath) موجودة داخل DrawAllVectorPaths
   bool pAllowPiv  = InpShowPrimaryGroup;

   BuildAndDrawUnitGroup(GRP_PRIMARY, ut, code, InpUnitsToShow,
                          pShowBox, pShowLabel, pShowPips, pShowFib, pShowVP,
                          pAllowPiv);

   if(!InpShowPrimaryGroup)
   {
      // تنظيف أي عناصر مرئية متبقية من حالة سابقة كانت المجموعة فيها مفعّلة —
      // محصور بدقة بكود هذه المجموعة الحالي (code) وحده، فلا تُمس أي عناصر
      // لوحدات إضافية تشترك نفس بادئة الاسم.
      string primPfx[] = {"VL_","HL_","TXL_","UN_","PP_","AR_","TX_"};
      for(int p=0;p<ArraySize(primPfx);p++)
         ObjDelByPrefix(PFX+primPfx[p]+code+"_");

      string vpTags[] = {"236","500","618","764"};
      for(int vt=0; vt<ArraySize(vpTags); vt++)
      {
         ObjDelByPrefix(PFX+"VP_H_"+vpTags[vt]+"_"+code+"_");
         ObjDelByPrefix(PFX+"VP_V_"+vpTags[vt]+"_"+code+"_");
      }
   }

   double periodSec = (double)PeriodSeconds(_Period);

   // وحدة نصف الساعة الإضافية: تحتاج فريم شارت أصغر من 30 دقيقة كي تحتوي أكثر من شمعة واحدة لكل وحدة
   if(InpShowAuxM30 && periodSec < 1800)
      BuildAndDrawUnitGroup(GRP_AUX_M30, UNIT_30M, "m30", InpAuxM30UnitsToShow,
                             InpAuxM30ShowBox, InpAuxM30ShowLabel, InpAuxM30ShowPips, InpAuxM30ShowFib, InpAuxM30ShowVP,
                             false);

   // وحدة ربع الساعة الإضافية: تحتاج فريم شارت أصغر من 15 دقيقة
   if(InpShowAuxM15 && periodSec < 900)
      BuildAndDrawUnitGroup(GRP_AUX_M15, UNIT_15M, "m15", InpAuxM15UnitsToShow,
                             InpAuxM15ShowBox, InpAuxM15ShowLabel, InpAuxM15ShowPips, InpAuxM15ShowFib, InpAuxM15ShowVP,
                             false);

   // وحدة 45 دقيقة الإضافية: تحتاج فريم شارت أصغر من 45 دقيقة
   if(InpShowAuxM45 && periodSec < 2700)
      BuildAndDrawUnitGroup(GRP_AUX_M45, UNIT_45M, "m45", InpAuxM45UnitsToShow,
                             InpAuxM45ShowBox, InpAuxM45ShowLabel, InpAuxM45ShowPips, InpAuxM45ShowFib, InpAuxM45ShowVP,
                             false);

   // وحدة 7.5 دقيقة الإضافية (نصف الربع ساعة): تحتاج فريم شارت أصغر من 7.5 دقيقة
   if(InpShowAuxM7H && periodSec < 450)
      BuildAndDrawUnitGroup(GRP_AUX_M7H, UNIT_HALFQ, "m7h", InpAuxM7HUnitsToShow,
                             InpAuxM7HShowBox, InpAuxM7HShowLabel, InpAuxM7HShowPips, InpAuxM7HShowFib, InpAuxM7HShowVP,
                             false);

   // وحدة 4.5 ساعة الإضافية (تركيبية): تحتاج فريم شارت أصغر من 4.5 ساعة
   if(InpShowAuxH4_5 && periodSec < 16200)
      BuildAndDrawUnitGroup(GRP_AUX_H4_5, UNIT_4_5H, "H4.5", InpAuxH4_5UnitsToShow,
                             InpAuxH4_5ShowBox, InpAuxH4_5ShowLabel, InpAuxH4_5ShowPips, InpAuxH4_5ShowFib, InpAuxH4_5ShowVP,
                             false);

   // وحدة 18 ساعة الإضافية (تركيبية): تحتاج فريم شارت أصغر من 18 ساعة
   if(InpShowAuxH18 && periodSec < 64800)
      BuildAndDrawUnitGroup(GRP_AUX_H18, UNIT_18H, "H18", InpAuxH18UnitsToShow,
                             InpAuxH18ShowBox, InpAuxH18ShowLabel, InpAuxH18ShowPips, InpAuxH18ShowFib, InpAuxH18ShowVP,
                             false);

   // عائلة الوحدات اليومية (24 ساعة / 12 / 6 / 3 / ساعة ونصف) — تعمل على M5/M15/H1
   if(InpShowAuxD24 && periodSec < 86400)
      BuildAndDrawUnitGroup(GRP_AUX_D24, UNIT_DAY, "d24", InpAuxD24UnitsToShow,
                             InpAuxD24ShowBox, InpAuxD24ShowLabel, InpAuxD24ShowPips, InpAuxD24ShowFib, InpAuxD24ShowVP,
                             false);

   if(InpShowAuxD12 && periodSec < 43200)
      BuildAndDrawUnitGroup(GRP_AUX_D12, UNIT_12H, "d12", InpAuxD12UnitsToShow,
                             InpAuxD12ShowBox, InpAuxD12ShowLabel, InpAuxD12ShowPips, InpAuxD12ShowFib, InpAuxD12ShowVP,
                             false);

   if(InpShowAuxD6 && periodSec < 21600)
      BuildAndDrawUnitGroup(GRP_AUX_D6, UNIT_6H, "d6", InpAuxD6UnitsToShow,
                             InpAuxD6ShowBox, InpAuxD6ShowLabel, InpAuxD6ShowPips, InpAuxD6ShowFib, InpAuxD6ShowVP,
                             false);

   if(InpShowAuxD3 && periodSec < 10800)
      BuildAndDrawUnitGroup(GRP_AUX_D3, UNIT_3H, "d3", InpAuxD3UnitsToShow,
                             InpAuxD3ShowBox, InpAuxD3ShowLabel, InpAuxD3ShowPips, InpAuxD3ShowFib, InpAuxD3ShowVP,
                             false);

   if(InpShowAuxD1H30 && periodSec < 5400)
      BuildAndDrawUnitGroup(GRP_AUX_D1H30, UNIT_1H30, "d1h30", InpAuxD1H30UnitsToShow,
                             InpAuxD1H30ShowBox, InpAuxD1H30ShowLabel, InpAuxD1H30ShowPips, InpAuxD1H30ShowFib, InpAuxD1H30ShowVP,
                             false);

   if(InpAlertsOnlyOnUnitClose) g_allowAlertsNow = false;
}

// تصريح مسبق (يُستخدم في UpdateM5M15Overlay ويُعرَّف لاحقًا)
void FireAlert(string tag, DirState dir);

//====================================================
// القمة/القاع داخل [t1,t2) لرمز وإطار زمني محددين صراحةً
// (مستقل عن إطار الشارت نفسه — يُستخدم لقراءة صناديق M5/M15 وأنت على شارت M1)
//====================================================
bool GetRangeEx(string sym, ENUM_TIMEFRAMES tf, datetime t1, datetime t2, double &lo, double &hi)
{
   lo = DBL_MAX; hi = -DBL_MAX;
   if(t2<=t1) return false;

   int i1 = iBarShift(sym, tf, t2-1, false);
   int i2 = iBarShift(sym, tf, t1,   false);
   if(i1<0 || i2<0) return false;
   if(i2 < i1){ int tmp=i1; i1=i2; i2=tmp; }

   for(int i=i1; i<=i2; i++)
   {
      datetime bt = iTime(sym, tf, i);
      if(bt < t1) continue;
      if(bt >= t2) continue;

      double L = iLow (sym, tf, i);
      double H = iHigh(sym, tf, i);
      if(L < lo) lo = L;
      if(H > hi) hi = H;
   }
   if(lo==DBL_MAX || hi==-DBL_MAX) return false;
   return (hi>lo);
}

// اتجاه ومستوى refRatio الحيّين للصندوق قيد التكوّن على (sym, tf) — عامّة لأي نسبة فيبو،
// وليست مقيّدة بـ 50% فقط. تُستخدم من overlay القديمة (بـ 0.5) ومن لوحة المعلومات
// الموحّدة (بأي نسبة مفعّلة: 23.6/50/61.8/76.4) والوحدات الإضافية.
DirState GetLiveLevelR(string sym, ENUM_TIMEFRAMES tf, UnitType ut, double refRatio, double &mid)
{
   datetime now = iTime(sym, tf, 0);
   if(now == 0) return DIR_UNKNOWN; // بيانات هذا الفريم لم تُحمَّل بعد

   datetime start0 = FloorToUnitStartEx(sym, tf, now, ut);

   double lo=0, hi=0;
   bool have = false;
   {
      datetime prevEnd   = start0;
      datetime prevStart = AddUnitEx(sym, tf, start0, ut, -1);
      have = GetRangeEx(sym, tf, prevStart, prevEnd, lo, hi);
   }

   if(!have)
      have = GetRangeEx(sym, tf, start0, now, lo, hi);

   if(!have) return DIR_UNKNOWN;

   double rng = hi - lo;
   if(rng <= 0) return DIR_UNKNOWN;

   mid = lo + rng*refRatio;
   double liveClose = iClose(sym, tf, 0);
   return (liveClose > mid) ? DIR_UP : DIR_DOWN;
}

// نسخة متوافقة مع الاستدعاءات القديمة (overlay M5/M15/H1/H4/D1) التي تستخدم 50% دائمًا
DirState GetLiveLevel(string sym, ENUM_TIMEFRAMES tf, UnitType ut, double &mid)
{
   return GetLiveLevelR(sym, tf, ut, 0.5, mid);
}

//====================================================
// اتجاه مباشر من شمعة فريم قياسي حقيقي — بدون أي حساب "وحدة" (Floor/Add) إطلاقًا.
// الفرق الجوهري عن GetLiveLevelR: هذه الدالة لا تُنشئ صندوقًا زمنيًا مستقلاً بحساب
// تقويمي (قد يختلف عن حدود الشمعة الفعلية بسبب قواعد أسبوعية/جلسات الوسيط)، بل
// تقرأ مباشرة الشمعة رقم 1 (آخر شمعة مكتملة فعليًا من فريم tf) وتقارنها بإغلاق
// الشمعة رقم 0 (الجارية). هذا هو الأسلوب الصحيح عندما تكون "الوحدة" = "شمعة
// فريم قياسي حقيقي بذاتها" (M15/M30/H1/H4/D1/W1) — لا لبس ولا حاجة لمحاذاة يدوية،
// لأن iHigh/iLow/iClose تُرجع دائمًا حدود الشمعة كما بناها الخادم فعليًا.
// تُستخدم فقط للفريمات التي لها مكافئ قياسي حقيقي في MT5. أما 7.5 دقيقة و45
// دقيقة (لا فريم قياسي لهما) فتبقيان تستخدمان GetLiveLevelR (حساب "وحدة" حقيقية
// مبنية من شموع الشارت الحالي).
DirState GetNativeBarDirection(string sym, ENUM_TIMEFRAMES tf, double ratio, double &mid)
{
   if(iBars(sym, tf) < 2) return DIR_UNKNOWN; // لا تتوفر شمعة سابقة مكتملة بعد

   double hi = iHigh(sym, tf, 1);
   double lo = iLow(sym, tf, 1);
   if(hi<=0 || lo<=0 || hi<=lo) return DIR_UNKNOWN;

   mid = lo + (hi-lo)*ratio;

   double liveClose = iClose(sym, tf, 0);
   if(liveClose<=0) return DIR_UNKNOWN;

   return (liveClose > mid) ? DIR_UP : DIR_DOWN;
}

//====================================================
// رسم/تحديث خطوط 50% لـ M5+M15 وإطلاق تنبيهات صوتية عند الاختراق
//====================================================
void UpdateM5M15Overlay()
{
   if(!InpShowLevelsOverlay && !InpAlertOnCross) return;

   ENUM_TIMEFRAMES tfs[LVL_COUNT]    = { PERIOD_M5,   PERIOD_M15,  PERIOD_H1,  PERIOD_H4,   PERIOD_D1  };
   UnitType         uts[LVL_COUNT]    = { UNIT_3H,     UNIT_6H,     UNIT_DAY,   UNIT_WEEK,   UNIT_MONTH };
   string           tags[LVL_COUNT]   = { "M5",        "M15",       "H1",       "H4",        "D1"       };
   bool             shows[LVL_COUNT]  = { InpShow_M5,  InpShow_M15, InpShow_H1, InpShow_H4,  InpShow_D1 };
   color            cols[LVL_COUNT]   = { InpColor_M5, InpColor_M15,InpColor_H1,InpColor_H4, InpColor_D1};
   int              widths[LVL_COUNT] = { InpWidth_M5, InpWidth_M15,InpWidth_H1,InpWidth_H4, InpWidth_D1};
   ENUM_LINE_STYLE  styles[LVL_COUNT] = { InpStyle_M5, InpStyle_M15,InpStyle_H1,InpStyle_H4, InpStyle_D1};

   datetime tNow   = TimeCurrent();
   datetime tLeft  = tNow - (datetime)(200*PeriodSeconds(_Period));
   datetime tRight = tNow + (datetime)(200*PeriodSeconds(_Period));

   bool anyUnknown = false;

   for(int k=0; k<LVL_COUNT; k++)
   {
      double mid = 0;
      DirState st = GetLiveLevel(_Symbol, tfs[k], uts[k], mid);

      if(st == DIR_UNKNOWN)
      {
         anyUnknown = true;
      }
      else if(InpShowLevelsOverlay && shows[k])
      {
         string nLine = PFX+"LVL_"+tags[k];
         CreateHSeg(nLine, tLeft, mid, tRight, cols[k], widths[k], styles[k]);
         string nText = PFX+"LVL_"+tags[k]+"_TXT";
         CreateText(nText, tRight, mid, tags[k]+" 50%", cols[k], 8, ANCHOR_LEFT);
      }

      if(InpAlertOnCross && shows[k] && st != DIR_UNKNOWN)
      {
         if(g_levels_initialized && st != g_prevState[k])
            FireAlert(tags[k], st);
         g_prevState[k] = st;
      }
   }

   if(InpShowLevelsOverlay)
      Comment(anyUnknown ? "AIBFX: بانتظار تحميل بيانات بعض الأطر الزمنية... (افتح شارت ذلك الإطار لهذا الرمز مرة واحدة إذا لم تختفِ الرسالة)" : "");

   g_levels_initialized = true;
}

void FireAlert(string tag, DirState dir)
{
   string dirText = (dir==DIR_UP) ? "UP" : "DOWN";
   string msg = _Symbol + " " + tag + " crossed 50% -> " + dirText;

   if(InpAlertPopup && !MasterMuting())
      Alert(msg);

   if(InpAlertSpeech && !MasterMuting())
   {
      string file = (dir==DIR_UP) ? InpSoundFile_Up : InpSoundFile_Down;
      PlaySound(file);
   }
}

//====================================================
// مسار المتجه متعدد الفريمات (Multi-Timeframe Vector Path)
// يعمل بشكل مستقل تمامًا عن فريم الشارت الحالي: كل فريم قياسي (M1..MN1) يُقرأ
// مباشرة من بياناته الخاصة به (sym, tf)، وليس من مصفوفات الشارت الحالي. هذا هو
// الفرق الجوهري عن GetRange/GetCloseAtEnd/SnapUnitToBars الأصلية التي تفترض
// دائمًا (_Symbol, _Period) — أي فريم الشارت فقط.
//====================================================

// سعر إغلاق آخر شمعة داخل [t1, t2) على (sym, tf) محددين صراحةً
double GetCloseAtEndEx(string sym, ENUM_TIMEFRAMES tf, datetime t1, datetime t2)
{
   if(t2<=t1) return EMPTY_VALUE;

   int i = iBarShift(sym, tf, t2-1, false);
   if(i<0) return EMPTY_VALUE;

   datetime bt = iTime(sym, tf, i);
   if(bt < t1) return EMPTY_VALUE; // لا توجد شمعة فعلية داخل هذا النطاق بعد

   return iClose(sym, tf, i);
}

// محاذاة حدود الوحدة مع الشموع الفعلية على (sym, tf) محددين صراحةً
bool SnapUnitToBarsEx(string sym, ENUM_TIMEFRAMES tf, datetime t1, datetime t2, datetime &out1, datetime &out2)
{
   out1 = t1; out2 = t2;
   if(!InpSnapToTradingBars) return true;

   int idxS = iBarShift(sym, tf, t1, false);
   if(idxS < 0) return false;

   datetime ts = iTime(sym, tf, idxS);
   if(ts < t1 && idxS > 0) idxS--;

   out1 = iTime(sym, tf, idxS);

   int idxE = iBarShift(sym, tf, t2-1, false);
   if(idxE < 0) return false;

   out2 = iTime(sym, tf, idxE) + (datetime)PeriodSeconds(tf);
   if(out2 <= out1) return false;
   return true;
}

// بناء تاريخ الوحدات الثابتة لفريم مستقل (sym, tf) داخل مجموعة تخزين grp معيّنة.
// نفس منطق الجزء الخاص بالوحدات الثابتة في BuildAndDrawUnitGroup، لكن بدون أي
// رسم فيبو/أسهم — بيانات فقط، تُستخدم كمدخل لـ DrawVectorPathMTF.
bool BuildMTFUnitHistory(int grp, string sym, ENUM_TIMEFRAMES tf, UnitType ut, int unitsToShow)
{
   for(int i=0;i<256;i++) g_unitValid[grp][i] = false;
   g_unitsDrawnFixed[grp] = 0;

   datetime now = iTime(sym, tf, 0);
   if(now == 0) return false; // بيانات هذا الفريم لم تُحمَّل بعد لهذا الرمز

   datetime fixedEnd = FloorToUnitStartEx(sym, tf, now, ut);

   int drawn = 0;
   int safety = MathMax(1, unitsToShow) * 10;
   while(drawn < unitsToShow && safety > 0)
   {
      safety--;
      datetime fixedStart = AddUnitEx(sym, tf, fixedEnd, ut, -1);

      double lo, hi;
      if(GetRangeEx(sym, tf, fixedStart, fixedEnd, lo, hi))
      {
         drawn++;
         if(drawn < 256)
         {
            g_unitStart[grp][drawn]=fixedStart; g_unitEnd[grp][drawn]=fixedEnd;
            g_unitLo[grp][drawn]=lo; g_unitHi[grp][drawn]=hi;
            g_unitValid[grp][drawn]=true;
            g_unitsDrawnFixed[grp]=drawn;
         }
      }
      fixedEnd = fixedStart;
   }
   return (g_unitsDrawnFixed[grp] >= 1);
}

// رسم مسار المتجه لفريم مستقل (sym, tf) بنفس منطق DrawVectorPath الأصلي تمامًا:
// إغلاق الوحدة أعلى من refRatio (المحسوبة على مدى الوحدة السابقة) -> عقدة صاعدة
// تلتصق بمستوى 0% لهذه الوحدة. إغلاق أقل أو يساوي -> عقدة هابطة تلتصق بـ 100%.
// كما يمتد المسار داخل الوحدة قيد التكوّن، ويُطلق تنبيهًا عند افتتاح عقدة جديدة.
void DrawVectorPathMTF(int grp, string sym, ENUM_TIMEFRAMES tf, UnitType ut, string code,
                       double refRatio, color colUp, color colDown, int mtfIndex)
{
   if(g_unitsDrawnFixed[grp] < 1) return;

   double prevNode = 0.0;
   bool   havePrev = false;
   DirState lastDir = DIR_UNKNOWN;

   // من الأقدم إلى الأحدث لهذا الفريم فقط
   for(int idx=g_unitsDrawnFixed[grp]; idx>=1; idx--)
   {
      if(!g_unitValid[grp][idx]) continue;

      datetime t1 = g_unitStart[grp][idx];
      datetime t2 = g_unitEnd[grp][idx];
      double lo = g_unitLo[grp][idx];
      double hi = g_unitHi[grp][idx];
      double rng = hi - lo;
      if(rng <= 0) continue;

      double refLo=0, refHi=0;
      bool haveRef = false;

      if(idx+1 <= g_unitsDrawnFixed[grp] && g_unitValid[grp][idx+1])
      {
         refLo = g_unitLo[grp][idx+1];
         refHi = g_unitHi[grp][idx+1];
         haveRef = true;
      }
      else
      {
         datetime prevStart = AddUnitEx(sym, tf, t1, ut, -1);
         haveRef = GetRangeEx(sym, tf, prevStart, t1, refLo, refHi);
      }
      if(!haveRef) continue;

      double refRng = refHi - refLo;
      if(refRng <= 0) continue;

      double closeP = GetCloseAtEndEx(sym, tf, t1, t2);
      if(closeP == EMPTY_VALUE) continue;

      double mid  = refLo + refRng*refRatio;
      bool   isUp = (closeP > mid);
      double node = isUp ? lo : hi;

      datetime a1=t1, a2=t2;
      if(!SnapUnitToBarsEx(sym, tf, t1, t2, a1, a2)) { a1=t1; a2=t2; }

      if(havePrev)
      {
         string vname = PFX+"MTFVP_"+code+"_V_"+IntegerToString(idx);
         CreateVSeg(vname, a1, prevNode, node, isUp?colUp:colDown, InpMTFVPWidth, InpMTFVPStyle);
      }

      string hname = PFX+"MTFVP_"+code+"_H_"+IntegerToString(idx);
      CreateHSeg(hname, a1, node, a2, isUp?colUp:colDown, InpMTFVPWidth, InpMTFVPStyle);

      prevNode = node;
      havePrev = true;
      if(idx==1) lastDir = isUp ? DIR_UP : DIR_DOWN;
   }

   // امتداد المسار داخل الوحدة الحالية قيد التكوّن (باستخدام آخر سعر إغلاق حيّ لهذا الفريم)
   if(havePrev)
   {
      datetime now    = iTime(sym, tf, 0);
      datetime start0 = FloorToUnitStartEx(sym, tf, now, ut);

      double lo0=0, hi0=0; bool have0=false;
      if(g_unitValid[grp][1])
      {
         lo0 = g_unitLo[grp][1]; hi0 = g_unitHi[grp][1]; have0=true;
      }
      else
      {
         have0 = GetRangeEx(sym, tf, start0, now, lo0, hi0);
      }

      if(have0)
      {
         double rng0 = hi0 - lo0;
         if(rng0 > 0)
         {
            double liveClose = iClose(sym, tf, 0);
            double mid0  = lo0 + rng0*refRatio;
            bool   isUp0 = (liveClose > mid0);
            double node0 = isUp0 ? lo0 : hi0;

            datetime a1 = start0;
            datetime a2live = now + (datetime)PeriodSeconds(tf);

            string vname0 = PFX+"MTFVP_"+code+"_V_0";
            CreateVSeg(vname0, a1, prevNode, node0, isUp0?colUp:colDown, InpMTFVPWidth, InpMTFVPStyle);

            string hname0 = PFX+"MTFVP_"+code+"_H_0";
            CreateHSeg(hname0, a1, node0, a2live, isUp0?colUp:colDown, InpMTFVPWidth, InpMTFVPStyle);

            lastDir = isUp0 ? DIR_UP : DIR_DOWN;
         }
      }
   }

   // التنبيه عند افتتاح عقدة جديدة مختلفة عن سابقتها (أي إغلاق/افتتاح وحدة جديدة)
   if(InpMTFAlertOnNewNode && lastDir != DIR_UNKNOWN)
   {
      if(g_mtfInitialized[mtfIndex] && lastDir != g_mtfPrevDir[mtfIndex])
      {
         string dirTxt = (lastDir==DIR_UP) ? "UP" : "DOWN";
         string msg = sym+" "+code+" VectorPath("+DoubleToString(refRatio*100.0,1)+"%) -> "+dirTxt;

         if(InpMTFAlertPopup && !MasterMuting()) Alert(msg);
         if(InpMTFAlertSound && !MasterMuting()) PlaySound(lastDir==DIR_UP ? InpMTFSoundUp : InpMTFSoundDown);
      }
      g_mtfPrevDir[mtfIndex]    = lastDir;
      g_mtfInitialized[mtfIndex] = true;
   }

   // تُحدَّث دائمًا بصرف النظر عن إعدادات التنبيه — تُستخدم في لوحة المعلومات الموحّدة
   g_mtfCurrentDir[mtfIndex] = lastDir;
}

// المنسّق: يمرّ على الفريمات القياسية التسعة (M1..MN1) ويبني ويرسم مسار المتجه
// لكل فريم مفعّل، بصرف النظر تمامًا عن فريم الشارت الحالي.
void UpdateMTFVectorPaths()
{
   if(!InpEnableMTFVectorPath)
   {
      ObjDelByPrefix(PFX+"MTFVP_");
      return;
   }

   ENUM_TIMEFRAMES tfs[MTF_COUNT]   = { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30,
                                        PERIOD_H1, PERIOD_H4, PERIOD_D1,  PERIOD_W1, PERIOD_MN1 };
   UnitType        uts[MTF_COUNT]   = { UNIT_1H,   UNIT_3H,   UNIT_6H,    UNIT_12H,
                                        UNIT_DAY,  UNIT_WEEK, UNIT_MONTH, UNIT_6MONTH, UNIT_YEAR };
   string          codes[MTF_COUNT] = { "M1","M5","M15","M30","H1","H4","D1","W1","MN1" };
   bool            shows[MTF_COUNT] = { InpMTFShow_M1, InpMTFShow_M5, InpMTFShow_M15, InpMTFShow_M30,
                                        InpMTFShow_H1, InpMTFShow_H4, InpMTFShow_D1, InpMTFShow_W1, InpMTFShow_MN1 };

   for(int k=0; k<MTF_COUNT; k++)
   {
      if(!shows[k])
      {
         ObjDelByPrefix(PFX+"MTFVP_"+codes[k]+"_");
         continue;
      }

      int grp = GRP_MTF_M1 + k;
      if(BuildMTFUnitHistory(grp, _Symbol, tfs[k], uts[k], InpMTFUnitsToShow))
         DrawVectorPathMTF(grp, _Symbol, tfs[k], uts[k], codes[k],
                            InpMTFVPRatio, InpMTFColorUp, InpMTFColorDown, k);
   }
}

//====================================================
// المسار الكسري داخل الوحدة (Intra-Unit Fractional Path)
// يقسّم الوحدة الأساسية الحالية (D0، أيًا كان فريم الشارت) إلى 4 نقاط تفتيش عند
// 1/8، 1/4، 1/2، 3/4 من مدتها الحقيقية الفعلية (المحسوبة من حدود الوحدة نفسها
// عبر FloorToUnitStartEx/AddUnitEx — دقيقة تمامًا حتى للشهر/الأسبوع المتغيّري
// الطول، بلا أي تقريب بالثواني). كل نقطة تُقارن بمستوى مرجعي (InpFracPathRatio
// من مدى الوحدة السابقة المكتملة — نفس مرجع مسار المتجه الأساسي في المؤشر).
//====================================================
void DrawFractionalPath()
{
   string fracPfx = PFX+"FRACVP_";

   if(!InpShowFracPath)
   {
      ObjDelByPrefix(fracPfx);
      for(int i=0;i<4;i++) g_fracDir[i] = DIR_UNKNOWN;
      return;
   }

   UnitType ut = GetUnitType();
   if(ut == UNIT_NONE || !g_unitValid[GRP_PRIMARY][1])
   {
      for(int i=0;i<4;i++) g_fracDir[i] = DIR_UNKNOWN;
      return;
   }

   datetime nowBar = iTime(_Symbol, (ENUM_TIMEFRAMES)_Period, 0);
   datetime start0 = FloorToUnitStartEx(_Symbol, (ENUM_TIMEFRAMES)_Period, nowBar, ut);
   datetime end0   = AddUnitEx(_Symbol, (ENUM_TIMEFRAMES)_Period, start0, ut, 1);
   double   durSec = (double)(end0 - start0);
   if(durSec <= 0) { for(int i=0;i<4;i++) g_fracDir[i] = DIR_UNKNOWN; return; }

   // المرجع: فيبو الوحدة السابقة المكتملة — نفس تقليد مسار المتجه الأساسي في المؤشر بالكامل
   double refLo = g_unitLo[GRP_PRIMARY][1];
   double refHi = g_unitHi[GRP_PRIMARY][1];
   double refRng = refHi - refLo;
   if(refRng <= 0) { for(int i=0;i<4;i++) g_fracDir[i] = DIR_UNKNOWN; return; }
   double midLevel = refLo + refRng*InpFracPathRatio;

   double   fractions[4]  = {0.125, 0.25, 0.5, 0.75};
   bool     fracEnabled[4]= {InpShowFrac18, InpShowFrac14, InpShowFrac12, InpShowFrac34};
   string   fracTag[4]    = {"F18","F14","F12","F34"};
   string   fracLabel[4]  = {"1/8","1/4","1/2","3/4"};

   datetime nowTime = TimeCurrent();

   for(int i=0;i<4;i++)
   {
      g_fracDir[i] = DIR_UNKNOWN;

      string vname = fracPfx+"V_"+fracTag[i];
      string hname = fracPfx+"H_"+fracTag[i];
      string tname = fracPfx+"T_"+fracTag[i];

      if(!fracEnabled[i])
      {
         ObjectDelete(0, vname); ObjectDelete(0, hname); ObjectDelete(0, tname);
         continue;
      }

      datetime cutT = start0 + (datetime)(durSec*fractions[i]);
      if(nowTime < cutT)
      {
         // لم تصل الوحدة الحالية لهذه النقطة بعد — لا رسم، لا اتجاه متاح
         ObjectDelete(0, vname); ObjectDelete(0, hname); ObjectDelete(0, tname);
         continue;
      }

      int idx = iBarShift(_Symbol, (ENUM_TIMEFRAMES)_Period, cutT, false);
      if(idx < 0) continue;
      double closeAtCut = iClose(_Symbol, (ENUM_TIMEFRAMES)_Period, idx);
      if(closeAtCut <= 0) continue;

      bool isUp = (closeAtCut > midLevel);
      g_fracDir[i] = isUp ? DIR_UP : DIR_DOWN;

      double node = isUp ? refLo : refHi; // نفس تقليد مسار المتجه: صاعد يلتصق بأسفل الوحدة، هابط بأعلاها
      color  col  = isUp ? InpFracColorUp : InpFracColorDown;

      // نهاية القطعة الأفقية: نقطة التفتيش التالية المفعّلة إن وصلناها، وإلا حتى الآن (طرف حي)
      datetime segEnd = nowTime + (datetime)PeriodSeconds(_Period);
      for(int j=i+1;j<4;j++)
      {
         if(!fracEnabled[j]) continue;
         datetime nextCutT = start0 + (datetime)(durSec*fractions[j]);
         if(nowTime >= nextCutT) segEnd = nextCutT;
         break;
      }

      CreateHSeg(hname, cutT, node, segEnd, col, InpFracWidth, InpFracStyle);
      CreateText(tname, cutT, node, fracLabel[i], col, 8, ANCHOR_CENTER);

      // موصل رأسي من نقطة التفتيش السابقة المفعّلة (إن وُجدت)
      for(int j=i-1;j>=0;j--)
      {
         if(!fracEnabled[j] || g_fracDir[j]==DIR_UNKNOWN) continue;
         double prevNode = (g_fracDir[j]==DIR_UP) ? refLo : refHi;
         CreateVSeg(vname, cutT, prevNode, node, col, InpFracWidth, InpFracStyle);
         break;
      }
      if(i==0) ObjectDelete(0, vname); // لا موصل قبل أول نقطة تفتيش
   }
}

//====================================================
// التنبيه الرئيسي الموحّد (Master Alert)
// يُطلق تنبيهًا عندما يتغيّر القرار فعليًا: إمّا انقلاب الاتجاه (شراء↔بيع)، أو
// ترقّي/تراجع المستوى (مؤكد ↔ مؤكد جدًا جدًا) بنفس الاتجاه — دون تكرار عند مجرد
// التذبذب داخل "لا يوجد دخول مؤكد".
//====================================================
void UpdateMasterAlert(DirState verdictDir, int verdictTier, string verdictLabel)
{
   if(!InpMasterAlertEnable) return;
   if(verdictDir == DIR_UNKNOWN) return; // لا تنبيه إلا عند تأكيد فعلي (شراء أو بيع)

   if(g_masterInitialized && (verdictDir != g_masterPrevDir || verdictTier != g_masterPrevTier))
   {
      string msg = _Symbol+" — تنبيه رئيسي موحّد: "+verdictLabel;
      if(InpMasterAlertPopup) Alert(msg);
      if(InpMasterAlertSound) PlaySound(verdictDir==DIR_UP ? InpMasterSoundUp : InpMasterSoundDown);
   }
   g_masterPrevDir     = verdictDir;
   g_masterPrevTier    = verdictTier;
   g_masterInitialized = true;
}

//====================================================
// تنبيه الدخول المبكر (Early Entry Alert) — قناة مستقلة تمامًا عن التنبيه
// الرئيسي الموحّد. الهدف صراحة عدم انتظار أي بوابة (MA/RSI) أو أي سلّم كامل —
// فور توافق أغلبية أسرع المكوّنات، يُطلق تنبيهًا فوريًا.
//====================================================
void UpdateEarlyEntryAlert(DirState earlyDir)
{
   if(!InpEarlyEntryAlertEnable) return;
   if(earlyDir == DIR_UNKNOWN) return;

   if(g_earlyInitialized && earlyDir != g_earlyPrevDir)
   {
      string msg = _Symbol+" — دخول مبكر: "+(earlyDir==DIR_UP ? "شراء ▲" : "بيع ▼");
      if(InpEarlyEntryAlertPopup) Alert(msg);
      if(InpEarlyEntryAlertSound) PlaySound(earlyDir==DIR_UP ? InpEarlyEntrySoundUp : InpEarlyEntrySoundDown);
   }
   g_earlyPrevDir     = earlyDir;
   g_earlyInitialized = true;
}

//====================================================
// لوحة المعلومات (الداشبورد) — سلّم التوافق (Confluence Ladder)
// المحرك الأساسي: 8 طبقات زمنية مرتّبة من الأصغر للأكبر —
//   الحد الأدنى: 7.5د، ربع ساعة، نصف ساعة، 45د، ساعة، 4 ساعات
//   الحد الأقصى: يومي، أسبوعي
// كل طبقة تُقرأ من بياناتها الحقيقية (فريمها الفعلي)، عدا 7.5د و45د اللتين لا
// فريم قياسي لهما في MT5 فتُحسبان من بيانات الشارت الحالي. القرار يبدأ من توافق
// MA+RSI، ثم يتطلّب توافق الحد الأدنى ليصبح "مؤكد"، ثم توافق الحد الأقصى أيضًا
// ليُرقّى إلى "مؤكد جدًا جدًا". كل طبقة تُعرض بلون مستقل: أخضر=صاعد، أحمر=هابط،
// رمادي=غير متاح — تمامًا كما طلبت.
//====================================================
void UpdateDashboard()
{
   string dashPfx = PFX+"DASH_";

   if(!InpShowDashboard)
   {
      ObjDelByPrefix(dashPfx);
      return;
   }

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(price<=0) price = iClose(_Symbol,(ENUM_TIMEFRAMES)_Period,0);

   double maVal = EMPTY_VALUE, rsiVal = EMPTY_VALUE;
   double buf[];
   if(g_maHandle  != INVALID_HANDLE && CopyBuffer(g_maHandle, 0,0,1,buf) > 0) maVal  = buf[0];
   if(g_rsiHandle != INVALID_HANDLE && CopyBuffer(g_rsiHandle,0,0,1,buf) > 0) rsiVal = buf[0];

   // ---- موقع السعر بين مستويات فيبو الوحدة الحالية (D0) ----
   UnitType utPrimary = GetUnitType();
   string zoneText = "غير متاح";
   if(utPrimary != UNIT_NONE)
   {
      double lo0=0, hi0=0; bool haveZone=false;

      if(InpD0UsesPrevFixedFib && g_unitValid[GRP_PRIMARY][1])
      {
         lo0 = g_unitLo[GRP_PRIMARY][1]; hi0 = g_unitHi[GRP_PRIMARY][1]; haveZone=true;
      }
      else
      {
         datetime now    = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,0);
         datetime start0 = FloorToUnitStartEx(_Symbol,(ENUM_TIMEFRAMES)_Period, now, utPrimary);
         haveZone = GetRange(start0, now, lo0, hi0);
      }

      if(haveZone && (hi0-lo0) > 0)
      {
         double rng0 = hi0 - lo0;
         double pMin = lo0 + rng0*FibRatios[0];
         double pMax = lo0 + rng0*FibRatios[8];

         if(price < pMin)
            zoneText = "تحت "+DoubleToString(FibPerc[0],1)+"%";
         else if(price > pMax)
            zoneText = "فوق "+DoubleToString(FibPerc[8],1)+"%";
         else
         {
            for(int k=0; k<8; k++)
            {
               double pA = lo0 + rng0*FibRatios[k];
               double pB = lo0 + rng0*FibRatios[k+1];
               double loP = MathMin(pA,pB), hiP = MathMax(pA,pB);
               if(price>=loP && price<=hiP)
               {
                  zoneText = "بين "+DoubleToString(FibPerc[k],1)+"% و "+DoubleToString(FibPerc[k+1],1)+"%";
                  break;
               }
            }
         }
      }
   }

   // ---- فلاتر MA/RSI (تُستخدم كقاعدة أساسية لسلّم التوافق ولسطرها التفصيلي) ----
   bool maBull=false, maBear=false;
   if(InpUseMAFilter && maVal!=EMPTY_VALUE) { maBull=(price>maVal); maBear=(price<maVal); }

   bool rsiBull=false, rsiBear=false;
   if(InpUseRSIFilter && rsiVal!=EMPTY_VALUE) { rsiBull=(rsiVal>InpRSIBuyLevel); rsiBear=(rsiVal<InpRSISellLevel); }

   // ================================================================
   // سلّم التوافق (Confluence Ladder) — المحرك الرئيسي للقرار
   // ================================================================
   // كل طبقة "وحدة" = مدة اسمها بالضبط. الطبقات الست التي لها فريم قياسي حقيقي
   // مطابق تمامًا لمدتها (M15/M30/H1/H4/D1/W1) تُقرأ مباشرة من شمعتها الحقيقية
   // (GetNativeBarDirection) — لا تجميع، لا حساب تقويمي وسيط، لا لبس ممكن. أما
   // 7.5 دقيقة و45 دقيقة (لا فريم قياسي لهما في MT5) فتبقيان تُبنيان بتجميع شموع
   // الشارت الحالي عبر GetLiveLevelR. الطبقات الأربع الأخيرة (1/8 حتى 3/4) تأتي
   // مباشرة من المسار الكسري داخل الوحدة (g_fracDir) — محسوبة مسبقًا في
   // DrawFractionalPath()، فلا تُعاد حسابتها هنا.
   string   confLabel[14] = {"7.5 دقيقة","ربع ساعة (15د)","نصف ساعة (30د)","45 دقيقة","ساعة واحدة (H1)","4 ساعات (H4)","4.5 ساعة","18 ساعة","يومي (D1)","أسبوعي (W1)",
                             "1/8 من الوحدة","1/4 من الوحدة","1/2 من الوحدة","3/4 من الوحدة"};
   bool     confEnabled[14]  = {InpConfShow_m7h, InpConfShow_m15, InpConfShow_m30, InpConfShow_m45,
                                InpConfShow_H1, InpConfShow_H4, InpConfShow_H4_5, InpConfShow_H18, InpConfShow_D1, InpConfShow_W1,
                                InpShowFrac18, InpShowFrac14, InpShowFrac12, InpShowFrac34};
   ENUM_TIMEFRAMES confTF[14]= {(ENUM_TIMEFRAMES)_Period, PERIOD_M15, PERIOD_M30, (ENUM_TIMEFRAMES)_Period,
                                PERIOD_H1, PERIOD_H4, (ENUM_TIMEFRAMES)_Period, (ENUM_TIMEFRAMES)_Period, PERIOD_D1, PERIOD_W1,
                                (ENUM_TIMEFRAMES)_Period, (ENUM_TIMEFRAMES)_Period, (ENUM_TIMEFRAMES)_Period, (ENUM_TIMEFRAMES)_Period};
   UnitType confUT[14]       = {UNIT_HALFQ, UNIT_NONE, UNIT_NONE, UNIT_45M, UNIT_NONE, UNIT_NONE, UNIT_4_5H, UNIT_18H, UNIT_NONE, UNIT_NONE,
                                UNIT_NONE, UNIT_NONE, UNIT_NONE, UNIT_NONE}; // UNIT_NONE = غير مستخدَم (طبقة أصلية native أو كسرية)
   bool     confNative[14]   = {false, true, true, false, true, true, false, false, true, true,
                                false, false, false, false}; // true = قراءة مباشرة من الشمعة الحقيقية، false = تجميع/كسر
   bool     confChartDep[14] = {true, false, false, true, false, false, true, true, false, false,
                                false, false, false, false};
   double   confSecLimit[14] = {450, 0, 0, 2700, 0, 0, 16200, 64800, 0, 0, 0, 0, 0, 0};
   int      confTier[14]     = {0,0,0,0,0,0,0,0, 1,1, 0,0,0,0}; // 0=الحد الأدنى، 1=الحد الأقصى
   bool     confIsFraction[14]={false,false,false,false,false,false,false,false,false,false, true,true,true,true};

   DirState confDir[14];
   bool     confAvail[14];
   double periodSecCur = (double)PeriodSeconds(_Period);
   double midv;

   for(int c=0;c<14;c++)
   {
      confAvail[c]=false; confDir[c]=DIR_UNKNOWN;
      if(!InpEnableConfluenceLadder || !confEnabled[c]) continue;

      if(confIsFraction[c])
      {
         // طبقات المسار الكسري: القيمة محسوبة مسبقًا في DrawFractionalPath()
         int fi = c - 10;
         DirState fd = g_fracDir[fi];
         if(fd==DIR_UNKNOWN) continue;
         confDir[c]=fd; confAvail[c]=true;
         continue;
      }

      DirState d;
      if(confNative[c])
      {
         // طبقة أصلية: قراءة مباشرة للشمعة الحقيقية (shift=1 المكتملة مقابل shift=0 الجارية) — بلا أي حساب "وحدة"
         d = GetNativeBarDirection(_Symbol, confTF[c], InpConfluenceRatio, midv);
      }
      else
      {
         // طبقة تركيبية (7.5د/45د): لا فريم قياسي لها — تُبنى بتجميع شموع الشارت الحالي
         if(confChartDep[c] && periodSecCur>=confSecLimit[c]) continue; // فريم الشارت أبطأ من هذه الوحدة الصغيرة
         d = GetLiveLevelR(_Symbol, confTF[c], confUT[c], InpConfluenceRatio, midv);
      }

      if(d==DIR_UNKNOWN) continue;
      confDir[c]=d; confAvail[c]=true;
   }

   // اتجاه القاعدة: تصويت حقيقي يشمل MA وRSI كصوتين إضافيين مع كل طبقات الحد
   // الأدنى معًا (السلوك الافتراضي الجديد) — أو النمط الصارم القديم (MA وRSI
   // شرط إلزامي قبل قبول أي اتجاه) إن فعّلت InpConfRequireMAandRSI يدويًا.
   // التغيير هنا مقصور على *كيفية تحديد الاتجاه*؛ صيغة نسب الاتفاق والعتبات
   // أدناه لم تُمَس إطلاقًا.
   DirState baseDir = DIR_UNKNOWN;
   if(InpEnableConfluenceLadder)
   {
      if(InpConfRequireMAandRSI && (InpUseMAFilter || InpUseRSIFilter))
      {
         // النمط الصارم (اختياري الآن، معطّل افتراضيًا): MA وRSI بوابة إلزامية كما كانت
         bool mUp = (!InpUseMAFilter) || maBull;
         bool mDn = (!InpUseMAFilter) || maBear;
         bool rUp = (!InpUseRSIFilter) || rsiBull;
         bool rDn = (!InpUseRSIFilter) || rsiBear;
         if(mUp && rUp) baseDir = DIR_UP;
         else if(mDn && rDn) baseDir = DIR_DOWN;
      }
      else
      {
         // الافتراضي الجديد: MA وRSI صوتان في التصويت، وليسا بوابة إلزامية
         int u=0,d=0;
         if(InpUseMAFilter  && maVal!=EMPTY_VALUE)  { if(maBull)  u++; else if(maBear)  d++; }
         if(InpUseRSIFilter && rsiVal!=EMPTY_VALUE) { if(rsiBull) u++; else if(rsiBear) d++; }
         for(int c=0;c<14;c++) if(confTier[c]==0 && confAvail[c]) { if(confDir[c]==DIR_UP) u++; else d++; }
         if(u>d) baseDir=DIR_UP; else if(d>u) baseDir=DIR_DOWN;
      }
   }

   // نسب الاتفاق داخل كل حد (أدنى/أقصى) مقابل اتجاه القاعدة
   int minAvail=0, minAgree=0, maxAvail=0, maxAgree=0;
   for(int c=0;c<14;c++)
   {
      if(!confAvail[c]) continue;
      bool agrees = (baseDir!=DIR_UNKNOWN && confDir[c]==baseDir);
      if(confTier[c]==0) { minAvail++; if(agrees) minAgree++; }
      else                { maxAvail++; if(agrees) maxAgree++; }
   }
   double minRatio = (minAvail>0) ? (double)minAgree/(double)minAvail : 0.0;
   double maxRatio = (maxAvail>0) ? (double)maxAgree/(double)maxAvail : 0.0;

   bool minTierOK = InpEnableConfluenceLadder && (baseDir!=DIR_UNKNOWN)
                    && (minAvail>=InpConfMinTierMinCount) && (minRatio>=InpConfMinTierThreshold);
   bool maxTierOK = minTierOK && (maxAvail>=InpConfMaxTierMinCount) && (maxRatio>=InpConfMaxTierThreshold);

   // ---- القرار النهائي ----
   string verdictText; color verdictColor; DirState verdictDir=DIR_UNKNOWN; int verdictTier=0;
   if(maxTierOK)
   {
      verdictText  = (baseDir==DIR_UP) ? "شراء مؤكد جدًا جدًا ✓✓" : "بيع مؤكد جدًا جدًا ✓✓";
      verdictColor = (baseDir==DIR_UP) ? InpDashColorUp : InpDashColorDown;
      verdictDir = baseDir; verdictTier = 2;
   }
   else if(minTierOK)
   {
      verdictText  = (baseDir==DIR_UP) ? "شراء مؤكد ✓" : "بيع مؤكد ✓";
      verdictColor = (baseDir==DIR_UP) ? InpDashColorUp : InpDashColorDown;
      verdictDir = baseDir; verdictTier = 1;
   }
   else
   {
      verdictText  = "لا يوجد دخول مؤكد";
      verdictColor = InpDashColorNeutral;
      verdictTier  = 0;
   }

   UpdateMasterAlert(verdictDir, verdictTier, verdictText);

   // ================================================================
   // منطق إشارات مسار الوحدة (23.6% / 50% / 76.4%) — تأكيدات إضافية للداشبورد
   // يعتمد على مكان إغلاق الوحدة الحالية (D0) بالنسبة لمستويات فيبو الوحدة
   // السابقة المكتملة (نفس مرجع مسار المتجه الأساسي في المؤشر) — وليس على
   // اتجاه "وحدة" مُجمَّعة مستقلة، بل على السعر الفعلي وحده وارتداده/اختراقه.
   // ================================================================
   string vpSigLine1="", vpSigLine2="", vpSigLine3="", vpSigLine4="", vpSigLine5="";
   color  vpSigColor1=InpDashColorNeutral, vpSigColor2=InpDashColorNeutral, vpSigColor3=InpDashColorNeutral, vpSigColor4=InpDashColorNeutral, vpSigColor5=InpDashColorNeutral;
   DirState earlyVPDir = DIR_UNKNOWN; // الإشارة المبكرة: إغلاق فوق/تحت أقرب مستوى مفعّل فقط (وليس الكل) — تُستخدم في مخرج الدخول المبكر أدناه
   bool   haveVpSignal=false;

   if(InpShowVPSignalBlock && InpShowVectorPath && utPrimary!=UNIT_NONE && g_unitValid[GRP_PRIMARY][1])
   {
      double refLo = g_unitLo[GRP_PRIMARY][1];
      double refHi = g_unitHi[GRP_PRIMARY][1];
      double refRng = refHi - refLo;

      if(refRng > 0)
      {
         double lvl236 = refLo + refRng*0.236;
         double lvl500 = refLo + refRng*0.500;
         double lvl764 = refLo + refRng*0.764;

         // نطاق الوحدة الحالية الفعلي منذ بدايتها وحتى الآن (القمة/القاع الحقيقيان، وليس فيبو مرجعي)
         datetime nowT   = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,0);
         datetime start0 = FloorToUnitStartEx(_Symbol,(ENUM_TIMEFRAMES)_Period, nowT, utPrimary);
         double curLo=0, curHi=0;
         bool haveCur = GetRange(start0, nowT, curLo, curHi);
         if(!haveCur) { curLo = price; curHi = price; }

         double curClose = price;
         haveVpSignal = true;

         // ---- 1) تأكيد صارم: إغلاق فوق/تحت كل المستويات المفعّلة معًا (كان يُسمى "الإشارة الأساسية") ----
         bool en236 = InpShowVP_236, en500 = InpShowVP_500, en764 = InpShowVP_764;
         bool anyLevelEnabled = en236 || en500 || en764;

         bool buyStrict  = anyLevelEnabled;
         bool sellStrict = anyLevelEnabled;
         if(en236) { buyStrict = buyStrict && (curClose > lvl236); sellStrict = sellStrict && (curClose < lvl236); }
         if(en500) { buyStrict = buyStrict && (curClose > lvl500); sellStrict = sellStrict && (curClose < lvl500); }
         if(en764) { buyStrict = buyStrict && (curClose > lvl764); sellStrict = sellStrict && (curClose < lvl764); }

         DirState basicDir = DIR_UNKNOWN; // يبقى بنفس المنطق القديم — يُستخدم في تأكيد توافق 7.5 دقيقة (بند 4) دون أي تغيير
         if(anyLevelEnabled)
         {
            if(buyStrict)       basicDir = DIR_UP;
            else if(sellStrict) basicDir = DIR_DOWN;
         }

         if(basicDir==DIR_UP)        { vpSigLine1="تأكيد صارم: شراء ▲ (إغلاق فوق كل المستويات المفعّلة)"; vpSigColor1=InpDashColorUp; }
         else if(basicDir==DIR_DOWN) { vpSigLine1="تأكيد صارم: بيع ▼ (إغلاق تحت كل المستويات المفعّلة)"; vpSigColor1=InpDashColorDown; }
         else                        { vpSigLine1="تأكيد صارم: لا إشارة"; vpSigColor1=InpDashColorNeutral; }

         // ---- 1ب) الإشارة المبكرة: إغلاق فوق/تحت أقرب مستوى مفعّل فقط (الأسرع استجابة، تغذّي الدخول المبكر أدناه) ----
         double earlyLevel=0; bool haveEarlyLevel=false;
         if(en236)      { earlyLevel=lvl236; haveEarlyLevel=true; }
         else if(en500) { earlyLevel=lvl500; haveEarlyLevel=true; }
         else if(en764) { earlyLevel=lvl764; haveEarlyLevel=true; }

         if(haveEarlyLevel)
         {
            if(curClose > earlyLevel)      earlyVPDir = DIR_UP;
            else if(curClose < earlyLevel) earlyVPDir = DIR_DOWN;
         }

         // ---- 2) حالات الاختراق والارتداد (تتطلب حركة سعر فعلية، وليس إغلاقًا فقط) ----
         bool buyBreakout76    = (curLo  <  lvl764) && (curClose > lvl764); // اختراق صاعد عبر 76.4
         bool buyRebound23to50 = (curLo  <= lvl236) && (curClose > lvl500); // ارتداد من 23.6 وإغلاق فوق 50
         bool sellBreakdown76  = (curHi  >  lvl764) && (curClose < lvl764); // اختراق هابط عبر 76.4
         bool sellRebound50to23= (curHi  >= lvl500) && (curClose < lvl236); // ارتداد من 50 وإغلاق تحت 23.6

         if(buyBreakout76)         { vpSigLine2="اختراق صاعد: كسر 76.4% وإغلاق فوقه";      vpSigColor2=InpDashColorUp; }
         else if(buyRebound23to50) { vpSigLine2="ارتداد من 23.6% وإغلاق فوق 50%";           vpSigColor2=InpDashColorUp; }
         else if(sellBreakdown76)  { vpSigLine2="اختراق هابط: كسر 76.4% وإغلاق تحته";        vpSigColor2=InpDashColorDown; }
         else if(sellRebound50to23){ vpSigLine2="ارتداد من 50% وإغلاق تحت 23.6%";           vpSigColor2=InpDashColorDown; }

         // ---- 3) الارتدادات القوية (تأكيد إضافي أقوى من الحالات العادية) ----
         bool strongBuyRebound  = (curLo <= lvl500) && (curClose > lvl764); // ارتداد من 50 وإغلاق فوق 76.4
         bool strongSellRebound = sellRebound50to23;                        // نفس شرط ارتداد البيع، يُبرز هنا كتأكيد أقوى

         if(strongBuyRebound)       { vpSigLine3="✓ تأكيد إضافي قوي للشراء — ارتداد من 50% وإغلاق فوق 76.4%"; vpSigColor3=InpDashColorUp; }
         else if(strongSellRebound) { vpSigLine3="✓ تأكيد إضافي قوي للبيع — ارتداد من 50% وإغلاق تحت 23.6%";  vpSigColor3=InpDashColorDown; }

         // ---- 5) تأكيد إضافي عبر الامتدادات الخارجية (123.6% / -23.6%) وكشف الاختراق الكاذب ----
         // 123.6% و-23.6% موجودتان أصلًا ضمن مصفوفة الفيبو الأساسية للمؤشر (FibRatios[8] و FibRatios[0])
         double lvlExtUp = refLo + refRng*1.236;    // 123.6% — الامتداد الخارجي العلوي
         double lvlExtDn = refLo + refRng*(-0.236); // -23.6% — الامتداد الخارجي السفلي

         bool buyExtraStrong    = strongBuyRebound  && (curClose > lvlExtUp); // انطلاق فوق 123.6 يؤكد الارتداد القوي أكثر
         bool sellExtraStrong   = strongSellRebound && (curClose < lvlExtDn); // انطلاق تحت -23.6 يؤكد الارتداد القوي أكثر
         bool falseBreakoutBuy  = (curHi >= lvlExtUp) && (curClose < refHi);  // لمس 123.6 ثم ارتد وكسر 100% مجددًا → اختراق كاذب صاعد
         bool falseBreakoutSell = (curLo <= lvlExtDn) && (curClose > refLo);  // لمس -23.6 ثم ارتد وكسر 0% مجددًا → اختراق كاذب هابط

         if(falseBreakoutBuy)
            { vpSigLine5="⚠ اختراق كاذب صاعد — ارتداد من 123.6% وكسر 100% مجددًا، الهدف المحتمل: 23.6%"; vpSigColor5=InpDashColorDown; }
         else if(falseBreakoutSell)
            { vpSigLine5="⚠ اختراق كاذب هابط — ارتداد من -23.6% وكسر 0% مجددًا، الهدف المحتمل: 76.4%";   vpSigColor5=InpDashColorUp; }
         else if(buyExtraStrong)
            { vpSigLine5="✓✓ تأكيد إضافي أقوى للشراء — انطلاق فوق 123.6%"; vpSigColor5=InpDashColorUp; }
         else if(sellExtraStrong)
            { vpSigLine5="✓✓ تأكيد إضافي أقوى للبيع — انطلاق تحت -23.6%"; vpSigColor5=InpDashColorDown; }

         // ---- 4) توافق الإشارة الأساسية مع طبقة 7.5 دقيقة في سلّم التوافق ----
         if(basicDir!=DIR_UNKNOWN && confAvail[0] && confDir[0]==basicDir)
         {
            vpSigLine4 = (basicDir==DIR_UP)
               ? "✓✓ توافق قوي جدًا — إشارة مؤكدة من الوحدتين مع 7.5 دقيقة (شراء)"
               : "✓✓ توافق قوي جدًا — إشارة مؤكدة من الوحدتين مع 7.5 دقيقة (بيع)";
            vpSigColor4 = (basicDir==DIR_UP) ? InpDashColorUp : InpDashColorDown;
         }
      }
   }

   // ================================================================
   // الدخول المبكر (Early Entry) — مخرج مستقل تمامًا عن القرار الموحّد وسلّم
   // التوافق، طبقًا للمراجعة: لا بوابة MA/RSI، ولا انتظار للطبقات البطيئة.
   // تصويت أغلبية بسيط بين 5 مكوّنات فقط، أسرعها في كامل المؤشر:
   //   confDir[0]=7.5 دقيقة، confDir[1]=ربع ساعة، confDir[10]=1/8 من الوحدة،
   //   confDir[11]=1/4 من الوحدة، earlyVPDir=أقرب مستوى مفعّل من مسار الوحدة.
   // ================================================================
   string   earlyLine = "";
   color    earlyColor = InpDashColorNeutral;
   DirState earlyEntryDir = DIR_UNKNOWN;

   if(InpShowEarlyEntry)
   {
      int eu=0, ed=0, eAvail=0;
      DirState earlyVotes[5] = { confAvail[0]?confDir[0]:DIR_UNKNOWN, confAvail[1]?confDir[1]:DIR_UNKNOWN,
                                  confAvail[10]?confDir[10]:DIR_UNKNOWN, confAvail[11]?confDir[11]:DIR_UNKNOWN,
                                  earlyVPDir };
      for(int v=0; v<5; v++)
      {
         if(earlyVotes[v]==DIR_UNKNOWN) continue;
         eAvail++;
         if(earlyVotes[v]==DIR_UP) eu++; else ed++;
      }

      if(eAvail >= InpEarlyEntryMinCount)
      {
         if(eu>ed)      earlyEntryDir = DIR_UP;
         else if(ed>eu) earlyEntryDir = DIR_DOWN;
      }

      if(earlyEntryDir==DIR_UP)
         { earlyLine="⚡ دخول مبكر: شراء ▲  ("+IntegerToString(eu)+"/"+IntegerToString(eAvail)+")"; earlyColor=InpDashColorUp; }
      else if(earlyEntryDir==DIR_DOWN)
         { earlyLine="⚡ دخول مبكر: بيع ▼  ("+IntegerToString(ed)+"/"+IntegerToString(eAvail)+")"; earlyColor=InpDashColorDown; }
      else
         { earlyLine="⚡ دخول مبكر: لا يوجد بعد  ("+IntegerToString(eAvail)+"/5 متاح)"; earlyColor=InpDashColorNeutral; }

      UpdateEarlyEntryAlert(earlyEntryDir);
   }

   // ---- سطور تفصيلية إضافية (اختيارية — Overlay/MTF/الوحدات الإضافية/مسار فريمك) ----
   string detailLines[16]; color detailColors[16];
   int dn = 0;

   if(InpShowVectorPath && utPrimary!=UNIT_NONE)
   {
      double ratios[4] = {0.236, 0.500, 0.618, 0.764};
      bool   ren[4]    = {InpShowVP_236, InpShowVP_500, InpShowVP_618, InpShowVP_764};
      string rtag[4]   = {"23.6%","50%","61.8%","76.4%"};
      string vpLine = "مسار المتجه (فريمك): ";
      bool any=false;

      for(int ri=0; ri<4; ri++)
      {
         if(!ren[ri]) continue;
         DirState d = GetLiveLevelR(_Symbol, (ENUM_TIMEFRAMES)_Period, utPrimary, ratios[ri], midv);
         if(d==DIR_UNKNOWN) continue;
         vpLine += rtag[ri] + (d==DIR_UP?"▲":"▼") + " ";
         any=true;
      }
      if(any) { detailLines[dn]=vpLine; detailColors[dn]=InpDashColorNeutral; dn++; }
   }

   if(InpShowLevelsOverlay)
   {
      string ovTags[LVL_COUNT] = {"M5","M15","H1","H4","D1"};
      bool   ovShow[LVL_COUNT] = {InpShow_M5, InpShow_M15, InpShow_H1, InpShow_H4, InpShow_D1};
      string ovLine = "Overlay 50%: ";
      bool any=false;

      for(int k=0;k<LVL_COUNT;k++)
      {
         if(!ovShow[k]) continue;
         DirState d = g_prevState[k];
         if(d==DIR_UNKNOWN) continue;
         ovLine += ovTags[k] + (d==DIR_UP?"▲":"▼") + " ";
         any=true;
      }
      if(any) { detailLines[dn]=ovLine; detailColors[dn]=InpDashColorNeutral; dn++; }
   }

   if(InpEnableMTFVectorPath)
   {
      string mtfTags[MTF_COUNT] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN1"};
      bool   mtfShow[MTF_COUNT] = {InpMTFShow_M1, InpMTFShow_M5, InpMTFShow_M15, InpMTFShow_M30,
                                    InpMTFShow_H1, InpMTFShow_H4, InpMTFShow_D1, InpMTFShow_W1, InpMTFShow_MN1};
      string mtfLine = "MTF VectorPath: ";
      bool any=false;

      for(int k=0;k<MTF_COUNT;k++)
      {
         if(!mtfShow[k]) continue;
         DirState d = g_mtfCurrentDir[k];
         if(d==DIR_UNKNOWN) continue;
         mtfLine += mtfTags[k] + (d==DIR_UP?"▲":"▼") + " ";
         any=true;
      }
      if(any) { detailLines[dn]=mtfLine; detailColors[dn]=InpDashColorNeutral; dn++; }
   }

   if(g_lastSignalText != "")
   {
      detailLines[dn] = "آخر إشارة ارتداد: " + g_lastSignalText;
      detailColors[dn] = g_lastSignalColor;
      dn++;
   }

   // ---- بناء أسطر اللوحة النهائية (كل سطر بحجم خط ولون مستقلين) ----
   string lines[DASH_MAX_LINES]; color lineColors[DASH_MAX_LINES]; int lineSizes[DASH_MAX_LINES];
   int n=0;

   lines[n]="AIBV2F — سلّم التوافق"; lineColors[n]=clrWhite;                       lineSizes[n]=InpDashFontSize; n++;
   lines[n]="السعر: "+DoubleToString(price,_Digits); lineColors[n]=InpDashColorNeutral; lineSizes[n]=InpDashFontSize; n++;
   lines[n]="الموقع: "+zoneText; lineColors[n]=InpDashColorNeutral;                lineSizes[n]=InpDashFontSize; n++;

   if(InpShowEarlyEntry && earlyLine!="")
   {
      lines[n]=earlyLine; lineColors[n]=earlyColor; lineSizes[n]=InpDashVerdictFontSize; n++;
   }

   string maDirTxt = maBull?"▲":(maBear?"▼":"—");
   color  maCol    = maBull?InpDashColorUp:(maBear?InpDashColorDown:InpDashColorNeutral);
   lines[n]="MA"+IntegerToString(InpMAPeriod)+": "+(maVal!=EMPTY_VALUE?DoubleToString(maVal,_Digits):"...")+"  "+maDirTxt;
   lineColors[n]=maCol; lineSizes[n]=InpDashFontSize; n++;

   string rsiDirTxt = rsiBull?"▲":(rsiBear?"▼":"—");
   color  rsiCol    = rsiBull?InpDashColorUp:(rsiBear?InpDashColorDown:InpDashColorNeutral);
   lines[n]="RSI"+IntegerToString(InpRSIPeriod)+": "+(rsiVal!=EMPTY_VALUE?DoubleToString(rsiVal,1):"...")+"  "+rsiDirTxt;
   lineColors[n]=rsiCol; lineSizes[n]=InpDashFontSize; n++;

   if(InpEnableConfluenceLadder)
   {
      lines[n]="— الحد الأدنى للتوافق —"; lineColors[n]=clrSilver; lineSizes[n]=InpDashFontSize; n++;

      if(InpConfShowPerLayerLines)
      {
         for(int c=0;c<14;c++)
         {
            if(confTier[c]!=0 || !confEnabled[c]) continue;
            string st = confAvail[c] ? (confDir[c]==DIR_UP?"صاعد ▲":"هابط ▼") : "غير متاح";
            color  cc = confAvail[c] ? (confDir[c]==DIR_UP?InpDashColorUp:InpDashColorDown) : InpDashColorNeutral;
            lines[n]="  "+confLabel[c]+": "+st; lineColors[n]=cc; lineSizes[n]=InpDashFontSize; n++;
         }
      }

      lines[n]="توافق الحد الأدنى: "+DoubleToString(minRatio*100.0,0)+"%  ("+IntegerToString(minAgree)+"/"+IntegerToString(minAvail)+")";
      lineColors[n] = minTierOK ? (baseDir==DIR_UP?InpDashColorUp:InpDashColorDown) : InpDashColorNeutral;
      lineSizes[n]=InpDashFontSize; n++;

      lines[n]="— الحد الأقصى للتوافق —"; lineColors[n]=clrSilver; lineSizes[n]=InpDashFontSize; n++;

      if(InpConfShowPerLayerLines)
      {
         for(int c=0;c<14;c++)
         {
            if(confTier[c]!=1 || !confEnabled[c]) continue;
            string st = confAvail[c] ? (confDir[c]==DIR_UP?"صاعد ▲":"هابط ▼") : "غير متاح";
            color  cc = confAvail[c] ? (confDir[c]==DIR_UP?InpDashColorUp:InpDashColorDown) : InpDashColorNeutral;
            lines[n]="  "+confLabel[c]+": "+st; lineColors[n]=cc; lineSizes[n]=InpDashFontSize; n++;
         }
      }

      lines[n]="توافق الحد الأقصى: "+DoubleToString(maxRatio*100.0,0)+"%  ("+IntegerToString(maxAgree)+"/"+IntegerToString(maxAvail)+")";
      lineColors[n] = maxTierOK ? (baseDir==DIR_UP?InpDashColorUp:InpDashColorDown) : InpDashColorNeutral;
      lineSizes[n]=InpDashFontSize; n++;
   }

   if(haveVpSignal && n<DASH_MAX_LINES-6)
   {
      lines[n]="— إشارات مسار الوحدة —"; lineColors[n]=clrSilver; lineSizes[n]=InpDashFontSize; n++;

      lines[n]=vpSigLine1; lineColors[n]=vpSigColor1; lineSizes[n]=InpDashFontSize; n++;

      if(vpSigLine2 != "") { lines[n]=vpSigLine2; lineColors[n]=vpSigColor2; lineSizes[n]=InpDashFontSize; n++; }
      if(vpSigLine3 != "") { lines[n]=vpSigLine3; lineColors[n]=vpSigColor3; lineSizes[n]=InpDashFontSize; n++; }
      if(vpSigLine4 != "") { lines[n]=vpSigLine4; lineColors[n]=vpSigColor4; lineSizes[n]=InpDashFontSize; n++; }
      if(vpSigLine5 != "") { lines[n]=vpSigLine5; lineColors[n]=vpSigColor5; lineSizes[n]=InpDashFontSize; n++; }
   }

   if(InpDashShowDetails)
   {
      for(int i=0; i<dn && n<DASH_MAX_LINES-1; i++)
      {
         lines[n]=detailLines[i]; lineColors[n]=detailColors[i]; lineSizes[n]=InpDashFontSize; n++;
      }
   }

   lines[n]=verdictText; lineColors[n]=verdictColor; lineSizes[n]=InpDashVerdictFontSize; n++;

   // ---- الرسم: الموضع والزاوية قابلان للتحكم الكامل عبر InpDashCorner/X/Y، وكذلك
   //      المسافة بين الأسطر عبر InpDashLineSpacing، وبلا خلفية افتراضيًا ----
   int corner = (InpDashCorner==DASH_TOP_LEFT)     ? CORNER_LEFT_UPPER  :
                (InpDashCorner==DASH_TOP_RIGHT)    ? CORNER_RIGHT_UPPER :
                (InpDashCorner==DASH_BOTTOM_LEFT)  ? CORNER_LEFT_LOWER  : CORNER_RIGHT_LOWER;

   string bgName = dashPfx+"BG";
   if(InpDashShowBackground)
   {
      int totalH = 10;
      for(int i=0;i<n;i++) totalH += lineSizes[i] + 8 + InpDashLineSpacing;

      if(!ObjExists(bgName)) ObjectCreate(0,bgName,OBJ_RECTANGLE_LABEL,0,0,0);
      ObjectSetInteger(0,bgName,OBJPROP_CORNER, corner);
      ObjectSetInteger(0,bgName,OBJPROP_XDISTANCE, InpDashX);
      ObjectSetInteger(0,bgName,OBJPROP_YDISTANCE, InpDashY);
      ObjectSetInteger(0,bgName,OBJPROP_XSIZE, 300);
      ObjectSetInteger(0,bgName,OBJPROP_YSIZE, totalH);
      ObjectSetInteger(0,bgName,OBJPROP_BGCOLOR, ApplyOpacity(InpDashBgColor, InpDashBgOpacity, clrBlack));
      ObjectSetInteger(0,bgName,OBJPROP_BORDER_TYPE, BORDER_FLAT);
      ObjectSetInteger(0,bgName,OBJPROP_COLOR, clrGray);
      ObjectSetInteger(0,bgName,OBJPROP_BACK, false);
      ObjectSetInteger(0,bgName,OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0,bgName,OBJPROP_HIDDEN, true);
   }
   else if(ObjExists(bgName))
   {
      ObjectDelete(0,bgName); // لا خلفية افتراضيًا — إزالة أي خلفية قديمة متبقية من إعداد سابق
   }

   int yOffset = InpDashY + 8;
   for(int i=0;i<n;i++)
   {
      string nm = dashPfx+"L"+IntegerToString(i);
      if(!ObjExists(nm)) ObjectCreate(0,nm,OBJ_LABEL,0,0,0);
      ObjectSetInteger(0,nm,OBJPROP_CORNER, corner);
      ObjectSetInteger(0,nm,OBJPROP_XDISTANCE, InpDashX+10);
      ObjectSetInteger(0,nm,OBJPROP_YDISTANCE, yOffset);
      ObjectSetString (0,nm,OBJPROP_TEXT, lines[i]);
      ObjectSetString (0,nm,OBJPROP_FONT, InpDashFontName);
      ObjectSetInteger(0,nm,OBJPROP_FONTSIZE, lineSizes[i]);
      ObjectSetInteger(0,nm,OBJPROP_COLOR, lineColors[i]);
      ObjectSetInteger(0,nm,OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0,nm,OBJPROP_HIDDEN, true);
      ObjectSetInteger(0,nm,OBJPROP_BACK, false);

      yOffset += lineSizes[i] + 8 + InpDashLineSpacing;
   }

   // حذف أي أسطر زائدة من رسمة سابقة كانت أطول
   for(int i=n;i<DASH_MAX_LINES;i++)
   {
      string nm = dashPfx+"L"+IntegerToString(i);
      if(ObjExists(nm)) ObjectDelete(0,nm);
   }
}

//====================================================
// دليل الألوان — لوحة مستقلة (افتراضيًا في الجهة المقابلة للداشبورد) تشرح معنى
// كل لون فعلي مُستخدَم على الشارت، بحيث لا يحتاج المستخدم لحفظ الألوان عن ظهر
// قلب. القيم مأخوذة مباشرة من إعدادات الإدخال الفعلية، فتبقى صحيحة دائمًا حتى
// لو غيّر المستخدم الألوان الافتراضية.
//====================================================
#define LEGEND_MAX_LINES 20
void UpdateColorLegend()
{
   string legPfx = PFX+"LEGEND_";

   if(!InpShowColorLegend)
   {
      ObjDelByPrefix(legPfx);
      return;
   }

   string legLines[LEGEND_MAX_LINES];
   color  legColors[LEGEND_MAX_LINES];
   int ln = 0;

   legLines[ln]="دليل ألوان الشارت";                        legColors[ln]=clrWhite;            ln++;
   legLines[ln]="■ مسار المتجه (فريمك): اتجاه صاعد";         legColors[ln]=InpVPColorUp_500;    ln++;
   legLines[ln]="■ مسار المتجه (فريمك): اتجاه هابط";         legColors[ln]=InpVPColorDown_500;  ln++;
   legLines[ln]="■ نقطة كسرية (1/8..3/4 من الوحدة): صاعدة";  legColors[ln]=InpFracColorUp;      ln++;
   legLines[ln]="■ نقطة كسرية (1/8..3/4 من الوحدة): هابطة";  legColors[ln]=InpFracColorDown;    ln++;
   legLines[ln]="■ مسار متعدد الفريمات (M1..MN1): صاعد";     legColors[ln]=InpMTFColorUp;       ln++;
   legLines[ln]="■ مسار متعدد الفريمات (M1..MN1): هابط";     legColors[ln]=InpMTFColorDown;     ln++;
   legLines[ln]="▲ سهم NQ: إشارة شراء (ارتداد مؤكد)";        legColors[ln]=clrDodgerBlue;       ln++;
   legLines[ln]="▼ سهم NQ: إشارة بيع (ارتداد مؤكد)";         legColors[ln]=clrRed;              ln++;
   legLines[ln]="■ القرار/الإشارة على اللوحة: شراء";          legColors[ln]=InpDashColorUp;      ln++;
   legLines[ln]="■ القرار/الإشارة على اللوحة: بيع";           legColors[ln]=InpDashColorDown;    ln++;
   legLines[ln]="■ محايد / غير متاح";                         legColors[ln]=InpDashColorNeutral; ln++;

   int corner = (InpLegendCorner==DASH_TOP_LEFT)     ? CORNER_LEFT_UPPER  :
                (InpLegendCorner==DASH_TOP_RIGHT)    ? CORNER_RIGHT_UPPER :
                (InpLegendCorner==DASH_BOTTOM_LEFT)  ? CORNER_LEFT_LOWER  : CORNER_RIGHT_LOWER;

   string bgName = legPfx+"BG";
   if(InpLegendShowBackground)
   {
      int totalH = 10 + ln*(InpLegendFontSize+8);
      if(!ObjExists(bgName)) ObjectCreate(0,bgName,OBJ_RECTANGLE_LABEL,0,0,0);
      ObjectSetInteger(0,bgName,OBJPROP_CORNER, corner);
      ObjectSetInteger(0,bgName,OBJPROP_XDISTANCE, InpLegendX);
      ObjectSetInteger(0,bgName,OBJPROP_YDISTANCE, InpLegendY);
      ObjectSetInteger(0,bgName,OBJPROP_XSIZE, 300);
      ObjectSetInteger(0,bgName,OBJPROP_YSIZE, totalH);
      ObjectSetInteger(0,bgName,OBJPROP_BGCOLOR, ApplyOpacity(InpDashBgColor, InpDashBgOpacity, clrBlack));
      ObjectSetInteger(0,bgName,OBJPROP_BORDER_TYPE, BORDER_FLAT);
      ObjectSetInteger(0,bgName,OBJPROP_COLOR, clrGray);
      ObjectSetInteger(0,bgName,OBJPROP_BACK, false);
      ObjectSetInteger(0,bgName,OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0,bgName,OBJPROP_HIDDEN, true);
   }
   else if(ObjExists(bgName))
   {
      ObjectDelete(0,bgName);
   }

   int yOff = InpLegendY + 8;
   for(int i=0;i<ln;i++)
   {
      string nm = legPfx+"L"+IntegerToString(i);
      if(!ObjExists(nm)) ObjectCreate(0,nm,OBJ_LABEL,0,0,0);
      ObjectSetInteger(0,nm,OBJPROP_CORNER, corner);
      ObjectSetInteger(0,nm,OBJPROP_XDISTANCE, InpLegendX+10);
      ObjectSetInteger(0,nm,OBJPROP_YDISTANCE, yOff);
      ObjectSetString (0,nm,OBJPROP_TEXT, legLines[i]);
      ObjectSetString (0,nm,OBJPROP_FONT, InpDashFontName);
      ObjectSetInteger(0,nm,OBJPROP_FONTSIZE, InpLegendFontSize);
      ObjectSetInteger(0,nm,OBJPROP_COLOR, legColors[i]);
      ObjectSetInteger(0,nm,OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0,nm,OBJPROP_HIDDEN, true);
      ObjectSetInteger(0,nm,OBJPROP_BACK, false);
      yOff += InpLegendFontSize + 8;
   }

   for(int i=ln;i<LEGEND_MAX_LINES;i++)
   {
      string nm = legPfx+"L"+IntegerToString(i);
      if(ObjExists(nm)) ObjectDelete(0,nm);
   }
}

//====================================================
// أحداث MT5 القياسية
//====================================================
int OnInit()
{
   g_forceRedraw = true;
   g_lastRedrawBarTime = 0;

   IndicatorSetString(INDICATOR_SHORTNAME,"AIBV2F");

   // تحميل تاريخ كل الفريمات القياسية (M1..MN1) مبكرًا لهذا الرمز — يغذي كلاً من
   // طبقة overlay الخاصة بمستوى 50% وطبقة مسار المتجه متعددة الفريمات المستقلة
   // عن فريم الشارت، بحيث لا تنتظر أول تحديث سعر لتحميل بياناتها.
   ENUM_TIMEFRAMES prefetchTFs[9] = { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30,
                                      PERIOD_H1, PERIOD_H4, PERIOD_D1,  PERIOD_W1, PERIOD_MN1 };
   for(int pf=0; pf<9; pf++)
   {
      MqlRates tmpRates[];
      CopyRates(_Symbol, prefetchTFs[pf], 0, 500, tmpRates);
   }

   // إنشاء مقابض الموفينج افريدج و RSI لفلاتر التأكيد وللوحة المعلومات
   g_maHandle  = iMA(_Symbol, PERIOD_CURRENT, InpMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   g_rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, InpRSIPeriod, PRICE_CLOSE);

   if(g_maHandle == INVALID_HANDLE)
      Print("AIBV2F: تعذّر إنشاء مؤشر الموفينج افريدج — سيتم تعطيل فلتره تلقائيًا حتى إعادة التحميل.");
   if(g_rsiHandle == INVALID_HANDLE)
      Print("AIBV2F: تعذّر إنشاء مؤشر RSI — سيتم تعطيل فلتره تلقائيًا حتى إعادة التحميل.");

   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   if(g_maHandle  != INVALID_HANDLE) IndicatorRelease(g_maHandle);
   if(g_rsiHandle != INVALID_HANDLE) IndicatorRelease(g_rsiHandle);

   ObjDelByPrefix(PFX);
   Comment("");
}

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[])
{
   g_rates_total = rates_total;

   datetime t0 = iTime(_Symbol,(ENUM_TIMEFRAMES)_Period,0);

   if(g_forceRedraw || g_lastRedrawBarTime != t0)
   {
      g_lastRedrawBarTime = t0;
      g_forceRedraw = false;
      DrawAll();
   }

   UpdateM5M15Overlay();
   UpdateMTFVectorPaths();
   DrawFractionalPath();
   UpdateDashboard();
   UpdateColorLegend();

   return(rates_total);
}