﻿//+------------------------------------------------------------------+
//|                                                     Managers.mqh |
//|                              Copyright 2026, Christian Benjamin. |
//|                           https://www.mql5.com/en/users/lynchris |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Christian Benjamin."
#property link      "https://www.mql5.com/en/users/lynchris"
#property version   "1.0"
#property strict

#ifndef __MANAGERS_MQH__
#define __MANAGERS_MQH__

#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>

//+------------------------------------------------------------------+
//| CSymbolManager – manages the list of trading symbols             |
//+------------------------------------------------------------------+
class CSymbolManager
  {
private:
   string            m_symbols[];
   int               m_count;

public:
                     CSymbolManager(string symbolsList);
                    ~CSymbolManager() {}
   bool              Init();
   int               Count() const          { return m_count; }
   string            GetSymbol(int index) const;
   bool              IsSymbolValid(string symbol) const;
  };

//+------------------------------------------------------------------+
//| Constructor: parses a comma‑separated string into an array       |
//+------------------------------------------------------------------+
CSymbolManager::CSymbolManager(string symbolsList)
  {
//--- The input string (e.g., "EURUSD,GBPUSD") is split by commas
   m_count = 0;
   string temp[];
   int n = StringSplit(symbolsList, ',', temp);
   if(n > 0)
     {
      //--- Reserve space and copy each token after trimming whitespace
      ArrayResize(m_symbols, n);
      for(int i = 0; i < n; i++)
        {
         StringTrimLeft(temp[i]);
         StringTrimRight(temp[i]);
         if(StringLen(temp[i]) > 0)
           {
            m_symbols[m_count] = temp[i];
            m_count++;
           }
        }
      //--- Shrink the array to the exact number of valid symbols
      ArrayResize(m_symbols, m_count);
     }
  }

//+------------------------------------------------------------------+
//| Initializes the symbol list – skips invalid symbols              |
//+------------------------------------------------------------------+
bool CSymbolManager::Init()
  {
//--- No symbols from the input string – nothing to do
   if(m_count == 0)
     {
      Print("No symbols provided in input.");
      return(false);
     }

   string validSymbols[];
   int validCount = 0;

   for(int i = 0; i < m_count; i++)
     {
      string sym = m_symbols[i];
      //--- Check if the symbol actually exists in the broker's list
      if(SymbolInfoInteger(sym, SYMBOL_EXIST) == 0)
        {
         Print("Symbol ", sym, " not found – skipping.");
         continue;
        }
      //--- Make sure the symbol is visible in Market Watch (required for trading)
      if(!SymbolSelect(sym, true))
        {
         Print("Failed to select symbol ", sym, " – skipping.");
         continue;
        }
      //--- Build a temporary array of only valid symbols
      ArrayResize(validSymbols, validCount + 1);
      validSymbols[validCount] = sym;
      validCount++;
     }

//--- If no symbols are valid, the EA cannot trade anything
   if(validCount == 0)
     {
      Print("No valid symbols found. Please check symbol names.");
      m_count = 0;
      ArrayResize(m_symbols, 0);
      return(false);
     }

//--- Replace the original array with the validated one
   ArrayResize(m_symbols, validCount);
   for(int i = 0; i < validCount; i++)
      m_symbols[i] = validSymbols[i];
   m_count = validCount;

   Print("Symbol Manager initialized with ", m_count, " valid symbol(s).");
   return(true);
  }

//+------------------------------------------------------------------+
//| Returns symbol name at given index                               |
//+------------------------------------------------------------------+
string CSymbolManager::GetSymbol(int index) const
  {
   if(index >= 0 && index < m_count)
      return(m_symbols[index]);
   return("");
  }

//+------------------------------------------------------------------+
//| Checks if a symbol is in the monitored list                      |
//+------------------------------------------------------------------+
bool CSymbolManager::IsSymbolValid(string symbol) const
  {
   for(int i = 0; i < m_count; i++)
     {
      if(m_symbols[i] == symbol)
         return(true);
     }
   return(false);
  }

//+------------------------------------------------------------------+
//| CTradeManager – handles all trading operations                   |
//+------------------------------------------------------------------+
class CTradeManager
  {
private:
   CSymbolManager    *m_symbolManager;   
   int               m_magic;           
   double            m_lotSize;         

public:
                     CTradeManager(CSymbolManager *sm, int magic);
                    ~CTradeManager() {}

   void              SetLotSize(double lot)     { m_lotSize = lot; }
   double            GetLotSize() const         { return m_lotSize; }

   bool              OpenBuy(string symbol, double lot = -1.0);
   bool              OpenSell(string symbol, double lot = -1.0);
   bool              CloseAllPositions(string symbol);
   bool              CloseAllPositions();
   bool              CloseWinners();
   bool              CloseLosers();
   bool              SetTakeProfit(string symbol, double tpPrice);
   bool              SetStopLoss(string symbol, double slPrice);

   double            GetSymbolProfit(string symbol) const;
   int               GetSymbolPositionsCount(string symbol) const;
   double            GetTotalProfit() const;
   int               GetTotalPositions() const;
   void              UpdatePositions();
  };

//+------------------------------------------------------------------+
//| Constructor: stores symbol manager and magic number              |
//+------------------------------------------------------------------+
CTradeManager::CTradeManager(CSymbolManager *sm, int magic)
   : m_symbolManager(sm), m_magic(magic), m_lotSize(0.01)
  {
//--- The default lot size will be overridden by the EA's input later
  }

//+------------------------------------------------------------------+
//| Opens a Buy market order                                         |
//+------------------------------------------------------------------+
bool CTradeManager::OpenBuy(string symbol, double lot)
  {
//--- Protect against trading on symbols not listed in the manager
   if(!m_symbolManager.IsSymbolValid(symbol))
     {
      Print("Symbol ", symbol, " not in monitored list.");
      return(false);
     }
//--- Use the provided lot or fall back to the default
   double volume = (lot > 0) ? lot : m_lotSize;
//--- For a Buy order we must use the ASK price (the price we pay)
   double price = SymbolInfoDouble(symbol, SYMBOL_ASK);
   if(price <= 0)
     {
      Print("Failed to get ASK for ", symbol);
      return(false);
     }
//--- g_Trade is a global CTrade object provided by the main EA
   return(g_Trade.Buy(volume, symbol, price, 0, 0, "Buy from panel"));
  }

//+------------------------------------------------------------------+
//| Opens a Sell market order                                        |
//+------------------------------------------------------------------+
bool CTradeManager::OpenSell(string symbol, double lot)
  {
   if(!m_symbolManager.IsSymbolValid(symbol))
     {
      Print("Symbol ", symbol, " not in monitored list.");
      return(false);
     }
   double volume = (lot > 0) ? lot : m_lotSize;
//--- For a Sell order we must use the BID price (the price we receive)
   double price = SymbolInfoDouble(symbol, SYMBOL_BID);
   if(price <= 0)
     {
      Print("Failed to get BID for ", symbol);
      return(false);
     }
   return(g_Trade.Sell(volume, symbol, price, 0, 0, "Sell from panel"));
  }

//+------------------------------------------------------------------+
//| Closes all positions for a specific symbol                       |
//+------------------------------------------------------------------+
bool CTradeManager::CloseAllPositions(string symbol)
  {
   if(!m_symbolManager.IsSymbolValid(symbol))
     {
      Print("Symbol ", symbol, " not in monitored list.");
      return(false);
     }
   bool result = true;
//--- Iterate backwards because closing a position changes the PositionsTotal
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Symbol() == symbol && g_PositionInfo.Magic() == m_magic)
           {
            if(!g_Trade.PositionClose(g_PositionInfo.Ticket()))
              {
               Print("Failed to close position ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Closes ALL positions managed by this EA                          |
//+------------------------------------------------------------------+
bool CTradeManager::CloseAllPositions()
  {
   bool result = true;
//--- Reverse loop to avoid index issues after closing
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         //--- Only close positions with our Magic number (belonging to this EA)
         if(g_PositionInfo.Magic() == m_magic)
           {
            if(!g_Trade.PositionClose(g_PositionInfo.Ticket()))
              {
               Print("Failed to close position ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Closes all winning positions                                     |
//+------------------------------------------------------------------+
bool CTradeManager::CloseWinners()
  {
   bool result = true;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         //--- Profit > 0 means floating profit is positive
         if(g_PositionInfo.Magic() == m_magic && g_PositionInfo.Profit() > 0)
           {
            if(!g_Trade.PositionClose(g_PositionInfo.Ticket()))
              {
               Print("Failed to close winner ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Closes all losing positions                                      |
//+------------------------------------------------------------------+
bool CTradeManager::CloseLosers()
  {
   bool result = true;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Magic() == m_magic && g_PositionInfo.Profit() < 0)
           {
            if(!g_Trade.PositionClose(g_PositionInfo.Ticket()))
              {
               Print("Failed to close loser ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Sets Take Profit for all positions of a symbol                   |
//+------------------------------------------------------------------+
bool CTradeManager::SetTakeProfit(string symbol, double tpPrice)
  {
   if(!m_symbolManager.IsSymbolValid(symbol))
     {
      Print("Symbol ", symbol, " not in monitored list.");
      return(false);
     }
   bool result = true;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Symbol() == symbol && g_PositionInfo.Magic() == m_magic)
           {
            //--- PositionModify requires the ticket, new SL, and new TP. We keep the current SL.
            if(!g_Trade.PositionModify(g_PositionInfo.Ticket(), g_PositionInfo.StopLoss(), tpPrice))
              {
               Print("Failed to modify TP for ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Sets Stop Loss for all positions of a symbol                     |
//+------------------------------------------------------------------+
bool CTradeManager::SetStopLoss(string symbol, double slPrice)
  {
   if(!m_symbolManager.IsSymbolValid(symbol))
     {
      Print("Symbol ", symbol, " not in monitored list.");
      return(false);
     }
   bool result = true;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Symbol() == symbol && g_PositionInfo.Magic() == m_magic)
           {
            //--- Keep the current TP and only update the SL
            if(!g_Trade.PositionModify(g_PositionInfo.Ticket(), slPrice, g_PositionInfo.TakeProfit()))
              {
               Print("Failed to modify SL for ", g_PositionInfo.Ticket(), " error: ", GetLastError());
               result = false;
              }
           }
        }
     }
   return(result);
  }

//+------------------------------------------------------------------+
//| Returns total floating profit for a symbol                       |
//+------------------------------------------------------------------+
double CTradeManager::GetSymbolProfit(string symbol) const
  {
   double profit = 0.0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Symbol() == symbol && g_PositionInfo.Magic() == m_magic)
            profit += g_PositionInfo.Profit();   
        }
     }
   return(profit);
  }

//+------------------------------------------------------------------+
//| Returns number of open positions for a symbol                    |
//+------------------------------------------------------------------+
int CTradeManager::GetSymbolPositionsCount(string symbol) const
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Symbol() == symbol && g_PositionInfo.Magic() == m_magic)
            count++;
        }
     }
   return(count);
  }

//+------------------------------------------------------------------+
//| Returns total floating profit across all managed positions       |
//+------------------------------------------------------------------+
double CTradeManager::GetTotalProfit() const
  {
   double profit = 0.0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Magic() == m_magic)
            profit += g_PositionInfo.Profit();
        }
     }
   return(profit);
  }

//+------------------------------------------------------------------+
//| Returns total number of open positions managed by this EA        |
//+------------------------------------------------------------------+
int CTradeManager::GetTotalPositions() const
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(g_PositionInfo.SelectByIndex(i))
        {
         if(g_PositionInfo.Magic() == m_magic)
            count++;
        }
     }
   return(count);
  }

//+------------------------------------------------------------------+
//| Placeholder for position cache (no caching used)                 |
//+------------------------------------------------------------------+
void CTradeManager::UpdatePositions()
  {
//--- All queries are performed on the fly to keep data real-time
  }

#endif // __MANAGERS_MQH__
//+------------------------------------------------------------------+