//+------------------------------------------------------------------+
//|                                     Trident_Swing_Projector.mq4  |
//|                                  Copyright 2026, MarkitTick / AI |
//|                                      https://www.mql4.com        |
//+------------------------------------------------------------------+
#property copyright   "MarkitTick (Converted to MQL4)"
#property link        "https://creativecommons.org/licenses/by-nc-sa/4.0/"
#property version     "1.01"
#property description "Trident Swing Projector [MarkitTick] translated to MQL4"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//--- ENUMS
enum ENUM_SWING_UNIT
{
   UNIT_POINTS  = 0, // Points
   UNIT_PERCENT = 1  // Percent
};

enum ENUM_SMOOTH_METHOD
{
   SMOOTH_NONE = 0, // None
   SMOOTH_SMA  = 1, // SMA
   SMOOTH_WMA  = 3, // WMA
   SMOOTH_VWMA = 5  // VWMA
};

enum ENUM_STOP_BUF_MODE
{
   BUF_NONE         = 0, // None
   BUF_TICKS        = 1, // Ticks / Points
   BUF_ATR_FRACTION = 2  // ATR Fraction
};

enum ENUM_DASH_POS
{
   DASH_TOP_RIGHT    = 0, // Top Right
   DASH_TOP_LEFT     = 1, // Top Left
   DASH_BOTTOM_RIGHT = 2, // Bottom Right
   DASH_BOTTOM_LEFT  = 3  // Bottom Left
};

enum ENUM_HTF_PERIOD
{
   HTF_CURRENT = 0,     // Current
   HTF_M1      = 1,     // M1
   HTF_M5      = 5,     // M5
   HTF_M15     = 15,    // M15
   HTF_M30     = 30,    // M30
   HTF_H1      = 60,    // H1
   HTF_H4      = 240,   // H4
   HTF_D1      = 1440,  // D1
   HTF_W1      = 10080, // W1
   HTF_MN1     = 43200  // MN1
};

//--- INPUT PARAMETERS
// Core
input string            InpGroupCore      = "=== Core Settings ==="; // --- Core Settings ---
input int               InpPivotLeft      = 5;                     // Pivot Left (1..50)
input int               InpPivotRight     = 5;                     // Pivot Right (1..50)
input ENUM_SWING_UNIT   InpSwingUnit      = UNIT_POINTS;           // Swing Unit

// Filters
input string            InpGroupFilters   = "=== Filters ===";     // --- Filters ---
input bool              InpUseTrend       = true;                  // Trend Filter
input double            InpMinPullback    = 23.6;                  // Min Pullback %
input double            InpMaxPullback    = 78.6;                  // Max Pullback %
input bool              InpUseHtf         = false;                 // HTF Confirmation
input ENUM_HTF_PERIOD   InpHtfTf          = HTF_H4;                // HTF Timeframe
input bool              InpUseAdx         = false;                 // ADX Filter
input int               InpAdxLen         = 14;                    // ADX Length
input double            InpAdxThresh      = 20.0;                  // ADX Threshold
input ENUM_SMOOTH_METHOD InpSmoothMethod  = SMOOTH_NONE;           // Signal Smoothing
input int               InpSmoothLen      = 5;                     // Smoothing Length

// Trade Tools
input string            InpGroupTrade     = "=== Trade Tools ==="; // --- Trade Tools ---
input bool              InpLockSignal     = false;                 // Lock Signal
input ENUM_STOP_BUF_MODE InpStopBufMode   = BUF_NONE;              // Stop Buffer Mode
input int               InpStopBufTicks   = 0;                     // Buffer Ticks / Points
input double            InpStopBufAtr     = 0.0;                   // Buffer ATR Fraction
input int               InpAtrLen         = 14;                    // ATR Length
input int               InpExpiryBars     = 20;                    // Armed Expiry Bars
input bool              InpUseBreakeven   = false;                 // Stop to Breakeven after TP1

// Visuals
input string            InpGroupVisuals   = "=== Visuals ===";     // --- Visuals ---
input bool              InpShowLevels     = true;                  // Trade Levels
input bool              InpShowABC        = true;                  // A - B - C Markers
input bool              InpShowProj       = true;                  // Projected Leg
input bool              InpShowMarker     = true;                  // Entry Markers
input int               InpLineExtBars    = 10;                    // Line Extension Bars (Length)
input int               InpHistCount      = 5;                     // Keep Last N Setups (1..20)

// Dashboard
input string            InpGroupDash      = "=== Dashboard ===";   // --- Dashboard ---
input bool              InpShowDash       = true;                  // Show Dashboard
input ENUM_DASH_POS     InpDashPos        = DASH_TOP_RIGHT;        // Dashboard Position
input int               InpDashXOffset    = 20;                    // Dashboard X Offset (px)
input int               InpDashYOffset    = 25;                    // Dashboard Y Offset (px)

// Alerts
input string            InpGroupAlerts    = "=== Alerts ===";      // --- Alerts ---
input bool              InpEnableAlerts   = true;                  // Enable Popup Alerts
input bool              InpEnablePush     = false;                 // Enable Mobile Push Notifications
input bool              InpEnableSound    = true;                  // Enable Audio Alerts
input string            InpActionLong       = "long";              // Long Action
input string            InpActionShort      = "short";             // Short Action
input string            InpActionCloseLong  = "closelong";         // Close Long Action
input string            InpActionCloseShort = "closeshort";        // Close Short Action
input string            InpActionInfo       = "info";              // Info Action

// Colors
input string            InpGroupColors    = "=== Colors ===";      // --- Colors ---
input color             InpColorBullish   = C'38,166,154';         // Bullish (#26a69a)
input color             InpColorBearish   = C'239,83,80';          // Bearish (#ef5350)
input color             InpColorNeutral   = C'249,168,37';         // Neutral (#f9a825)
input color             InpColorStop      = C'239,83,80';          // Stop (#ef5350)
input color             InpColorEntry     = C'33,150,243';         // Entry (#2196f3)
input color             InpColorTargets   = C'38,166,154';         // Targets (#26a69a)
input color             InpColorABC       = C'120,123,134';        // A - B - C (#787b86)
input color             InpColorProj      = C'120,123,134';        // Projected Leg (#787b86)
input color             InpColorTxt       = clrWhite;              // Label Text
input color             InpDashHdrBg      = C'58,42,109';          // Dash Header BG (#3a2a6d)
input color             InpDashRowEvenBg  = C'19,23,34';           // Dash Row Even BG (#131722)
input color             InpDashRowOddBg   = C'28,33,46';           // Dash Row Odd BG (#1c212e)
input color             InpDashBorder     = C'42,48,64';           // Dash Border (#2a3040)
input color             InpDashTxt        = clrWhite;              // Dash Text
input color             InpDashLblTxt     = C'180,185,200';        // Dash Label Text

//--- STRUCTURES
struct SetupInfo
{
   int      id;
   int      dir;
   double   a;
   int      aBar;
   datetime aTime;
   double   b;
   int      bBar;
   datetime bTime;
   double   c;
   int      cBar;
   datetime cTime;
   double   leg;
   double   pb;
   double   entry;
   double   stop;
   double   mgStop;
   double   tp1;
   double   tp2;
   double   tp3;
   double   risk;
   int      armBar;
   int      entryBar;
   int      tp1Bar;
   int      tp2Bar;
   int      tp3Bar;
   int      slBar;
   int      cancelBar;
   int      closeBar;
   datetime closeTime;
   datetime closeExtTime;
   int      finalState;
   string   reason;
   bool     trendFail;
   bool     hitT1;
   bool     hitT2;
   bool     hitT3;
   bool     hitSL;
   bool     cancelled;
   bool     entryTrig;
};

//--- GLOBALS
string   g_prefix       = "Trident_";
int      g_artCounter   = 0;
ENUM_DASH_POS g_prevDashPos = (ENUM_DASH_POS)-1;

int      g_liveState    = 0;
int      g_liveDir      = 0;
double   g_liveEntry    = 0.0;
double   g_liveStop     = 0.0;
double   g_liveMgStop   = 0.0;
double   g_liveTp1      = 0.0;
double   g_liveTp2      = 0.0;
double   g_liveTp3      = 0.0;
double   g_liveRisk     = 0.0;
double   g_livePb       = 0.0;
double   g_liveLeg      = 0.0;
double   g_liveC        = 0.0;
int      g_liveEntryBar = 0;
string   g_liveReason   = "";

datetime g_lastAlertBarTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   g_prefix = "Trident_" + IntegerToString((long)ChartID()) + "_";

   ObjectsDeleteAll(0, g_prefix);
   g_prevDashPos = (ENUM_DASH_POS)-1;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, g_prefix);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Helper: Format Numbers (B/M/K)                                   |
//+------------------------------------------------------------------+
string FormatNum(double v)
{
   double a = MathAbs(v);
   if(a >= 1e9) return StringFormat("%.1fB", v / 1e9);
   if(a >= 1e6) return StringFormat("%.1fM", v / 1e6);
   if(a >= 1e3) return StringFormat("%.1fK", v / 1e3);
   return DoubleToString(v, Digits);
}

//+------------------------------------------------------------------+
//| Helper: Percent Text                                             |
//+------------------------------------------------------------------+
string FormatPctTxt(double lvl, double entry, bool isLong)
{
   if(entry == 0.0) return "-";
   double p = (isLong ? lvl - entry : entry - lvl) / entry * 100.0;
   return (p >= 0 ? "+" : "") + DoubleToString(p, 2) + "%";
}

//+------------------------------------------------------------------+
//| Helper: Risk-Reward                                              |
//+------------------------------------------------------------------+
double CalcRR(double lvl, double entry, double risk)
{
   if(lvl == 0.0 || entry == 0.0 || risk <= 0.0) return 0.0;
   return MathAbs(lvl - entry) / risk;
}

//+------------------------------------------------------------------+
//| Helper: Progress Bar String                                      |
//+------------------------------------------------------------------+
string FormatBar(double val, double maxVal)
{
   if(maxVal <= 0.0) maxVal = 1.0;
   double r = MathMin(MathMax(val / maxVal, 0.0), 1.0);
   int filled = (int)MathRound(r * 10.0);
   string b = "[";
   for(int i = 1; i <= 10; i++)
   {
      b += (i <= filled ? "|" : ".");
   }
   b += "]";
   return b + " " + IntegerToString((int)MathRound(r * 100.0)) + "%";
}

//+------------------------------------------------------------------+
//| Helper: Bar Color based on ratio                                 |
//+------------------------------------------------------------------+
color GetBarColor(double pct)
{
   if(pct >= 0.66) return InpColorBullish;
   if(pct >= 0.33) return InpColorNeutral;
   return InpColorBearish;
}

//+------------------------------------------------------------------+
//| Helper: State Text                                               |
//+------------------------------------------------------------------+
string GetStateTxt(int s)
{
   switch(s)
   {
      case 1: return "Armed";
      case 2: return "Active";
      case 3: return "TP1 hit";
      case 4: return "TP2 hit";
      case 5: return "TP3 hit";
      case 6: return "Stopped";
      case 7: return "Cancelled";
      default: return "-";
   }
}

//+------------------------------------------------------------------+
//| Helper: Timeframe String                                         |
//+------------------------------------------------------------------+
string GetTfString(int tf)
{
   if(tf == 0) tf = Period();
   switch(tf)
   {
      case 1:     return "1m";
      case 5:     return "5m";
      case 15:    return "15m";
      case 30:    return "30m";
      case 60:    return "1H";
      case 240:   return "4H";
      case 1440:  return "1D";
      case 10080: return "1W";
      case 43200: return "1M";
      default:    return IntegerToString(tf) + "m";
   }
}

//+------------------------------------------------------------------+
//| Smoothers for Array Data                                         |
//+------------------------------------------------------------------+
double CalcSMA(const double &arr[], int idx, int len)
{
   if(idx < len - 1) return arr[idx];
   double sum = 0;
   for(int i = 0; i < len; i++) sum += arr[idx - i];
   return sum / len;
}

double CalcWMA(const double &arr[], int idx, int len)
{
   if(idx < len - 1) return arr[idx];
   double sum = 0, denom = 0;
   for(int i = 0; i < len; i++)
   {
      double weight = len - i;
      sum += arr[idx - i] * weight;
      denom += weight;
   }
   return denom > 0 ? sum / denom : arr[idx];
}

double CalcVWMA(const double &arr[], const long &vol[], int idx, int len)
{
   if(idx < len - 1) return arr[idx];
   double sum = 0, sumV = 0;
   for(int i = 0; i < len; i++)
   {
      double v = (double)vol[idx - i];
      sum += arr[idx - i] * v;
      sumV += v;
   }
   return sumV > 0 ? sum / sumV : arr[idx];
}

//+------------------------------------------------------------------+
//| Chart Object Creators                                            |
//+------------------------------------------------------------------+
void CreateLine(string name, datetime t1, double p1, datetime t2, double p2, color clr, ENUM_LINE_STYLE style, int width)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME1, t1);
      ObjectSetDouble(0, name, OBJPROP_PRICE1, p1);
      ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE2, p2);
   }
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_RAY, false);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
}

void CreatePriceText(string name, datetime t, double p, string text, color clr, int anchor=ANCHOR_LEFT)
{
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_TEXT, 0, t, p);
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME1, t);
      ObjectSetDouble(0, name, OBJPROP_PRICE1, p);
   }
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
   ObjectSetString(0, name, OBJPROP_FONT, "Segoe UI");
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, false);
}

//+------------------------------------------------------------------+
//| Helper to get time at chronological bar offset                   |
//+------------------------------------------------------------------+
datetime GetBarTimeOffset(const datetime &c_time[], int total, int targetBar)
{
   if(targetBar < total)
      return c_time[targetBar];
   int diff = targetBar - (total - 1);
   return c_time[total - 1] + (datetime)(Period() * 60 * diff);
}

//+------------------------------------------------------------------+
//| Draw Setup Graphics from SetupInfo struct                        |
//+------------------------------------------------------------------+
void RenderSetupGraphics(const SetupInfo &s, const datetime &time[], int rates_total, bool isLatest)
{
   string uid = IntegerToString(s.id);
   bool bull = (s.dir == 1);

   int defaultEndBar;
   if(s.cancelled)
      defaultEndBar = (s.cancelBar > 0) ? (s.cancelBar + InpLineExtBars) : (s.armBar + InpLineExtBars);
   else if(s.closeBar > 0)
      defaultEndBar = s.closeBar + InpLineExtBars;
   else if(isLatest)
      defaultEndBar = rates_total - 1 + InpLineExtBars;
   else
      defaultEndBar = s.armBar + InpLineExtBars;

   datetime tDefaultEnd = GetBarTimeOffset(time, rates_total, defaultEndBar);

   datetime tEntryEnd = tDefaultEnd;
   if(s.entryTrig && s.entryBar > 0)
      tEntryEnd = GetBarTimeOffset(time, rates_total, s.entryBar + 2);

   datetime tTp1End = (s.hitT1 && s.tp1Bar > 0) ? GetBarTimeOffset(time, rates_total, s.tp1Bar) : tDefaultEnd;
   datetime tTp2End = (s.hitT2 && s.tp2Bar > 0) ? GetBarTimeOffset(time, rates_total, s.tp2Bar) : tDefaultEnd;
   datetime tTp3End = (s.hitT3 && s.tp3Bar > 0) ? GetBarTimeOffset(time, rates_total, s.tp3Bar) : tDefaultEnd;
   datetime tSlEnd  = (s.hitSL && s.slBar > 0)  ? GetBarTimeOffset(time, rates_total, s.slBar)  : tDefaultEnd;

   if(InpShowABC)
   {
      string aLbl = g_prefix + "A_" + uid;
      string bLbl = g_prefix + "B_" + uid;
      string cLbl = g_prefix + "C_" + uid;
      CreatePriceText(aLbl, s.aTime, s.a, "A", InpColorABC, bull ? ANCHOR_UPPER : ANCHOR_LOWER);
      CreatePriceText(bLbl, s.bTime, s.b, "B", InpColorABC, bull ? ANCHOR_LOWER : ANCHOR_UPPER);
      CreatePriceText(cLbl, s.cTime, s.c, "C", InpColorABC, bull ? ANCHOR_UPPER : ANCHOR_LOWER);
   }

   if(InpShowProj)
   {
      string projLine = g_prefix + "proj_" + uid;
      int dtBars = MathMax(s.bBar - s.aBar, 1);
      datetime projTime = GetBarTimeOffset(time, rates_total, s.cBar + dtBars);
      CreateLine(projLine, s.cTime, s.c, projTime, s.tp3, InpColorProj, STYLE_DASHDOT, 1);
   }

   if(s.entryTrig && InpShowMarker)
   {
      string markLbl = g_prefix + "mark_" + uid;
      datetime mTime = (s.entryBar < rates_total) ? time[s.entryBar] : time[rates_total - 1];
      CreatePriceText(markLbl, mTime, bull ? (s.c - 5 * Point) : (s.c + 5 * Point), bull ? "BULL" : "BEAR", bull ? InpColorBullish : InpColorBearish, bull ? ANCHOR_UPPER : ANCHOR_LOWER);
   }

   if(InpShowLevels)
   {
      string slLine    = g_prefix + "sl_" + uid;
      string entryLine = g_prefix + "entry_" + uid;
      string tp1Line   = g_prefix + "tp1_" + uid;
      string tp2Line   = g_prefix + "tp2_" + uid;
      string tp3Line   = g_prefix + "tp3_" + uid;

      string slLbl     = g_prefix + "sl_lbl_" + uid;
      string entryLbl  = g_prefix + "ent_lbl_" + uid;
      string tp1Lbl    = g_prefix + "tp1_lbl_" + uid;
      string tp2Lbl    = g_prefix + "tp2_lbl_" + uid;
      string tp3Lbl    = g_prefix + "tp3_lbl_" + uid;

      CreateLine(slLine, s.cTime, s.stop, tSlEnd, s.stop, InpColorStop, STYLE_SOLID, 2);
      CreateLine(entryLine, s.cTime, s.entry, tEntryEnd, s.entry, InpColorEntry, STYLE_DASH, 1);
      CreateLine(tp1Line, s.cTime, s.tp1, tTp1End, s.tp1, InpColorTargets, STYLE_DASH, 1);
      CreateLine(tp2Line, s.cTime, s.tp2, tTp2End, s.tp2, InpColorTargets, STYLE_DASH, 1);
      CreateLine(tp3Line, s.cTime, s.tp3, tTp3End, s.tp3, InpColorTargets, STYLE_DASH, 1);

      string slTxt = s.hitSL ? ((s.trendFail ? "SL HIT (TREND FAILURE) " : "SL HIT ") + FormatPctTxt(s.stop, s.entry, bull)) : ("SL " + DoubleToString(s.stop, Digits));
      string tp1Txt = s.hitT1 ? ("TP1 HIT " + FormatPctTxt(s.tp1, s.entry, bull)) : ("TP1 " + DoubleToString(s.tp1, Digits) + " - " + DoubleToString(CalcRR(s.tp1, s.entry, s.risk), 2) + "R");
      string tp2Txt = s.hitT2 ? ("TP2 HIT " + FormatPctTxt(s.tp2, s.entry, bull)) : ("TP2 " + DoubleToString(s.tp2, Digits) + " - " + DoubleToString(CalcRR(s.tp2, s.entry, s.risk), 2) + "R");
      string tp3Txt = s.hitT3 ? ("TP3 HIT " + FormatPctTxt(s.tp3, s.entry, bull)) : ("TP3 " + DoubleToString(s.tp3, Digits) + " - " + DoubleToString(CalcRR(s.tp3, s.entry, s.risk), 2) + "R");

      CreatePriceText(slLbl, tSlEnd, s.stop, slTxt, InpColorStop);
      CreatePriceText(entryLbl, tEntryEnd, s.entry, "Entry " + DoubleToString(s.entry, Digits), InpColorEntry);
      CreatePriceText(tp1Lbl, tTp1End, s.tp1, tp1Txt, InpColorTargets);
      CreatePriceText(tp2Lbl, tTp2End, s.tp2, tp2Txt, InpColorTargets);
      CreatePriceText(tp3Lbl, tTp3End, s.tp3, tp3Txt, InpColorTargets);
   }
}

//+------------------------------------------------------------------+
//| Dashboard UI                                                     |
//+------------------------------------------------------------------+
void DrawDashRow(int row, string label, string val, color valColor, ENUM_BASE_CORNER corner, bool isRightSide, bool isBottomSide, int startX, int startY, int rowH, int totalW)
{
   string nameBg  = g_prefix + "dash_bg_" + IntegerToString(row);
   string nameLbl = g_prefix + "dash_lbl_" + IntegerToString(row);
   string nameVal = g_prefix + "dash_val_" + IntegerToString(row);

   int displayIdx = isBottomSide ? (18 - row) : row;
   int y = startY + displayIdx * rowH;
   color bg = (row % 2 == 0) ? InpDashRowEvenBg : InpDashRowOddBg;

   int xBg   = isRightSide ? (startX + totalW) : startX;
   int xLbl  = isRightSide ? (startX + totalW - 8) : (startX + 8);
   int xVal  = isRightSide ? (startX + 8)          : (startX + totalW - 8);

   int yTextOffset = isBottomSide ? 3 : 2;
   ENUM_ANCHOR_POINT anchorLbl = isBottomSide ? ANCHOR_LEFT_LOWER  : ANCHOR_LEFT_UPPER;
   ENUM_ANCHOR_POINT anchorVal = isBottomSide ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER;

   // Row BG Rectangle
   if(ObjectFind(0, nameBg) < 0)
      ObjectCreate(0, nameBg, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, nameBg, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, nameBg, OBJPROP_XDISTANCE, xBg);
   ObjectSetInteger(0, nameBg, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, nameBg, OBJPROP_XSIZE, totalW);
   ObjectSetInteger(0, nameBg, OBJPROP_YSIZE, rowH);
   ObjectSetInteger(0, nameBg, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, nameBg, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, nameBg, OBJPROP_COLOR, InpDashBorder);
   ObjectSetInteger(0, nameBg, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, nameBg, OBJPROP_HIDDEN, true);

   // Label Text
   if(ObjectFind(0, nameLbl) < 0)
      ObjectCreate(0, nameLbl, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, nameLbl, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, nameLbl, OBJPROP_XDISTANCE, xLbl);
   ObjectSetInteger(0, nameLbl, OBJPROP_YDISTANCE, y + yTextOffset);
   ObjectSetString(0, nameLbl, OBJPROP_TEXT, label);
   ObjectSetInteger(0, nameLbl, OBJPROP_COLOR, InpDashLblTxt);
   ObjectSetString(0, nameLbl, OBJPROP_FONT, "Segoe UI");
   ObjectSetInteger(0, nameLbl, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(0, nameLbl, OBJPROP_ANCHOR, anchorLbl);
   ObjectSetInteger(0, nameLbl, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, nameLbl, OBJPROP_HIDDEN, true);

   // Value Text
   if(ObjectFind(0, nameVal) < 0)
      ObjectCreate(0, nameVal, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, nameVal, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, nameVal, OBJPROP_XDISTANCE, xVal);
   ObjectSetInteger(0, nameVal, OBJPROP_YDISTANCE, y + yTextOffset);
   ObjectSetString(0, nameVal, OBJPROP_TEXT, val);
   ObjectSetInteger(0, nameVal, OBJPROP_COLOR, valColor);
   ObjectSetString(0, nameVal, OBJPROP_FONT, "Segoe UI");
   ObjectSetInteger(0, nameVal, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(0, nameVal, OBJPROP_ANCHOR, anchorVal);
   ObjectSetInteger(0, nameVal, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, nameVal, OBJPROP_HIDDEN, true);
}

void UpdateDashboard(int totalBars, int htfBias, bool adxOk, double adxVal)
{
   if(!InpShowDash)
   {
      ObjectsDeleteAll(0, g_prefix + "dash_");
      return;
   }

   if(g_prevDashPos != InpDashPos)
   {
      ObjectsDeleteAll(0, g_prefix + "dash_");
      g_prevDashPos = InpDashPos;
   }

   ENUM_BASE_CORNER corner = CORNER_RIGHT_UPPER;
   bool isRightSide  = true;
   bool isBottomSide = false;

   if(InpDashPos == DASH_TOP_LEFT)
   {
      corner = CORNER_LEFT_UPPER;
      isRightSide  = false;
      isBottomSide = false;
   }
   else if(InpDashPos == DASH_BOTTOM_RIGHT)
   {
      corner = CORNER_RIGHT_LOWER;
      isRightSide  = true;
      isBottomSide = true;
   }
   else if(InpDashPos == DASH_BOTTOM_LEFT)
   {
      corner = CORNER_LEFT_LOWER;
      isRightSide  = false;
      isBottomSide = true;
   }

   int startX = InpDashXOffset;
   int startY = InpDashYOffset;
   int rowH   = 20;
   int totalW = 240;

   // Header Background
   string hdrBg = g_prefix + "dash_hdr_bg";
   string hdrT1 = g_prefix + "dash_hdr_t1";
   string hdrT2 = g_prefix + "dash_hdr_t2";

   int yHdr = startY + (isBottomSide ? 18 * rowH : 0);

   int xHdrBg   = isRightSide ? (startX + totalW) : startX;
   int xHdrLeft = isRightSide ? (startX + totalW - 8) : (startX + 8);
   int xHdrRight= isRightSide ? (startX + 8)          : (startX + totalW - 8);

   int yHdrTextOffset = isBottomSide ? 3 : 3;
   ENUM_ANCHOR_POINT anchorHdrLeft  = isBottomSide ? ANCHOR_LEFT_LOWER  : ANCHOR_LEFT_UPPER;
   ENUM_ANCHOR_POINT anchorHdrRight = isBottomSide ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER;

   if(ObjectFind(0, hdrBg) < 0) ObjectCreate(0, hdrBg, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, hdrBg, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, hdrBg, OBJPROP_XDISTANCE, xHdrBg);
   ObjectSetInteger(0, hdrBg, OBJPROP_YDISTANCE, yHdr);
   ObjectSetInteger(0, hdrBg, OBJPROP_XSIZE, totalW);
   ObjectSetInteger(0, hdrBg, OBJPROP_YSIZE, rowH + 2);
   ObjectSetInteger(0, hdrBg, OBJPROP_BGCOLOR, InpDashHdrBg);
   ObjectSetInteger(0, hdrBg, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, hdrBg, OBJPROP_COLOR, InpDashBorder);
   ObjectSetInteger(0, hdrBg, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, hdrBg, OBJPROP_HIDDEN, true);

   // Header Left Title
   if(ObjectFind(0, hdrT1) < 0) ObjectCreate(0, hdrT1, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, hdrT1, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, hdrT1, OBJPROP_XDISTANCE, xHdrLeft);
   ObjectSetInteger(0, hdrT1, OBJPROP_YDISTANCE, yHdr + yHdrTextOffset);
   ObjectSetString(0, hdrT1, OBJPROP_TEXT, "Trident Swing Projector");
   ObjectSetInteger(0, hdrT1, OBJPROP_COLOR, InpDashTxt);
   ObjectSetString(0, hdrT1, OBJPROP_FONT, "Segoe UI Semibold");
   ObjectSetInteger(0, hdrT1, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(0, hdrT1, OBJPROP_ANCHOR, anchorHdrLeft);
   ObjectSetInteger(0, hdrT1, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, hdrT1, OBJPROP_HIDDEN, true);

   // Header Right Subtitle
   if(ObjectFind(0, hdrT2) < 0) ObjectCreate(0, hdrT2, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, hdrT2, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, hdrT2, OBJPROP_XDISTANCE, xHdrRight);
   ObjectSetInteger(0, hdrT2, OBJPROP_YDISTANCE, yHdr + yHdrTextOffset);
   ObjectSetString(0, hdrT2, OBJPROP_TEXT, Symbol() + " | " + GetTfString(Period()));
   ObjectSetInteger(0, hdrT2, OBJPROP_COLOR, InpDashTxt);
   ObjectSetString(0, hdrT2, OBJPROP_FONT, "Segoe UI");
   ObjectSetInteger(0, hdrT2, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(0, hdrT2, OBJPROP_ANCHOR, anchorHdrRight);
   ObjectSetInteger(0, hdrT2, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, hdrT2, OBJPROP_HIDDEN, true);

   // Values
   double rr1 = CalcRR(g_liveTp1, g_liveEntry, g_liveRisk);
   double rr2 = CalcRR(g_liveTp2, g_liveEntry, g_liveRisk);
   double rr3 = CalcRR(g_liveTp3, g_liveEntry, g_liveRisk);

   color dirCol = (g_liveDir == 1) ? InpColorBullish : (g_liveDir == -1 ? InpColorBearish : InpDashTxt);
   color stCol  = (g_liveState == 6) ? InpColorBearish : ((g_liveState >= 3 && g_liveState <= 5) ? InpColorBullish : InpDashTxt);
   string dirRow = (g_liveState == 0) ? "-" : (g_liveDir == 1 ? "Long" : "Short");
   string stRow  = GetStateTxt(g_liveState) + (g_liveState >= 5 && g_liveReason != "" ? " - " + g_liveReason : "");
   string entRow = (g_liveEntry == 0.0) ? "-" : DoubleToString(g_liveEntry, Digits);
   string slRow  = (g_liveStop == 0.0) ? "-" : DoubleToString(g_liveStop, Digits);
   string mgRow  = (g_liveMgStop == 0.0) ? "-" : DoubleToString(g_liveMgStop, Digits) + (g_liveMgStop != g_liveStop ? " - BE" : "");
   string t1Row  = (g_liveTp1 == 0.0) ? "-" : DoubleToString(g_liveTp1, Digits);
   string t2Row  = (g_liveTp2 == 0.0) ? "-" : DoubleToString(g_liveTp2, Digits);
   string t3Row  = (g_liveTp3 == 0.0) ? "-" : DoubleToString(g_liveTp3, Digits);

   string abRow = "-";
   if(g_liveLeg > 0)
   {
      if(InpSwingUnit == UNIT_PERCENT)
         abRow = (g_liveC == 0) ? "-" : DoubleToString(g_liveLeg / g_liveC * 100.0, 2) + "%";
      else
         abRow = FormatNum(g_liveLeg);
   }

   string bsRow = (g_liveEntryBar <= 0) ? "-" : IntegerToString(totalBars - 1 - g_liveEntryBar);

   string htfRow = !InpUseHtf ? "OFF" : (htfBias == 1 ? "Bull" : (htfBias == -1 ? "Bear" : "Flat"));
   color  htfCol = !InpUseHtf ? InpDashTxt : (htfBias == 1 ? InpColorBullish : (htfBias == -1 ? InpColorBearish : InpColorNeutral));
   color  adxCol = !InpUseAdx ? InpDashTxt : (adxOk ? InpColorBullish : InpColorBearish);

   string smRow = "OFF";
   switch(InpSmoothMethod)
   {
      case SMOOTH_SMA:  smRow = "SMA";  break;
      case SMOOTH_WMA:  smRow = "WMA";  break;
      case SMOOTH_VWMA: smRow = "VWMA"; break;
      default: smRow = "OFF"; break;
   }

   int r = 1;
   DrawDashRow(r++, "Lock", InpLockSignal ? "ACTIVE" : "OFF", InpLockSignal ? InpColorBearish : InpDashTxt, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Direction", dirRow, dirCol, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "State", stRow, stCol, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Entry", entRow, InpColorEntry, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Stop", slRow, InpColorStop, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Managed Stop", mgRow, InpColorStop, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "TP1 - Critical", t1Row, InpColorTargets, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "TP2", t2Row, InpColorTargets, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "TP3 - Equality", t3Row, InpColorTargets, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "R:R TP1", FormatBar(rr1, 5.0), GetBarColor(MathMin(rr1 / 5.0, 1.0)), corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "R:R TP2", FormatBar(rr2, 5.0), GetBarColor(MathMin(rr2 / 5.0, 1.0)), corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "R:R TP3", FormatBar(rr3, 5.0), GetBarColor(MathMin(rr3 / 5.0, 1.0)), corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Pullback", FormatBar(g_livePb, 100.0), GetBarColor(MathMin(g_livePb / 100.0, 1.0)), corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "AB Swing", abRow, InpDashTxt, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Bars Since Entry", bsRow, InpDashTxt, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "HTF Bias", htfRow, htfCol, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "ADX", InpUseAdx ? FormatBar(adxVal, 100.0) : "OFF", adxCol, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
   DrawDashRow(r++, "Smoothing", smRow, InpDashTxt, corner, isRightSide, isBottomSide, startX, startY, rowH, totalW);
}

//+------------------------------------------------------------------+
//| Alert Handler                                                    |
//+------------------------------------------------------------------+
void FireAlert(string action, string eventName, string msg)
{
   if(!InpEnableAlerts && !InpEnablePush && !InpEnableSound) return;

   string fullMsg = "MarkitTick Trident [" + Symbol() + " " + GetTfString(Period()) + "] " + msg;
   
   if(InpEnableAlerts)
      Alert(fullMsg);
   if(InpEnablePush)
      SendNotification(fullMsg);
   if(InpEnableSound)
      PlaySound("alert.wav");
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < InpPivotLeft + InpPivotRight + 10)
      return(0);

   // Populate chronological arrays (0 = oldest, rates_total-1 = newest)
   // This guarantees 100% consistent chronological execution in MT4
   double c_high[], c_low[], c_close[];
   datetime c_time[];
   long c_vol[];

   ArrayResize(c_high, rates_total);
   ArrayResize(c_low, rates_total);
   ArrayResize(c_close, rates_total);
   ArrayResize(c_time, rates_total);
   ArrayResize(c_vol, rates_total);

   for(int i = 0; i < rates_total; i++)
   {
      int s = rates_total - 1 - i;
      c_high[i]  = high[s];
      c_low[i]   = low[s];
      c_close[i] = close[s];
      c_time[i]  = time[s];
      c_vol[i]   = tick_volume[s];
   }

   // Simulation State Variables
   int      st          = 0;
   int      stDir       = 0;
   double   stA         = 0.0; int stABar = 0; datetime stATime = 0;
   double   stB         = 0.0; int stBBar = 0; datetime stBTime = 0;
   double   stC         = 0.0; int stCBar = 0; datetime stCTime = 0;
   double   stLeg       = 0.0;
   double   stPb        = 0.0;
   double   stEntry     = 0.0;
   double   stStop      = 0.0;
   double   stMgStop    = 0.0;
   double   stTp1       = 0.0;
   double   stTp2       = 0.0;
   double   stTp3       = 0.0;
   double   stRisk      = 0.0;
   int      stArmBar    = 0;
   int      stEntryBar  = 0;
   string   stReason    = "";
   bool     stTrendFail = false;
   int      g_closeBar  = -1;
   datetime g_closeTime = 0;

   // Swings Tracking
   double   lastPH      = 0.0; int lastPHBar = -1; datetime lastPHTime = 0; double prevPH = 0.0;
   double   lastPL      = 0.0; int lastPLBar = -1; datetime lastPLTime = 0; double prevPL = 0.0;

   SetupInfo allSetups[];
   int setupsCount = 0;
   int setupCounter = 0;

   // Maintain leg source series across bars
   double legSrcArr[];
   ArrayResize(legSrcArr, rates_total);
   ArrayInitialize(legSrcArr, 0.0);

   int htfBias = 0;
   double liveAdx = 0.0;
   bool liveAdxOk = !InpUseAdx;

   // Loop chronologically from oldest to newest
   for(int bar = InpPivotLeft + InpPivotRight + 2; bar < rates_total; bar++)
   {
      if(lastPH > 0 && lastPL > 0)
         legSrcArr[bar] = MathAbs(lastPH - lastPL);
      else
         legSrcArr[bar] = (bar > 0) ? legSrcArr[bar - 1] : 0.0;

      int rLen = InpPivotRight;
      int pivBar = bar - rLen;

      // 1. Pivot Detection
      double ph = 0.0, pl = 0.0;
      if(pivBar >= InpPivotLeft)
      {
         double pHigh = c_high[pivBar];
         double pLow  = c_low[pivBar];
         bool isPH = true, isPL = true;

         for(int i = 1; i <= InpPivotLeft; i++)
         {
            if(c_high[pivBar - i] > pHigh) isPH = false;
            if(c_low[pivBar - i] < pLow)   isPL = false;
         }
         for(int i = 1; i <= InpPivotRight; i++)
         {
            if(c_high[pivBar + i] > pHigh) isPH = false;
            if(c_low[pivBar + i] < pLow)   isPL = false;
         }

         if(isPH) ph = pHigh;
         if(isPL) pl = pLow;
      }

      // 2. ATR & Stop Buffer
      double atrVal = 0.0;
      if(InpStopBufMode == BUF_ATR_FRACTION)
      {
         int shift = rates_total - 1 - bar;
         atrVal = iATR(NULL, 0, InpAtrLen, shift);
      }
      double buf = 0.0;
      if(InpStopBufMode == BUF_TICKS)
         buf = InpStopBufTicks * Point;
      else if(InpStopBufMode == BUF_ATR_FRACTION)
         buf = atrVal * InpStopBufAtr;

      // 3. HTF Confirmation
      htfBias = 0;
      if(InpUseHtf)
      {
         int htfPeriod = (int)InpHtfTf;
         if(htfPeriod == 0) htfPeriod = Period();
         double htfC1 = iClose(NULL, htfPeriod, 1);
         double htfC2 = iClose(NULL, htfPeriod, 2);
         if(htfC1 > 0 && htfC2 > 0)
            htfBias = (htfC1 > htfC2 ? 1 : (htfC1 < htfC2 ? -1 : 0));
      }

      // 4. ADX Filter
      double adxVal = 0.0;
      bool adxOk = !InpUseAdx;
      if(InpUseAdx)
      {
         int shift = rates_total - 1 - bar;
         adxVal = iADX(NULL, 0, InpAdxLen, PRICE_CLOSE, MODE_MAIN, shift);
         adxOk = (adxVal >= InpAdxThresh);
         if(bar == rates_total - 1)
         {
            liveAdx   = adxVal;
            liveAdxOk = adxOk;
         }
      }

      // 5. Smoothing Ratio
      double legForSmooth = legSrcArr[bar];
      double legSmoothed  = legForSmooth;
      if(InpSmoothMethod == SMOOTH_SMA)
         legSmoothed = CalcSMA(legSrcArr, bar, InpSmoothLen);
      else if(InpSmoothMethod == SMOOTH_WMA)
         legSmoothed = CalcWMA(legSrcArr, bar, InpSmoothLen);
      else if(InpSmoothMethod == SMOOTH_VWMA)
         legSmoothed = CalcVWMA(legSrcArr, c_vol, bar, InpSmoothLen);

      double smoothRatio = (legForSmooth != 0.0) ? (legSmoothed / legForSmooth) : 1.0;

      // 6. Pattern Candidates Check
      int    cDir  = 0;
      double cA    = 0.0; int cABar = 0; datetime cATime = 0;
      double cB    = 0.0; int cBBar = 0; datetime cBTime = 0;
      double cC    = 0.0; int cCBar = 0; datetime cCTime = 0;
      double cLeg  = 0.0;
      double cPb   = 0.0;

      // Bullish candidate: PL confirmed
      if(pl > 0 && lastPL > 0 && lastPH > 0 && lastPLBar < lastPHBar && lastPHBar < pivBar && pl > lastPL)
      {
         double ab = lastPH - lastPL;
         if(ab > 0)
         {
            double pb = (lastPH - pl) / ab * 100.0;
            bool trendOk = !InpUseTrend || (prevPH > 0 && lastPH > prevPH);
            if(pb >= InpMinPullback && pb <= InpMaxPullback && trendOk)
            {
               cDir  = 1;
               cA    = lastPL; cABar = lastPLBar; cATime = lastPLTime;
               cB    = lastPH; cBBar = lastPHBar; cBTime = lastPHTime;
               cC    = pl;     cCBar = pivBar;    cCTime = c_time[pivBar];
               cLeg  = (InpSwingUnit == UNIT_PERCENT && lastPL != 0 ? pl * (ab / lastPL) : ab) * smoothRatio;
               cPb   = pb;
            }
         }
      }

      // Bearish candidate: PH confirmed
      if(cDir == 0 && ph > 0 && lastPH > 0 && lastPL > 0 && lastPHBar < lastPLBar && lastPLBar < pivBar && ph < lastPH)
      {
         double ab = lastPH - lastPL;
         if(ab > 0)
         {
            double pb = (ph - lastPL) / ab * 100.0;
            bool trendOk = !InpUseTrend || (prevPL > 0 && lastPL < prevPL);
            if(pb >= InpMinPullback && pb <= InpMaxPullback && trendOk)
            {
               cDir  = -1;
               cA    = lastPH; cABar = lastPHBar; cATime = lastPHTime;
               cB    = lastPL; cBBar = lastPLBar; cBTime = lastPLTime;
               cC    = ph;     cCBar = pivBar;    cCTime = c_time[pivBar];
               cLeg  = (InpSwingUnit == UNIT_PERCENT && lastPH != 0 ? ph * (ab / lastPH) : ab) * smoothRatio;
               cPb   = pb;
            }
         }
      }

      // Update Swings
      if(ph > 0)
      {
         prevPH    = lastPH;
         lastPH    = ph;
         lastPHBar = pivBar;
         lastPHTime= c_time[pivBar];
      }
      if(pl > 0)
      {
         prevPL    = lastPL;
         lastPL    = pl;
         lastPLBar = pivBar;
         lastPLTime= c_time[pivBar];
      }

      // 7. State Machine Execution
      int  st0     = st;
      bool isL     = (stDir == 1);
      bool openPos = (st0 >= 2 && st0 <= 4);
      bool wasBusy = (st0 == 1 || openPos);

      bool cancelBreak = (st0 == 1) && (isL ? c_low[bar - 1] < stC : c_high[bar - 1] > stC);
      bool cancelExp   = (st0 == 1) && (bar - stArmBar >= InpExpiryBars);
      bool cancelled   = (cancelBreak || cancelExp);
      bool entryTrig   = (st0 == 1) && !cancelled && (isL ? c_close[bar - 1] >= stEntry : c_close[bar - 1] <= stEntry);

      bool slCross = (openPos || entryTrig) && (isL ? c_low[bar] <= stMgStop : c_high[bar] >= stMgStop);
      bool c1      = (openPos || entryTrig) && (isL ? c_high[bar] >= stTp1 : c_low[bar] <= stTp1);
      bool c2      = (openPos || entryTrig) && (isL ? c_high[bar] >= stTp2 : c_low[bar] <= stTp2);
      bool c3      = (openPos || entryTrig) && (isL ? c_high[bar] >= stTp3 : c_low[bar] <= stTp3);

      bool hitSL = slCross;
      bool hitT1 = !slCross && c1 && (st0 == 2 || entryTrig);
      bool hitT2 = !slCross && c2 && (st0 <= 3 || entryTrig);
      bool hitT3 = !slCross && c3 && (st0 <= 4 || entryTrig);

      bool beExit     = hitSL && (stMgStop != stStop);
      bool drawnSlHit = hitSL && (stMgStop == stStop);

      if(cancelled)
      {
         st       = 7;
         stReason = cancelBreak ? "C broken" : "Expired";
      }
      else if(entryTrig)
      {
         st         = 2;
         stEntryBar = bar;
         stReason   = "";
      }

      if(hitSL)
      {
         stTrendFail = (st0 == 2);
         stReason    = (st0 == 2) ? "Trend failure" : (beExit ? "Breakeven" : "Stopped");
         st          = 6;
      }
      else if(hitT3)
      {
         st       = 5;
         stReason = "TP3 target";
      }
      else if(hitT2)
      {
         st = 4;
      }
      else if(hitT1)
      {
         st = 3;
      }

      if(InpUseBreakeven && hitT1 && st >= 3 && st <= 4)
         stMgStop = stEntry;

      bool setClosed = (st >= 5);
      if(setClosed && g_closeBar < 0)
      {
         g_closeBar  = bar;
         g_closeTime = c_time[bar];
      }

      // Update current active setup in array
      if(setupsCount > 0)
      {
         int curIdx = setupsCount - 1;
         allSetups[curIdx].finalState = st;
         allSetups[curIdx].reason     = stReason;
         allSetups[curIdx].mgStop     = stMgStop;
         if(entryTrig && allSetups[curIdx].entryBar == 0)
         {
            allSetups[curIdx].entryTrig = true;
            allSetups[curIdx].entryBar  = bar;
         }
         if(hitT1 && allSetups[curIdx].tp1Bar == 0)
         {
            allSetups[curIdx].hitT1  = true;
            allSetups[curIdx].tp1Bar = bar;
         }
         if(hitT2 && allSetups[curIdx].tp2Bar == 0)
         {
            allSetups[curIdx].hitT2  = true;
            allSetups[curIdx].tp2Bar = bar;
         }
         if(hitT3 && allSetups[curIdx].tp3Bar == 0)
         {
            allSetups[curIdx].hitT3  = true;
            allSetups[curIdx].tp3Bar = bar;
         }
         if(hitSL && allSetups[curIdx].slBar == 0)
         {
            allSetups[curIdx].hitSL     = true;
            allSetups[curIdx].slBar     = bar;
            allSetups[curIdx].trendFail = stTrendFail;
         }
         if(cancelled && allSetups[curIdx].cancelBar == 0)
         {
            allSetups[curIdx].cancelled = true;
            allSetups[curIdx].cancelBar = bar;
         }
         if(setClosed && allSetups[curIdx].closeBar < 0)
         {
            allSetups[curIdx].closeBar  = bar;
            allSetups[curIdx].closeTime = c_time[bar];
            allSetups[curIdx].closeExtTime = GetBarTimeOffset(c_time, rates_total, bar + InpLineExtBars);
         }
      }

      bool isLastBar = (bar == rates_total - 1);
      bool frozen    = InpLockSignal && isLastBar;
      bool busy      = (st == 1 || (st >= 2 && st <= 4));
      bool htfOk     = !InpUseHtf || (htfBias == cDir);
      bool newArmed  = (cDir != 0 && cLeg > 0 && !busy && !wasBusy && !frozen && htfOk && adxOk);

      if(newArmed)
      {
         stDir       = cDir;
         stA         = cA;
         stABar      = cABar;
         stATime     = cATime;
         stB         = cB;
         stBBar      = cBBar;
         stBTime     = cBTime;
         stC         = cC;
         stCBar      = cCBar;
         stCTime     = cCTime;
         stLeg       = cLeg;
         stPb        = cPb;
         stEntry     = (cDir == 1 ? cC + 0.25 * cLeg : cC - 0.25 * cLeg);
         stTp1       = (cDir == 1 ? cC + 0.50 * cLeg : cC - 0.50 * cLeg);
         stTp2       = (cDir == 1 ? cC + 0.75 * cLeg : cC - 0.75 * cLeg);
         stTp3       = (cDir == 1 ? cC + 1.00 * cLeg : cC - 1.00 * cLeg);
         stStop      = (cDir == 1 ? cC - buf : cC + buf);
         stMgStop    = stStop;
         stRisk      = MathAbs(stEntry - stStop);
         stArmBar    = bar;
         stEntryBar  = 0;
         stReason    = "";
         stTrendFail = false;
         g_closeBar  = -1;
         g_closeTime = 0;
         st          = 1;

         setupCounter++;
         SetupInfo s;
         s.id         = setupCounter;
         s.dir        = stDir;
         s.a          = stA; s.aBar = stABar; s.aTime = stATime;
         s.b          = stB; s.bBar = stBBar; s.bTime = stBTime;
         s.c          = stC; s.cBar = stCBar; s.cTime = stCTime;
         s.leg        = stLeg;
         s.pb         = stPb;
         s.entry      = stEntry;
         s.stop       = stStop;
         s.mgStop     = stMgStop;
         s.tp1        = stTp1;
         s.tp2        = stTp2;
         s.tp3        = stTp3;
         s.risk       = stRisk;
         s.armBar     = bar;
         s.entryBar   = 0;
         s.tp1Bar     = 0;
         s.tp2Bar     = 0;
         s.tp3Bar     = 0;
         s.slBar      = 0;
         s.cancelBar  = 0;
         s.closeBar   = -1;
         s.closeTime  = 0;
         s.closeExtTime = GetBarTimeOffset(c_time, rates_total, bar + InpLineExtBars);
         s.finalState = 1;
         s.reason     = "";
         s.trendFail  = false;
         s.hitT1      = false;
         s.hitT2      = false;
         s.hitT3      = false;
         s.hitSL      = false;
         s.cancelled  = false;
         s.entryTrig  = false;

         ArrayResize(allSetups, setupsCount + 1);
         allSetups[setupsCount] = s;
         setupsCount++;
      }

      // Live alerts on current forming bar
      if(isLastBar && c_time[bar] != g_lastAlertBarTime)
      {
         if(newArmed)
         {
            FireAlert(InpActionInfo, "armed", "Setup Armed: " + (stDir == 1 ? "LONG" : "SHORT") + " Entry: " + DoubleToString(stEntry, Digits));
            g_lastAlertBarTime = c_time[bar];
         }
         else if(entryTrig)
         {
            FireAlert((stDir == 1 ? InpActionLong : InpActionShort), "entry", "Entry Triggered at " + DoubleToString(c_close[bar], Digits));
            g_lastAlertBarTime = c_time[bar];
         }
         else if(cancelled)
         {
            FireAlert(InpActionInfo, "cancelled", "Setup Cancelled (" + stReason + ")");
            g_lastAlertBarTime = c_time[bar];
         }
         else if(hitSL)
         {
            FireAlert((stDir == 1 ? InpActionCloseLong : InpActionCloseShort), "stop", "Stop Reached (" + stReason + ")");
            g_lastAlertBarTime = c_time[bar];
         }
         else if(hitT3)
         {
            FireAlert((stDir == 1 ? InpActionCloseLong : InpActionCloseShort), "tp3", "TP3 Equality Target Reached");
            g_lastAlertBarTime = c_time[bar];
         }
         else if(hitT2)
         {
            FireAlert((stDir == 1 ? InpActionCloseLong : InpActionCloseShort), "tp2", "TP2 Target Reached");
            g_lastAlertBarTime = c_time[bar];
         }
         else if(hitT1)
         {
            FireAlert((stDir == 1 ? InpActionCloseLong : InpActionCloseShort), "tp1", "TP1 Critical Price Reached");
            g_lastAlertBarTime = c_time[bar];
         }
      }
   }

   // Update Global Live Dashboard State
   g_liveState    = st;
   g_liveDir      = stDir;
   g_liveEntry    = stEntry;
   g_liveStop     = stStop;
   g_liveMgStop   = stMgStop;
   g_liveTp1      = stTp1;
   g_liveTp2      = stTp2;
   g_liveTp3      = stTp3;
   g_liveRisk     = stRisk;
   g_livePb       = stPb;
   g_liveLeg      = stLeg;
   g_liveC        = stC;
   g_liveEntryBar = stEntryBar;
   g_liveReason   = stReason;

   // Render Last N Historical Setups
   int renderStart = MathMax(0, setupsCount - InpHistCount);
   for(int i = renderStart; i < setupsCount; i++)
   {
      bool isLatest = (i == setupsCount - 1);
      RenderSetupGraphics(allSetups[i], c_time, rates_total, isLatest);
   }

   // Render Dashboard
   UpdateDashboard(rates_total, htfBias, liveAdxOk, liveAdx);
   ChartRedraw(0);

   return(rates_total);
}
//+------------------------------------------------------------------+
