//+------------------------------------------------------------------+
//|                                         ClosedTradeExtractor.mqh |
//+------------------------------------------------------------------+
#ifndef CLOSEDTRADEEXTRACTOR_MQH
#define CLOSEDTRADEEXTRACTOR_MQH

#include "ScatterTypes.mqh"

//+------------------------------------------------------------------+
//| CClosedTradeExtractor                                            |
//| Reads closed deal history for a date range and computes each     |
//| trade's hold duration in minutes and net profit.                 |
//+------------------------------------------------------------------+
class CClosedTradeExtractor
  {
private:
   datetime              FindOpenTime(ulong position_id, datetime close_time_utc) const;

public:
                     CClosedTradeExtractor(void);
                    ~CClosedTradeExtractor(void);

   int                   Read(datetime from, datetime to, CTradePoint &trades[]);
  };

//+------------------------------------------------------------------+
//| Constructor: no member state to initialize.                      |
//+------------------------------------------------------------------+
CClosedTradeExtractor::CClosedTradeExtractor(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor: no dynamic resources to release.                     |
//+------------------------------------------------------------------+
CClosedTradeExtractor::~CClosedTradeExtractor(void)
  {
  }

//+------------------------------------------------------------------+
//| Read                                                             |
//| Selects history for the given date range, keeps only closing     |
//| deals, and computes each trade's hold duration in minutes and net|
//| profit (profit + swap + commission). Returns the number of trades|
//| populated.                                                       |
//+------------------------------------------------------------------+
int CClosedTradeExtractor::Read(datetime from, datetime to, CTradePoint &trades[])
  {
//--- scope the terminal's history cache to the requested range
   if(!::HistorySelect(from, to))
     {
      ::Print("ScatterDashboard: HistorySelect failed, error ", ::GetLastError());
      return(0);
     }
   int      total       = ::HistoryDealsTotal();
   ::ArrayResize(trades, total);
   int      found       = 0;
   ulong    ticket      = 0;
   long     entry_type  = 0;
   double   profit      = 0.0;
   double   swap        = 0.0;
   double   commission  = 0.0;
   string   symbol      = "";
   datetime close_time  = 0;
   datetime open_time   = 0;
   ulong    position_id = 0;
//--- iterate every deal in the selected history range
   for(int i = 0; i < total; i++)
     {
      ticket = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;
      //--- keep only deals that represent an actual closed trade
      entry_type = ::HistoryDealGetInteger(ticket, DEAL_ENTRY);
      if(entry_type != DEAL_ENTRY_OUT && entry_type != DEAL_ENTRY_INOUT)
         continue;
      profit      = ::HistoryDealGetDouble(ticket, DEAL_PROFIT);
      swap        = ::HistoryDealGetDouble(ticket, DEAL_SWAP);
      commission  = ::HistoryDealGetDouble(ticket, DEAL_COMMISSION);
      symbol      = ::HistoryDealGetString(ticket, DEAL_SYMBOL);
      close_time  = (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME);
      position_id = (ulong)::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
      //--- recover the open time from the earliest deal on this position
      open_time = FindOpenTime(position_id, close_time);
      //--- populate the trade point
      trades[found].ticket           = ticket;
      trades[found].symbol           = symbol;
      trades[found].duration_minutes = (double)(close_time - open_time) / 60.0;
      trades[found].profit           = profit + swap + commission;
      found++;
     }
   ::ArrayResize(trades, found);
   return(found);
  }

//+------------------------------------------------------------------+
//| FindOpenTime                                                     |
//| Searches the currently selected history for the earliest deal    |
//| sharing the given position id and returns its time. Falls back   |
//| to close_time_utc if no earlier deal on the position is found.   |
//+------------------------------------------------------------------+
datetime CClosedTradeExtractor::FindOpenTime(ulong position_id, datetime close_time_utc) const
  {
   int      total        = ::HistoryDealsTotal();
   datetime earliest     = close_time_utc;
   bool     found        = false;
   ulong    ticket       = 0;
   ulong    scanned_id   = 0;
   datetime scanned_time = 0;
   for(int i = 0; i < total; i++)
     {
      ticket     = ::HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;
      scanned_id = (ulong)::HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
      if(scanned_id != position_id)
         continue;
      scanned_time = (datetime)::HistoryDealGetInteger(ticket, DEAL_TIME);
      if(!found || scanned_time < earliest)
        {
         earliest = scanned_time;
         found    = true;
        }
     }
   return(earliest);
  }

#endif // CLOSEDTRADEEXTRACTOR_MQH
//+------------------------------------------------------------------+