//+------------------------------------------------------------------+
//|                                             TradingButtons.mqh    |
//|                        Copyright 2025, Clemence Benjamin          |
//|                        https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property strict

#include <Controls\Button.mqh>
#include <Controls\CheckBox.mqh>
#include <Trade\Trade.mqh>
#include <Canvas\Canvas.mqh>

//+------------------------------------------------------------------+
//| CTradingButtons Class                                            |
//+------------------------------------------------------------------+
class CTradingButtons
{
private:
   CButton btnMultiToggle;
   CButton btnBuy, btnSell, btnCloseAll, btnDeleteOrders, btnCloseProfit, btnCloseLoss, btnBuyStop, btnSellStop;
   CCanvas buttonPanel;
   CTrade  trade;
   int     buttonWidth;
   int     buttonHeight;
   int     buttonSpacing;

   // multipair UI & resolution
   CCheckBox *pairChecks[];      // array of checkbox objects (dynamically allocated)
   int        checkWidth;
   int        checkHeight;
   int        checkSpacing;
   int        checkStartX;

   string     availablePairs[];  // resolved broker symbols (same index as requested base)
   string     resolvedBases[];   // parallel array of requested bases (for logging)
   bool       multiEnabled;

public:
   double LotSize;
   int    StopLoss;
   int    TakeProfit;
   int    StopOrderDistancePips;
   double RiskRewardRatio;

   CTradingButtons() : buttonWidth(100), buttonHeight(30), buttonSpacing(10),
                      checkWidth(120), checkHeight(20), checkSpacing(6), checkStartX(10),
                      LotSize(0.01), StopLoss(50), TakeProfit(100),
                      StopOrderDistancePips(8), RiskRewardRatio(2.0),
                      multiEnabled(true)
   {
   }

   // initialize UI and trade defaults
   void Init()
   {
      trade.SetExpertMagicNumber(123456);
      trade.SetDeviationInPoints(10);
      CreateButtonPanel();
      CreateButtons();
      CreateMultiToggle();
      UpdateMultiToggleVisual();
   }

   // clean-up
   void Deinit()
   {
      // destroy checkboxes
      for(int i = 0; i < ArraySize(pairChecks); i++)
      {
         if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
         {
            pairChecks[i].Destroy();
            delete pairChecks[i];
         }
      }
      ArrayFree(pairChecks);

      // destroy buttons and panel
      btnMultiToggle.Destroy();
      btnBuy.Destroy();
      btnSell.Destroy();
      btnCloseAll.Destroy();
      btnDeleteOrders.Destroy();
      btnCloseProfit.Destroy();
      btnCloseLoss.Destroy();
      btnBuyStop.Destroy();
      btnSellStop.Destroy();
      buttonPanel.Destroy();
      ObjectDelete(0, "ButtonPanel");
   }

   // allow EA to set default multipair behavior
   void SetMultiEnabled(const bool enabled)
   {
      multiEnabled = enabled;
      UpdateMultiToggleVisual();
   }

   // ---------------- SYMBOL RESOLUTION ----------------
   // Resolve requested base (e.g. "EURUSD") to a broker symbol (e.g. "EURUSD.0" or "FX.EURUSD")
   string ResolveSymbol(const string base)
   {
      if(StringLen(base) == 0) return("");

      // 1) Try exact symbol name first (no market-watch add)
      // Use mutable copy
      string baseName = base;
      if(SymbolInfoInteger(baseName, SYMBOL_SELECT) != 0 || SymbolSelect(baseName, false))
      {
         if(SymbolInfoInteger(baseName, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("ResolveSymbol: exact match found %s", baseName);
            return(baseName);
         }
      }

      // 2) search all terminal symbols
      int total = SymbolsTotal(false); // include all
      string base_u = base;
      StringToUpper(base_u);

      string firstStarts = "";
      string firstContains = "";

      for(int i = 0; i < total; i++)
      {
         string sym = SymbolName(i, false);
         string sym_u = sym;
         StringToUpper(sym_u);

         // skip exact same (already checked)
         if(sym_u == base_u) continue;

         // skip disabled trade symbols
         if(SymbolInfoInteger(sym, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED) continue;

         // prefer start-with matches (EURUSD.0)
         if(StringFind(sym_u, base_u) == 0)
         {
            if(firstStarts == "") firstStarts = sym;
         }
         else if(StringFind(sym_u, base_u) >= 0) // contains
         {
            if(firstContains == "") firstContains = sym;
         }
      }

      if(firstStarts != "")
      {
         PrintFormat("ResolveSymbol: resolved %s -> %s (starts-with)", base, firstStarts);
         return(firstStarts);
      }
      if(firstContains != "")
      {
         PrintFormat("ResolveSymbol: resolved %s -> %s (contains)", base, firstContains);
         return(firstContains);
      }

      PrintFormat("ResolveSymbol: no match for %s", base);
      return("");
   }

   // Create checkboxes for requested major pairs (resolving broker symbols),
   // position at yPos (pixels from top). EA must pass requested list and selection array.
   void CreatePairCheckboxes(string &inMajorPairs[], bool &inPairSelected[], int yPos)
   {
      // cleanup previous
      for(int i = 0; i < ArraySize(pairChecks); i++)
      {
         if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
         {
            pairChecks[i].Destroy();
            delete pairChecks[i];
         }
      }
      ArrayFree(pairChecks);
      ArrayResize(availablePairs, ArraySize(inMajorPairs));
      ArrayResize(resolvedBases, ArraySize(inMajorPairs));
      for(int k = 0; k < ArraySize(availablePairs); k++) { availablePairs[k] = ""; resolvedBases[k] = ""; }

      int count = ArraySize(inMajorPairs);
      if(count == 0) return;

      // Resolve each requested base to broker symbol
      for(int i = 0; i < count; i++)
      {
         string requested = inMajorPairs[i];
         string resolved = ResolveSymbol(requested);
         if(resolved == "")
         {
            PrintFormat("CreatePairCheckboxes: could not resolve %s -> skipping checkbox", requested);
            availablePairs[i] = "";
            resolvedBases[i] = requested;
            continue;
         }

         // Add to Market Watch if missing
         if(!SymbolSelect(resolved, true))
         {
            PrintFormat("CreatePairCheckboxes: SymbolSelect failed for %s (from %s) Err=%d", resolved, requested, GetLastError());
            // still try to use it if symbol exists
         }

         // double-check tradeable
         if(SymbolInfoInteger(resolved, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("CreatePairCheckboxes: resolved symbol %s is disabled (from %s) - skipping", resolved, requested);
            availablePairs[i] = "";
            resolvedBases[i] = requested;
            continue;
         }

         // Accept
         availablePairs[i] = resolved;
         resolvedBases[i] = requested;
      }

      // Build checkboxes for resolved entries (preserve index alignment)
      ArrayResize(pairChecks, count);
      int xPos = checkStartX;
      int chartW = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
      int wrapX = chartW - (buttonWidth * 3) - 30; // leave space for button panel

      for(int i = 0; i < count; i++)
      {
         if(StringLen(availablePairs[i]) == 0)
         {
            pairChecks[i] = NULL; // placeholder
            continue;
         }

         pairChecks[i] = new CCheckBox();
         string objName = "Chk_" + availablePairs[i];

         if(!pairChecks[i].Create(ChartID(), objName, 0, xPos, yPos, xPos + checkWidth, yPos + checkHeight))
         {
            PrintFormat("CreatePairCheckboxes: failed to create checkbox %s Err=%d", objName, GetLastError());
            delete pairChecks[i];
            pairChecks[i] = NULL;
            availablePairs[i] = ""; // mark as failed
            continue;
         }

         // show resolved broker symbol label
         pairChecks[i].Text(" " + availablePairs[i]);
         pairChecks[i].Color(clrBlack);

         // initial checked state from EA's selection array (if available)
         bool checked = false;
         if(i < ArraySize(inPairSelected)) checked = inPairSelected[i];
         pairChecks[i].Checked(checked);

         xPos += checkWidth + checkSpacing;
         if(xPos + checkWidth > wrapX)
         {
            xPos = checkStartX;
            yPos += checkHeight + checkSpacing;
         }
      }

      ChartRedraw();
      PrintFormat("CreatePairCheckboxes: created checkboxes (resolved count=%d)", CountResolvedPairs());
      // Log mapping
      for(int i = 0; i < ArraySize(availablePairs); i++)
      {
         if(StringLen(availablePairs[i]) > 0)
            PrintFormat("CreatePairCheckboxes: requested %s -> broker %s (index %d)", resolvedBases[i], availablePairs[i], i);
      }
   }

   // Count resolved availablePairs entries
   int CountResolvedPairs()
   {
      int c = 0;
      for(int i = 0; i < ArraySize(availablePairs); i++)
         if(StringLen(availablePairs[i]) > 0) c++;
      return c;
   }

   // ---------------- EVENT HANDLING ----------------
   // EA must forward majorPairs & pairSelected arrays (requested base names + selection flags)
   void HandleChartEvent(const int id, const string &sparam, string &inMajorPairs[], bool &inPairSelected[])
   {
      if(id == CHARTEVENT_OBJECT_CLICK)
      {
         // Checkbox click handling: check for "Chk_<resolvedSymbol>"
         if(StringFind(sparam, "Chk_") == 0)
         {
            // iterate resolved mapping to find which index clicked
            for(int i = 0; i < ArraySize(availablePairs); i++)
            {
               if(StringLen(availablePairs[i]) == 0) continue;
               string expected = "Chk_" + availablePairs[i];
               if(expected == sparam)
               {
                  if(CheckPointer(pairChecks[i]) == POINTER_DYNAMIC)
                  {
                     bool current = pairChecks[i].Checked();
                     // sync UI -> EA selection array at same index
                     if(i < ArraySize(inPairSelected))
                        inPairSelected[i] = current;
                     else
                     {
                        ArrayResize(inPairSelected, i+1);
                        inPairSelected[i] = current;
                     }
                     PrintFormat("HandleChartEvent: checkbox for %s toggled -> %s", availablePairs[i], current ? "true":"false");
                  }
                  break;
               }
            }
            return;
         }

         // Multi toggle
         if(sparam == btnMultiToggle.Name())
         {
            multiEnabled = !multiEnabled;
            UpdateMultiToggleVisual();
            PrintFormat("HandleChartEvent: Multi toggle clicked. New multiEnabled=%s", (multiEnabled ? "true":"false"));
            return;
         }

         // Buttons - delegate
         if(sparam == btnBuy.Name())
         {
            Print("Buy button clicked");
            OpenBuyOrder(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnSell.Name())
         {
            Print("Sell button clicked");
            OpenSellOrder(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnCloseAll.Name())
         {
            Print("Close All button clicked");
            CloseAllPositions(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnDeleteOrders.Name())
         {
            Print("Delete Orders button clicked");
            DeleteAllPendingOrders(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnCloseProfit.Name())
         {
            Print("Close Profit button clicked");
            CloseProfitablePositions(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnCloseLoss.Name())
         {
            Print("Close Loss button clicked");
            CloseLosingPositions(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnBuyStop.Name())
         {
            Print("Buy Stop button clicked");
            PlaceBuyStop(inMajorPairs, inPairSelected);
         }
         else if(sparam == btnSellStop.Name())
         {
            Print("Sell Stop button clicked");
            PlaceSellStop(inMajorPairs, inPairSelected);
         }
      }
   }

private:
   // UI creation helpers
   void CreateButtonPanel()
   {
      int panelWidthLocal = buttonWidth + 20;
      int panelHeightLocal = (buttonHeight + buttonSpacing) * 9 + buttonSpacing + 40;
      int x = 0;
      int y = 40;

      if(!buttonPanel.CreateBitmap(0, 0, "ButtonPanel", x, y, panelWidthLocal, panelHeightLocal, COLOR_FORMAT_ARGB_NORMALIZE))
      {
         Print("Failed to create button panel: Error=", GetLastError());
         return;
      }

      ObjectSetInteger(0, "ButtonPanel", OBJPROP_ZORDER, 10);
      buttonPanel.FillRectangle(0, 0, panelWidthLocal, panelHeightLocal, ColorToARGB(clrDarkGray, 200));
      buttonPanel.Rectangle(0, 0, panelWidthLocal - 1, panelHeightLocal - 1, ColorToARGB(clrRed, 255));
      buttonPanel.Rectangle(1, 1, panelWidthLocal - 2, panelHeightLocal - 2, ColorToARGB(clrRed, 255));
      buttonPanel.FillRectangle(0, 0, 20, 20, ColorToARGB(clrYellow, 255));
      buttonPanel.Update(true);
      ChartRedraw(0);
   }

   void CreateButtons()
   {
      int x = 10;
      int y = 160;
      string font = "Calibri";
      int fontSize = 8;
      color buttonBgColor = clrBlack;

      btnBuy.Create(0, "btnBuy", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnBuy.Text("Buy");
      ObjectSetString(0, "btnBuy", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnBuy", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnBuy", OBJPROP_BGCOLOR, buttonBgColor);
      btnBuy.Color(clrLime);
      ObjectSetInteger(0, "btnBuy", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnSell.Create(0, "btnSell", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnSell.Text("Sell");
      ObjectSetString(0, "btnSell", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnSell", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnSell", OBJPROP_BGCOLOR, buttonBgColor);
      btnSell.Color(clrRed);
      ObjectSetInteger(0, "btnSell", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnCloseAll.Create(0, "btnCloseAll", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnCloseAll.Text("Close All");
      ObjectSetString(0, "btnCloseAll", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnCloseAll", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnCloseAll", OBJPROP_BGCOLOR, buttonBgColor);
      btnCloseAll.Color(clrYellow);
      ObjectSetInteger(0, "btnCloseAll", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnDeleteOrders.Create(0, "btnDeleteOrders", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnDeleteOrders.Text("Delete Orders");
      ObjectSetString(0, "btnDeleteOrders", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnDeleteOrders", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnDeleteOrders", OBJPROP_BGCOLOR, buttonBgColor);
      btnDeleteOrders.Color(clrAqua);
      ObjectSetInteger(0, "btnDeleteOrders", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnCloseProfit.Create(0, "btnCloseProfit", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnCloseProfit.Text("Close Profit");
      ObjectSetString(0, "btnCloseProfit", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnCloseProfit", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnCloseProfit", OBJPROP_BGCOLOR, buttonBgColor);
      btnCloseProfit.Color(clrGold);
      ObjectSetInteger(0, "btnCloseProfit", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnCloseLoss.Create(0, "btnCloseLoss", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnCloseLoss.Text("Close Loss");
      ObjectSetString(0, "btnCloseLoss", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnCloseLoss", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnCloseLoss", OBJPROP_BGCOLOR, buttonBgColor);
      btnCloseLoss.Color(clrOrange);
      ObjectSetInteger(0, "btnCloseLoss", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnBuyStop.Create(0, "btnBuyStop", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnBuyStop.Text("Buy Stop");
      ObjectSetString(0, "btnBuyStop", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnBuyStop", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnBuyStop", OBJPROP_BGCOLOR, buttonBgColor);
      btnBuyStop.Color(clrLightPink);
      ObjectSetInteger(0, "btnBuyStop", OBJPROP_ZORDER, 11);

      y += buttonHeight + buttonSpacing;
      btnSellStop.Create(0, "btnSellStop", 0, x, y, x + buttonWidth, y + buttonHeight);
      btnSellStop.Text("Sell Stop");
      ObjectSetString(0, "btnSellStop", OBJPROP_FONT, font);
      ObjectSetInteger(0, "btnSellStop", OBJPROP_FONTSIZE, fontSize);
      ObjectSetInteger(0, "btnSellStop", OBJPROP_BGCOLOR, buttonBgColor);
      btnSellStop.Color(clrLightCoral);
      ObjectSetInteger(0, "btnSellStop", OBJPROP_ZORDER, 11);
   }

   void CreateMultiToggle()
   {
      int x = 10;
      int y = 120; // above the first button
      string font = "Calibri";
      int fontSize = 8;
      color buttonBgColor = clrBlack;

      if(btnMultiToggle.Create(0, "btnMultiToggle", 0, x, y, x + buttonWidth, y + buttonHeight))
      {
         ObjectSetString(0, "btnMultiToggle", OBJPROP_FONT, font);
         ObjectSetInteger(0, "btnMultiToggle", OBJPROP_FONTSIZE, fontSize);
         ObjectSetInteger(0, "btnMultiToggle", OBJPROP_BGCOLOR, buttonBgColor);
         ObjectSetInteger(0, "btnMultiToggle", OBJPROP_ZORDER, 11);
         Print("CreateMultiToggle: created multi toggle button");
      }
      else
         Print("CreateMultiToggle: failed to create multi toggle. Err=", GetLastError());
   }

   void UpdateMultiToggleVisual()
   {
      if(multiEnabled)
      {
         btnMultiToggle.Text("MULTI:ON");
         btnMultiToggle.ColorBackground(clrGreen);
         btnMultiToggle.Color(clrWhite);
      }
      else
      {
         btnMultiToggle.Text("MULTI:OFF");
         btnMultiToggle.ColorBackground(clrRed);
         btnMultiToggle.Color(clrWhite);
      }
      PrintFormat("UpdateMultiToggleVisual: multiEnabled=%s", (multiEnabled ? "true":"false"));
   }

   // ---------- trading helper functions ----------
   double PipSize(string symbol)
   {
      double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
      if(point <= 0)
      {
         Print("Invalid point size for ", symbol, ": Error=", GetLastError());
         return 0;
      }
      return point * 10.0;
   }

   bool IsSymbolValid(string symbol)
   {
      bool inMarketWatch = SymbolInfoDouble(symbol, SYMBOL_BID) > 0 && SymbolInfoDouble(symbol, SYMBOL_ASK) > 0;
      if(!inMarketWatch)
         Print("Symbol ", symbol, " invalid: Not in Market Watch or no valid bid/ask price. Error=", GetLastError());
      return inMarketWatch;
   }

   bool TradeBuySingle(const string symbol)
   {
      if(!IsSymbolValid(symbol)) return false;
      long tradeMode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
      if(tradeMode != SYMBOL_TRADE_MODE_FULL)
      {
         Print("TradeBuySingle: Skipping ", symbol, ": Trading disabled, TradeMode=", tradeMode);
         return false;
      }
      double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
      double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
      if(LotSize < minLot || LotSize > maxLot)
      {
         Print("TradeBuySingle: Skipping ", symbol, ": Invalid lot size ", LotSize, " (min: ", minLot, ", max: ", maxLot, ")");
         return false;
      }

      double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
      double sl = StopLoss > 0 ? price - StopLoss * PipSize(symbol) : 0;
      double tp = TakeProfit > 0 ? price + TakeProfit * PipSize(symbol) : 0;
      trade.SetTypeFillingBySymbol(symbol);
      int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
      price = NormalizeDouble(price, digits);
      sl = NormalizeDouble(sl, digits);
      tp = NormalizeDouble(tp, digits);
      if(trade.Buy(LotSize, symbol, price, sl, tp))
      {
         Print("Buy order placed on ", symbol, ": Ticket #", trade.ResultOrder());
         return true;
      }
      else
      {
         Print("Buy order failed on ", symbol, ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
         return false;
      }
   }

   bool TradeSellSingle(const string symbol)
   {
      if(!IsSymbolValid(symbol)) return false;
      long tradeMode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
      if(tradeMode != SYMBOL_TRADE_MODE_FULL)
      {
         Print("TradeSellSingle: Skipping ", symbol, ": Trading disabled, TradeMode=", tradeMode);
         return false;
      }
      double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
      double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
      if(LotSize < minLot || LotSize > maxLot)
      {
         Print("TradeSellSingle: Skipping ", symbol, ": Invalid lot size ", LotSize, " (min: ", minLot, ", max: ", maxLot, ")");
         return false;
      }

      double price = SymbolInfoDouble(symbol, SYMBOL_BID);
      double sl = StopLoss > 0 ? price + StopLoss * PipSize(symbol) : 0;
      double tp = TakeProfit > 0 ? price - TakeProfit * PipSize(symbol) : 0;
      trade.SetTypeFillingBySymbol(symbol);
      int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
      price = NormalizeDouble(price, digits);
      sl = NormalizeDouble(sl, digits);
      tp = NormalizeDouble(tp, digits);
      if(trade.Sell(LotSize, symbol, price, sl, tp))
      {
         Print("Sell order placed on ", symbol, ": Ticket #", trade.ResultOrder());
         return true;
      }
      else
      {
         Print("Sell order failed on ", symbol, ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
         return false;
      }
   }

   // ---------- main operations ----------
   void OpenBuyOrder(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting OpenBuyOrder");
      string chartSym = Symbol();

      // multipair disabled -> only chart symbol
      if(!multiEnabled)
      {
         PrintFormat("OpenBuyOrder: multipair disabled => trading only chart symbol %s", chartSym);
         if(SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("OpenBuyOrder: chart symbol %s not tradeable (disabled).", chartSym);
            return;
         }
         TradeBuySingle(chartSym);
         return;
      }

      // multipair enabled -> iterate indices and use resolved availablePairs[]
      bool chartTraded = false;
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(i < ArraySize(inPairSelected) && inPairSelected[i] && StringLen(availablePairs[i]) > 0)
         {
            string symbol = availablePairs[i];
            Print("Attempting Buy order on ", symbol, " (requested ", resolvedBases[i], ")");
            if(TradeBuySingle(symbol))
            {
               if(symbol == chartSym) chartTraded = true;
            }
         }
         else
         {
            if(i < ArraySize(inMajorPairs))
               Print("Skipping ", inMajorPairs[i], ": Not selected or unresolved");
         }
      }

      if(!chartTraded)
      {
         if(SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("OpenBuyOrder: chart symbol %s was not traded by checkboxes. Attempting BUY on chart symbol.", chartSym);
            TradeBuySingle(chartSym);
         }
         else
         {
            PrintFormat("OpenBuyOrder: chart symbol %s not tradeable; skipping chart symbol.", chartSym);
         }
      }
   }

   void OpenSellOrder(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting OpenSellOrder");
      string chartSym = Symbol();

      if(!multiEnabled)
      {
         PrintFormat("OpenSellOrder: multipair disabled => trading only chart symbol %s", chartSym);
         if(SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("OpenSellOrder: chart symbol %s not tradeable (disabled).", chartSym);
            return;
         }
         TradeSellSingle(chartSym);
         return;
      }

      bool chartTraded = false;
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(i < ArraySize(inPairSelected) && inPairSelected[i] && StringLen(availablePairs[i]) > 0)
         {
            string symbol = availablePairs[i];
            Print("Attempting Sell order on ", symbol, " (requested ", resolvedBases[i], ")");
            if(TradeSellSingle(symbol))
            {
               if(symbol == chartSym) chartTraded = true;
            }
         }
         else
         {
            if(i < ArraySize(inMajorPairs))
               Print("Skipping ", inMajorPairs[i], ": Not selected or unresolved");
         }
      }

      if(!chartTraded)
      {
         if(SymbolInfoInteger(chartSym, SYMBOL_TRADE_MODE) != SYMBOL_TRADE_MODE_DISABLED)
         {
            PrintFormat("OpenSellOrder: chart symbol %s was not traded by checkboxes. Attempting SELL on chart symbol.", chartSym);
            TradeSellSingle(chartSym);
         }
         else
         {
            PrintFormat("OpenSellOrder: chart symbol %s not tradeable; skipping chart symbol.", chartSym);
         }
      }
   }

  

   
   void CloseAllPositions(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting CloseAllPositions");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Close positions for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Checking positions for ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               for(int j = PositionsTotal() - 1; j >= 0; j--)
               {
                  ulong ticket = PositionGetTicket(j);
                  if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == inMajorPairs[i])
                  {
                     Print("Attempting to close position on ", inMajorPairs[i], ": Ticket #", ticket);
                     if(trade.PositionClose(ticket))
                        Print("Position closed on ", inMajorPairs[i], ": Ticket #", ticket);
                     else
                        Print("Close position failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
                  }
               }
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }

   void DeleteAllPendingOrders(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting DeleteAllPendingOrders");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Delete orders for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Checking pending orders for ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               for(int j = OrdersTotal() - 1; j >= 0; j--)
               {
                  ulong ticket = OrderGetTicket(j);
                  if(OrderSelect(ticket) && OrderGetString(ORDER_SYMBOL) == inMajorPairs[i])
                  {
                     Print("Attempting to delete pending order on ", inMajorPairs[i], ": Ticket #", ticket);
                     if(trade.OrderDelete(ticket))
                        Print("Pending order deleted on ", inMajorPairs[i], ": Ticket #", ticket);
                     else
                        Print("Delete pending order failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
                  }
               }
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }

   void CloseProfitablePositions(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting CloseProfitablePositions");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Close profitable positions for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Checking profitable positions for ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               for(int j = PositionsTotal() - 1; j >= 0; j--)
               {
                  ulong ticket = PositionGetTicket(j);
                  if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == inMajorPairs[i])
                  {
                     double profit = PositionGetDouble(POSITION_PROFIT);
                     if(profit > 0)
                     {
                        Print("Attempting to close profitable position on ", inMajorPairs[i], ": Ticket #", ticket, ", Profit=", profit);
                        if(trade.PositionClose(ticket))
                           Print("Profitable position closed on ", inMajorPairs[i], ": Ticket #", ticket);
                        else
                           Print("Close profitable position failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
                     }
                  }
               }
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }

   void CloseLosingPositions(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting CloseLosingPositions");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Close losing positions for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Checking losing positions for ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               for(int j = PositionsTotal() - 1; j >= 0; j--)
               {
                  ulong ticket = PositionGetTicket(j);
                  if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == inMajorPairs[i])
                  {
                     double profit = PositionGetDouble(POSITION_PROFIT);
                     if(profit < 0)
                     {
                        Print("Attempting to close losing position on ", inMajorPairs[i], ": Ticket #", ticket, ", Profit=", profit);
                        if(trade.PositionClose(ticket))
                           Print("Losing position closed on ", inMajorPairs[i], ": Ticket #", ticket);
                        else
                           Print("Close losing position failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
                     }
                  }
               }
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }

   void PlaceBuyStop(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting PlaceBuyStop");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Place Buy Stop orders for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Attempting Buy Stop order on ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               long tradeMode = SymbolInfoInteger(inMajorPairs[i], SYMBOL_TRADE_MODE);
               if(tradeMode != SYMBOL_TRADE_MODE_FULL)
               {
                  Print("Skipping ", inMajorPairs[i], ": Trading disabled, TradeMode=", tradeMode);
                  continue;
               }

               double minLot = SymbolInfoDouble(inMajorPairs[i], SYMBOL_VOLUME_MIN);
               double maxLot = SymbolInfoDouble(inMajorPairs[i], SYMBOL_VOLUME_MAX);
               if(LotSize < minLot || LotSize > maxLot)
               {
                  Print("Skipping ", inMajorPairs[i], ": Invalid lot size ", LotSize, " (min: ", minLot, ", max: ", maxLot, ")");
                  continue;
               }

               double ask = SymbolInfoDouble(inMajorPairs[i], SYMBOL_ASK);
               double price = ask + StopOrderDistancePips * PipSize(inMajorPairs[i]);
               double sl = StopLoss > 0 ? price - StopLoss * PipSize(inMajorPairs[i]) : 0;
               double tp = TakeProfit > 0 ? price + TakeProfit * PipSize(inMajorPairs[i]) : 0;
               if(RiskRewardRatio > 0 && StopLoss > 0)
               {
                  double risk = price - sl;
                  tp = price + risk * RiskRewardRatio;
               }
               trade.SetTypeFillingBySymbol(inMajorPairs[i]);
               int digits = (int)SymbolInfoInteger(inMajorPairs[i], SYMBOL_DIGITS);
               price = NormalizeDouble(price, digits);
               sl = NormalizeDouble(sl, digits);
               tp = NormalizeDouble(tp, digits);
               if(trade.BuyStop(LotSize, price, inMajorPairs[i], sl, tp))
                  Print("Buy Stop order placed on ", inMajorPairs[i], ": Ticket #", trade.ResultOrder());
               else
                  Print("Buy Stop order failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }

   void PlaceSellStop(string &inMajorPairs[], bool &inPairSelected[])
   {
      Print("Starting PlaceSellStop");
      string selectedPairs = "Selected pairs: ";
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
         selectedPairs += inMajorPairs[i] + "=" + (inPairSelected[i] ? "true" : "false") + (i < ArraySize(inMajorPairs) - 1 ? ", " : "");
      Print(selectedPairs);

      // Place Sell Stop orders for all selected pairs
      for(int i = 0; i < ArraySize(inMajorPairs); i++)
      {
         if(inPairSelected[i])
         {
            Print("Attempting Sell Stop order on ", inMajorPairs[i]);
            if(IsSymbolValid(inMajorPairs[i]))
            {
               long tradeMode = SymbolInfoInteger(inMajorPairs[i], SYMBOL_TRADE_MODE);
               if(tradeMode != SYMBOL_TRADE_MODE_FULL)
               {
                  Print("Skipping ", inMajorPairs[i], ": Trading disabled, TradeMode=", tradeMode);
                  continue;
               }

               double minLot = SymbolInfoDouble(inMajorPairs[i], SYMBOL_VOLUME_MIN);
               double maxLot = SymbolInfoDouble(inMajorPairs[i], SYMBOL_VOLUME_MAX);
               if(LotSize < minLot || LotSize > maxLot)
               {
                  Print("Skipping ", inMajorPairs[i], ": Invalid lot size ", LotSize, " (min: ", minLot, ", max: ", maxLot, ")");
                  continue;
               }

               double bid = SymbolInfoDouble(inMajorPairs[i], SYMBOL_BID);
               double price = bid - StopOrderDistancePips * PipSize(inMajorPairs[i]);
               double sl = StopLoss > 0 ? price + StopLoss * PipSize(inMajorPairs[i]) : 0;
               double tp = TakeProfit > 0 ? price - TakeProfit * PipSize(inMajorPairs[i]) : 0;
               if(RiskRewardRatio > 0 && StopLoss > 0)
               {
                  double risk = sl - price;
                  tp = price - risk * RiskRewardRatio;
               }
               trade.SetTypeFillingBySymbol(inMajorPairs[i]);
               int digits = (int)SymbolInfoInteger(inMajorPairs[i], SYMBOL_DIGITS);
               price = NormalizeDouble(price, digits);
               sl = NormalizeDouble(sl, digits);
               tp = NormalizeDouble(tp, digits);
               if(trade.SellStop(LotSize, price, inMajorPairs[i], sl, tp))
                  Print("Sell Stop order placed on ", inMajorPairs[i], ": Ticket #", trade.ResultOrder());
               else
                  Print("Sell Stop order failed on ", inMajorPairs[i], ": Retcode=", trade.ResultRetcode(), ", Comment=", trade.ResultComment());
            }
            else
               Print("Skipping ", inMajorPairs[i], ": Symbol invalid");
         }
         else
            Print("Skipping ", inMajorPairs[i], ": Not selected");
      }
   }
}; // class CTradingButtons
//+------------------------------------------------------------------+
