//+------------------------------------------------------------------+
//|                                          DailyPnlCalculator.mqh  |
//+------------------------------------------------------------------+
#ifndef DAILYPNLCALCULATOR_MQH
#define DAILYPNLCALCULATOR_MQH

//+------------------------------------------------------------------+
//| CDailyPnlCalculator                                              |
//| Computes today's realized P&L from closed deal history and       |
//| today's floating P&L from currently open positions, plus the     |
//| combined total of both. The calendar day boundary is midnight    |
//| server time, matching the timestamps the trade server itself     |
//| uses on every deal.                                              |
//+------------------------------------------------------------------+
class CDailyPnlCalculator
  {
private:
   datetime          MidnightToday(void) const;

public:
                     CDailyPnlCalculator(void);
                    ~CDailyPnlCalculator(void);

   double            GetRealizedPnl(void);
   double            GetFloatingPnl(void) const;
   double            GetCombinedPnl(void);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDailyPnlCalculator::CDailyPnlCalculator(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CDailyPnlCalculator::~CDailyPnlCalculator(void)
  {
  }

//+------------------------------------------------------------------+
//| MidnightToday                                                    |
//| Returns midnight of the current server day. This is the reset    |
//| boundary the whole circuit breaker anchors to, since it matches  |
//| the server timestamps recorded on every deal.                    |
//+------------------------------------------------------------------+
datetime CDailyPnlCalculator::MidnightToday(void) const
  {
   MqlDateTime dt;
   ::TimeToStruct(::TimeCurrent(), dt);

//--- zeroing hour/min/sec turns "now" into "midnight of the same day"
   dt.hour = 0;
   dt.min  = 0;
   dt.sec  = 0;
   return(::StructToTime(dt));
  }

//+------------------------------------------------------------------+
//| GetRealizedPnl                                                   |
//| Computes today's realized P&L by selecting deal history from     |
//| midnight of the current server day to now, and summing profit,   |
//| swap, and commission for every exit deal. Entry deals are        |
//| skipped since they carry no realized result of their own.        |
//+------------------------------------------------------------------+
double CDailyPnlCalculator::GetRealizedPnl(void)
  {
   datetime from = MidnightToday();
   datetime to   = ::TimeCurrent();

//--- scope the history cache to today's window before reading deals
   if(!::HistorySelect(from, to))
     {
      ::PrintFormat("CDailyPnlCalculator: HistorySelect failed, error=%d", ::GetLastError());
      return(0.0);
     }

   double realized = 0.0;
   int total = ::HistoryDealsTotal();

   for(int i = 0; i < total; i++)
     {
      ulong ticket = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;

      //--- only closing deals realize P&L; an entry deal has none of its own
      long entry = ::HistoryDealGetInteger(ticket, DEAL_ENTRY);
      if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_INOUT)
         continue;

      double profit     = ::HistoryDealGetDouble(ticket, DEAL_PROFIT);
      double swap       = ::HistoryDealGetDouble(ticket, DEAL_SWAP);
      double commission = ::HistoryDealGetDouble(ticket, DEAL_COMMISSION);

      realized += (profit + swap + commission);
     }

   return(realized);
  }

//+------------------------------------------------------------------+
//| GetFloatingPnl                                                   |
//| Computes current floating P&L by summing POSITION_PROFIT and     |
//| POSITION_SWAP across every open position. Swap is included       |
//| because it is a real, already-accruing cost or credit, and       |
//| leaving it out would understate today's true exposure.           |
//+------------------------------------------------------------------+
double CDailyPnlCalculator::GetFloatingPnl(void) const
  {
   double floating = 0.0;
   int total = ::PositionsTotal();

   for(int i = 0; i < total; i++)
     {
      //--- PositionGetTicket() also selects the position for the Get* calls below
      ulong ticket = ::PositionGetTicket(i);
      if(ticket == 0)
         continue;

      double profit = ::PositionGetDouble(POSITION_PROFIT);
      double swap   = ::PositionGetDouble(POSITION_SWAP);

      floating += (profit + swap);
     }

   return(floating);
  }

//+------------------------------------------------------------------+
//| GetCombinedPnl                                                   |
//| Returns combined daily P&L: realized P&L plus floating P&L.      |
//+------------------------------------------------------------------+
double CDailyPnlCalculator::GetCombinedPnl(void)
  {
   return(GetRealizedPnl() + GetFloatingPnl());
  }

#endif // DAILYPNLCALCULATOR_MQH
//+------------------------------------------------------------------+