//+------------------------------------------------------------------+
//|                                               iGroupExposurev2.mq4 |
//+------------------------------------------------------------------+
#property copyright ""
#property link      ""

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_minimum 0.0
#property indicator_maximum 0.1

#define SYMBOLS_MAX 1024
#define DEALS          0
#define BUY_LOTS       1
#define BUY_PROFIT      2
#define SELL_LOTS      3
#define SELL_PROFIT     4
#define NET_LOTS       5
#define PROFIT         6
#define font_size 11
#define font_name "Lucida Console"

extern color ExtColor=DodgerBlue;
extern string specify_dates_as = "yyyy.mm.dd hh:mm";
extern datetime from_date = D'1970.01.01 00:00';
extern datetime to_date = D'2030.12.31 00:00';

extern bool view_history = false;

string ExtName="GroupiExposure";
string ExtSymbols[SYMBOLS_MAX];
int    ExtSymbolsTotal=0;
double ExtSymbolsSummaries[SYMBOLS_MAX][7];
int    ExtLines=-1;
string ExtCols[8]={"Group ",
                   "  Deals",
                   "  Buys",
                   "  Buy P/L",
                   " Sells",
                   "  Sell P/L",
                   "Net lots",
                   "   Net P/L"};
//int    ExtShifts[8]={ 10, 90, 140, 220, 320, 410, 510, 570 };
int    ExtShifts[8]={ 10, 120, 180, 250, 350, 420, 540, 600 };
int    ExtVertShift=16;
double ExtMapBuffer[];

int order_mode = MODE_TRADES;

int save_total = 0;

#define max_name_chars 14
#define refresh_secs   15

datetime next_refresh = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

void setup()
{
  int windex=WindowFind(ExtName);
  if(windex>0) ObjectsDeleteAll(windex);
  
  ExtLines=-1;  
  
	IndicatorShortName(ExtName);
  SetIndexBuffer(0,ExtMapBuffer);
  SetIndexStyle(0,DRAW_NONE);
  IndicatorDigits(0);
	SetIndexEmptyValue(0,0.0);

  for (int i = 0; i < ExtSymbolsTotal; i++ )
  {
    ExtSymbols[i] = "";
  }  

  ExtSymbolsTotal = 0;
  
  next_refresh = 0;  

}


void init()
  {
  string t = " Open";
  if (view_history ) t = " History";
  
  ExtName = ExtName + t;

  setup();	

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void deinit()
  {
   int windex=WindowFind(ExtName);
   if(windex>0) ObjectsDeleteAll(windex);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+

string PadVal(double Val, int Prec, int PadSpc)
{
  string S = DoubleToStr(Val, Prec);
  
  while ( StringLen(S) <= PadSpc ) S = " " + S;
  
  return(S); 
}

void AddDefaultGrps()
{
  //return(0);
  
	//add defaults in order

	SymbolsIndex("averages");
	SymbolsIndex("core");
	SymbolsIndex("six");
	SymbolsIndex("amigos");
	SymbolsIndex("eight");
	SymbolsIndex("three1");
	SymbolsIndex("three2");
	SymbolsIndex("three3");
	SymbolsIndex("three4");
	SymbolsIndex("three5");
	SymbolsIndex("OTHERS");
	SymbolsIndex("------");
	SymbolsIndex("TOTALS");

}

string StringChangeToUpperCase(string sText) {
  // Example: StringChangeToUpperCase("oNe mAn"); // ONE MAN 
  int iLen=StringLen(sText), i, iChar;
  for(i=0; i < iLen; i++) {
    iChar=StringGetChar(sText, i);
    if(iChar >= 97 && iChar <= 122) sText=StringSetChar(sText, i, iChar-32);
  }
  return(sText);
}
 
string StringChangeToLowerCase(string sText) {
  // Example: StringChangeToLowerCase("oNe mAn"); // one man
  int iLen=StringLen(sText), i, iChar;
  for(i=0; i < iLen; i++) {
    iChar=StringGetChar(sText, i);
    if(iChar >= 65 && iChar <= 90) sText=StringSetChar(sText, i, iChar+32);
  }
  return(sText);  
}
 
string StringChangeFirstToUpperCase(string sText) {
  // Example: StringChangeFirstToUpperCase("oNe mAn"); // One Man
  int iLen=StringLen(sText), i, iChar, iLast=32;
  for(i=0; i < iLen; i++) {
    iChar=StringGetChar(sText, i);
    if(iLast==32 && iChar >= 97 && iChar <= 122) sText=StringSetChar(sText, i, iChar-32);
    else if(iLast!=32 && iChar >= 65 && iChar <= 90) sText=StringSetChar(sText, i, iChar+32);
    iLast=iChar;
  }
  return(sText);  
}

string TidyComment(string cmt)
{
  string result = cmt;
  
  int p = StringFind(result, "[sl]");
  if ( p >= 0 ) result = StringSubstr(result, 0, StringLen(result)-4);

  return(result);  
}

void ScanCommentNames()
{
  int i, j, total;
  bool found;
  string prs[SYMBOLS_MAX];   
  
  string cmt;
  
  int cmt_count = 0;
  
  if ( view_history ) total = OrdersHistoryTotal(); else total = OrdersTotal();
  
  for(i=0; i<total; i++)
  {
    if ( ! OrderSelect(i,SELECT_BY_POS, order_mode) ) continue;
    
    if ( OrderType() > OP_SELL ) continue;
    
    if ( OrderOpenTime() < from_date || OrderOpenTime() > to_date ) continue;
    
    //cmt = StringChangeToUpperCase(OrderComment());
    //cmt = TidyComment(cmt);
    cmt = OrderSymbol();
    //Print(cmt);
    
    found = false;
    
    for ( j=0; j<cmt_count; j++)
    {
      if(cmt==prs[j])
      {
        found=true;
        continue;
      }
    }

    if ( ( ! found ) && ( cmt != "" ) )
    {
      prs[cmt_count] = StringSubstr(cmt, 0, max_name_chars);
      //Print(prs[cmt_count]);
      cmt_count += 1;
      QSortStringArray(prs, 0, cmt_count-1);
    }
  }    
 
  
  //Print("SORTED ", cmt_count);
  
  for ( i = 0; i < cmt_count; i++) 
  {
    //Print(prs[i]);
    SymbolsIndex(prs[i]);
  }  

	SymbolsIndex("------");
//	SymbolsIndex("OTHERS");
	//SymbolsIndex("______");
	SymbolsIndex("TOTALS");
  
}

void QSortStringArray(string& A[],int low,int high)
  {
   string pivot, tmp;
   int    scan_forward, scan_back;
   int    middle;
//---- if elements to sort less than 2 - return.

   if(high-low<=0) return(0);
   else
     if(high-low==1)
       {
        //---- if count of elements to sort equal to 2 swap it if need
        if(A[high]<A[low])
          {
           tmp=A[low];
           A[low]=A[high];
           A[high]=tmp;
          }
        return(0);
       }
//---- calc middle element and store it in the pivot variable.
   middle=(low+high)/2;
   pivot=A[middle];
   //---- swap middle and first elements
   tmp=A[middle];
   A[middle]=A[low];
   A[low]=tmp;
//---- initializing scan_up and scan_down indexes
   scan_forward=low+1;
   scan_back=high;
//---- scan elements that places in opposite lists.
   while(scan_forward<scan_back)
     {
      //---- scan to forward. stop when crossing second list or current element greater than middle.
      while(scan_forward <= scan_back && A[scan_forward]<=pivot) scan_forward++;
      //---- scan to backward. stop when current element greater than or equal to middle.
      while(A[scan_back]>pivot) scan_back--;
      //---- if scan indexes not crossed they points to elements need to swap.
      if(scan_forward < scan_back)
        {
         tmp=A[scan_forward];
         A[scan_forward]=A[scan_back];
         A[scan_back]=tmp;
        }
     }
//---- swap first and dividing elements
   A[low] = A[scan_back];
//---- swap middle and dividing elements
   A[scan_back] = pivot;
//---- if first sublist has 2 or more elements sort it's too
   if(low < scan_back-1)   QSortStringArray(A, low, scan_back-1);
//---- if second sublist has 2 or more elements sort it's too
   if(scan_back+1 < high)  QSortStringArray(A, scan_back+1, high);
//---- that's all
   return(0);
  }
  
  
  
void start()
  {
   string name, s;
   int    i,col,line,windex=WindowFind(ExtName);
   
   color clr = ExtColor;
   
   if (view_history) order_mode = MODE_HISTORY; else order_mode = MODE_TRADES;
   
   if(windex<0) return;

   if ( TimeCurrent() <= next_refresh ) return;
   
   next_refresh = TimeCurrent() + refresh_secs;
   
   setup();
   
//---- header line
   if(ExtLines<0)
     {
      for(col=0; col<8; col++)
        {
         name="Head_"+order_mode+col;
         if(ObjectCreate(name,OBJ_LABEL,windex,0,0))
           {
            ObjectSet(name,OBJPROP_XDISTANCE,ExtShifts[col]);
            ObjectSet(name,OBJPROP_YDISTANCE,ExtVertShift);
            ObjectSetText(name,ExtCols[col],font_size,font_name,ExtColor);
           }
        }
      ExtLines=0;
     }
//----
   ArrayInitialize(ExtSymbolsSummaries,0.0);
   ScanCommentNames();
   //AddDefaultGrps();
   
   int total=Analyze(order_mode);
   
   if(total>-999999999)
     {
      line=1;
      for(i=0; i<ExtSymbolsTotal; i++)
        {
         //if(ExtSymbolsSummaries[i][DEALS]<=0) continue;
         line++;
         //---- add line
         if(line>ExtLines)
           {
            int y_dist=ExtVertShift*(line+1)+1;
            for(col=0; col<8; col++)
              {
               name="Line_"+order_mode+line+"_"+col;
               if(ObjectCreate(name,OBJ_LABEL,windex,0,0))
                 {
                  ObjectSet(name,OBJPROP_XDISTANCE,ExtShifts[col]);
                  ObjectSet(name,OBJPROP_YDISTANCE,y_dist);
                 }
              }
            ExtLines++;
           }
         //---- set line
         int    digits=0;//MarketInfo(ExtSymbols[i],MODE_DIGITS);
         double buy_lots=ExtSymbolsSummaries[i][BUY_LOTS];
         double sell_lots=ExtSymbolsSummaries[i][SELL_LOTS];
         double buy_profit=0.0;
         double sell_profit=0.0;
         //if(buy_lots!=0) 
         buy_profit=ExtSymbolsSummaries[i][BUY_PROFIT];///buy_lots;
         //if(sell_lots!=0) 
         sell_profit=ExtSymbolsSummaries[i][SELL_PROFIT];///sell_lots;
         name="Line_"+order_mode+line+"_0";
         
         if ( buy_lots == 0 && sell_lots == 0 ) clr = DarkSlateGray; else clr = ExtColor;
         
         ObjectSetText(name,ExtSymbols[i],font_size,font_name,clr);

         bool spacer = ( ExtSymbols[i] == "------" || ExtSymbols[i] == "______" );
         if ( spacer ) s = ""; else s = PadVal(ExtSymbolsSummaries[i][DEALS],0,4);
         name="Line_"+order_mode+line+"_1";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         if ( spacer ) s = ""; else s = PadVal(buy_lots,2,5);
         name="Line_"+order_mode+line+"_2";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         // if (buy_profit>=0) clr = Lime; else clr = Red;
         if ( spacer ) s = ""; else s = PadVal(buy_profit,2,7);
         name="Line_"+order_mode+line+"_3";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         
         if ( spacer ) s = ""; else s = PadVal(sell_lots,2,5);
         name="Line_"+order_mode+line+"_4";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         if ( spacer ) s = ""; else s = PadVal(sell_profit,2,7);
         name="Line_"+order_mode+line+"_5";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         if (  spacer ) s = ""; else s = PadVal(buy_lots-sell_lots,2,5);
         name="Line_"+order_mode+line+"_6";
         ObjectSetText(name,s,font_size,font_name,clr);
         
         if ( spacer ) s = ""; else s = PadVal(ExtSymbolsSummaries[i][PROFIT],2,9);
         name="Line_"+order_mode+line+"_7";
         ObjectSetText(name,s,font_size,font_name,clr);
        }
     }
//---- remove lines
/*
   if(total<ExtLines)
     {
      for(line=ExtLines; line>total; line--)
        {
         name="Line_"+order_mode+line+"_0";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_1";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_2";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_3";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_4";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_5";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_6";
         ObjectSetText(name,"");
         name="Line_"+order_mode+line+"_7";
         ObjectSetText(name,"");
        }
     }
*/
  //WindowRedraw();
  
//---- to avoid minimum==maximum
   ExtMapBuffer[Bars-1]=-1;
//----
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int Analyze(int order_mode)
  {
   double profit;
   int    i,index,type,total=OrdersTotal();
   int ixtot;
   
   if (order_mode == MODE_HISTORY) total = OrdersHistoryTotal();

   ixtot=SymbolsIndex("TOTALS");
    
   for(i=0; i<total; i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS, order_mode)) continue;
      
      type=OrderType();
      //if(type!=OP_BUY && type!=OP_SELL)
      if (type > 1 ) continue;
      
      if ( OrderSymbol() == "" ) continue;
      
      string cmt = OrderSymbol();//StringChangeToUpperCase(OrderComment());
      //cmt = TidyComment(cmt);

      if ( OrderOpenTime() < from_date || OrderOpenTime() > to_date ) continue;
      
      index=SymbolsIndex(StringSubstr(cmt, 0, max_name_chars));
      
      //if ( StringFind(OrderComment(), "trf", 0) >= 0 ) Print("ORDER TYPE for trf ", OrderType());
      
      if(index<0 || index>=SYMBOLS_MAX) continue;
      
      ExtSymbolsSummaries[index][DEALS]++;
      ExtSymbolsSummaries[ixtot][DEALS]++;
      
      profit=OrderProfit()+OrderCommission()+OrderSwap();
      
      ExtSymbolsSummaries[index][PROFIT]+=profit;
      ExtSymbolsSummaries[ixtot][PROFIT]+=profit;
      
      if(type==OP_BUY)
        {
         ExtSymbolsSummaries[index][BUY_LOTS]+=OrderLots();
         ExtSymbolsSummaries[ixtot][BUY_LOTS]+=OrderLots();
         ExtSymbolsSummaries[index][BUY_PROFIT]+=profit;//0.00;//OrderOpenPrice()*OrderLots();
         ExtSymbolsSummaries[ixtot][BUY_PROFIT]+=profit;
        }
      else
        {
         ExtSymbolsSummaries[index][SELL_LOTS]+=OrderLots();
         ExtSymbolsSummaries[ixtot][SELL_LOTS]+=OrderLots();
         ExtSymbolsSummaries[index][SELL_PROFIT]+=profit;//0.00;//OrderOpenPrice()*OrderLots();
         ExtSymbolsSummaries[ixtot][SELL_PROFIT]+=profit;
        }
     }
//----
   total=0;
   for(i=0; i<ExtSymbolsTotal; i++)
     {
      if(ExtSymbolsSummaries[i][DEALS]>0) total++;
     }
//----
   return(total);
  }

int SymbolsIndex(string SymbolName)
  {
   if ( SymbolName == "" ) SymbolName = "OTHERS";
   
   bool found=false;
//----
   for(int i=0; i<ExtSymbolsTotal; i++)
     {
      if(SymbolName==ExtSymbols[i])
        {
         found=true;
         break;
        }
     }

   //Print("adding ", SymbolName, " ", found);

   if(found) return(i);
   
   if(ExtSymbolsTotal>=SYMBOLS_MAX) return(-1);

   i=ExtSymbolsTotal;
   ExtSymbolsTotal++;
   ExtSymbols[i]=SymbolName;
   ExtSymbolsSummaries[i][DEALS]=0;
   ExtSymbolsSummaries[i][BUY_LOTS]=0;
   ExtSymbolsSummaries[i][BUY_PROFIT]=0;
   ExtSymbolsSummaries[i][SELL_LOTS]=0;
   ExtSymbolsSummaries[i][SELL_PROFIT]=0;
   ExtSymbolsSummaries[i][NET_LOTS]=0;
   ExtSymbolsSummaries[i][PROFIT]=0;
//----
   return(i);
  }

