//+------------------------------------------------------------------+
//|                                             BucketTrading_v1.mq4 |
//|                      Copyright © 2008, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, by hotstorm"

// Extenral inputs
extern bool    OpenTradesEnabled          = false;
extern double  Lot                        = 0.1;
extern int     ProfitTargetPips           = 100;
extern int     StopLossPips               = 500;
extern int     Slippage                   = 2;
extern bool    TrailOn                    = false;
extern double  PercentOfProfTargetToTrail = 20;

#define NUMBER_OF_CCY 14
string pair[NUMBER_OF_CCY];
int mode[NUMBER_OF_CCY];
int orderSorted[NUMBER_OF_CCY][2];

string trend, out;
int bucketMode,openBucketMode;
bool closingMode = false, entryOK = false;
double profitBuys=0, profitSells=0, TotalProfitPips=0;
string COMMENT_ID="BucketTrader";
double TempTrailAmt, TempHiTrailVal, MinProfit;
double MinProfitBuysToOpen = 50;
string SymbolSuffix = "";
double pipMultiplier = 1;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
    if (StringLen(Symbol()) > 6)
        SymbolSuffix = StringSubstr(Symbol(), 6, 0);    
    else SymbolSuffix = "";

    if (MarketInfo("EURUSD"+SymbolSuffix,MODE_DIGITS)==5)
        pipMultiplier = 0.1;
    else
        pipMultiplier = 1;  


   pair[0] = "EURJPY"+SymbolSuffix; mode[0] = OP_SELL;
   pair[1] = "GBPUSD"+SymbolSuffix; mode[1] = OP_SELL;
   pair[2] = "EURGBP"+SymbolSuffix; mode[2] = OP_SELL;
   pair[3] = "GBPCHF"+SymbolSuffix; mode[3] = OP_SELL;
   pair[4] = "CHFJPY"+SymbolSuffix; mode[4] = OP_SELL;
   pair[5] = "USDCHF"+SymbolSuffix; mode[5] = OP_SELL;
   pair[6] = "AUDJPY"+SymbolSuffix; mode[6] = OP_SELL;
   
   pair[7] = "USDJPY"+SymbolSuffix; mode[7] = OP_BUY;
   pair[8] = "EURUSD"+SymbolSuffix; mode[8] = OP_BUY;
   pair[9] = "EURCHF"+SymbolSuffix; mode[9] = OP_BUY;
   pair[10] = "GBPJPY"+SymbolSuffix; mode[10] = OP_BUY;
   pair[11] = "USDCAD"+SymbolSuffix; mode[11] = OP_BUY;
   pair[12] = "CADJPY"+SymbolSuffix; mode[12] = OP_BUY;
   pair[13] = "AUDUSD"+SymbolSuffix; mode[13] = OP_BUY;
   
        
           
   
   /*
   The top seven pairs is set 1 and the bottom is set 2. These pairs will hedge each other. 
   Fresh start this method at the very beginning of the week, this will give you a good look at the 
   pairs as weeks goes on. Set 1 trade them SHORT and set 2 trade them LONG. No SL and no TP. As much as 
   possible run a script (attached) so as to maintain correct timing in opening them. Click twice the 
   profit column of your terminal so as to make the positive profit pairs stay at the top and the 
   negatives stay at the bottom or vice versa. Initially the order of this pairs is a mess, let it 
   run for a day or two and you will notice the pairs will start to make a proper order. All the buys 
   will stay at the bottom and all the sells will occupy the top or vice versa. It like putting to rest 
   the dirty bottled water and it will start to settle down after a certain period and all the dirt to 
   the bottom and the clearer water at the top.

   About a day or two, all the buys (if negatives) will stay below and the sell (if positives) will be 
   at the top. Now the indication that you should watch is, ideally the bottom 7 slots should be 
   occupied by the negatives, the first pair in the negative that crosses the boundary of positive and 
   negatives is the pairs we are concern. If one of the negative jump to slot 8 (counting from bottom) 
   there is also a corresponding positive that will jump to slot 7. This is the signal. We will trade 
   those two pairs that jump out of boundary.

   You should have another account where you real trading will be executed. You will trade the two pairs 
   that jump out. If the pair that jump up is Long then trade the two breakaway pairs LONG or vice versa. 
   I also trade the next two pairs that jump of the boundary. I limit myself to just 4 pairs being traded 
   at one time. The profit is up to you, what I do is when the pair i'm trading retreats 2 slots then 
   I close it.   
   */   
   
    // Define the variable if it doesn't already exist.
    if (!GlobalVariableCheck(COMMENT_ID+"_MaxTrailVal"))
        GlobalVariableSet(COMMENT_ID+"_MaxTrailVal", -100000);
   
   Comment("Initialized, wating for tick...");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
//----
   bool isAllOpen = false;
   double pl = 0;
   out = "";
   
   if(closingMode==true)
   {
      CloseAllOrders();
      if(OrdersTotal()==0)
         closingMode = false;
      return(0);
   }
   
   
   indicateTrend();
   isAllOpen = isAllOpen();
   // if it is buys/sells are more than 50 pips away from zero, then we can trade
   if(MathAbs(profitBuys)>MinProfitBuysToOpen && entryOK==true && isAllOpen==false && OpenTradesEnabled==true)
   {
      openBucket();
      Comment(out);
      return(0);
   }
   if(isAllOpen==true)
   {
      pl = GetTotalProfit();
      out = out + "\n==================================\n"; 
      
      
      if(openBucketMode==OP_BUY)
         out = out + "Open bucket mode: BUY\n";
      else 
         out = out + "Open bucket mode: SELL\n";
      
      out = out +  "TOTAL P/L   : $"+DoubleToStr(pl,2) + " ("+DoubleToStr(TotalProfitPips,0)+" pips)\n";
      out = out +  "Profit Traget : " + ProfitTargetPips + " pips\n";
      out = out +  "Stop Loss     : " + StopLossPips + " pips\n";
      
      if(openBucketMode!=bucketMode || TotalProfitPips<=-StopLossPips)
      {
         // stop loss hit or trend direction changed, we should close open bucket
         closingMode = true;
         CloseAllOrders();
      }
      
      // handle tariling stop
      if(TrailOn==true)
      {
            TempTrailAmt=(ProfitTargetPips * (PercentOfProfTargetToTrail / 100));
            TempHiTrailVal=GlobalVariableGet(COMMENT_ID+"_MaxTrailVal");
            
            MinProfit = ProfitTargetPips - TempTrailAmt;
            
            out = out +  "Trailing Stop : enabled (starts trailing after profit target hit)\n";
            
            if(TotalProfitPips > TempHiTrailVal)
               GlobalVariableSet(COMMENT_ID+"_MaxTrailVal",TotalProfitPips);
              
            if(TotalProfitPips <= 0)           
               GlobalVariableSet(COMMENT_ID+"_MaxTrailVal",-100000);    

            TempHiTrailVal=GlobalVariableGet(COMMENT_ID+"_MaxTrailVal");
            if (TempHiTrailVal >= ProfitTargetPips)
            {
               if(TotalProfitPips<=(TempHiTrailVal-TempTrailAmt) && 
               TotalProfitPips>=MinProfit) // it backed off, take profit..
               {
                  closingMode = true;
                  CloseAllOrders();
               }
            }      
      }
      else
      {
         if(TotalProfitPips>=ProfitTargetPips)
         {
            closingMode = true;
            CloseAllOrders();         
         }
      }
   }
   Comment(out);
   return(0);   
//----
  }
//+------------------------------------------------------------------+
// Closing of Open Orders
void CloseAllOrders()
{
    int prcMode;
    int res;
    int total = OrdersTotal();
    for (int cnt=total-1; cnt >=0; cnt--)
    {
        OrderSelect(cnt, SELECT_BY_POS);
        int mode = OrderType();
        res = -1;
        if (mode==OP_BUY || mode==OP_SELL)
        {
            if (mode==OP_BUY)
                prcMode = MODE_BID;
            else
                prcMode = MODE_ASK;
            RefreshRates();
            res = OrderClose(OrderTicket(),OrderLots(),MarketInfo(OrderSymbol(),prcMode),Slippage);
            if (res<0)
            {
                Print("OrderClose failed with error #",GetLastError());
            }
        }
    } // end for
}
double GetTotalProfit()
{
    int total=OrdersTotal();
    double TempTotalProfits = 0, pips = 0;
    TotalProfitPips = 0;
    for (int cnt=0;cnt < total;cnt++)
    {
        OrderSelect(cnt, SELECT_BY_POS);
        if (OrderType()==OP_BUY || OrderType()==OP_SELL)
        {
            TempTotalProfits += OrderProfit()+OrderCommission()+OrderSwap();
            openBucketMode = OrderType();
            if(OrderType()==OP_BUY)
               pips = MathRound((MarketInfo(OrderSymbol(),MODE_BID)-OrderOpenPrice())/(MarketInfo(OrderSymbol(),MODE_POINT)/pipMultiplier));
            else 
               pips = MathRound((OrderOpenPrice()-MarketInfo(OrderSymbol(),MODE_ASK))/(MarketInfo(OrderSymbol(),MODE_POINT)/pipMultiplier));            
            TotalProfitPips =  TotalProfitPips + pips;
        }
    }
    return(TempTotalProfits);
}
bool isPairOpen(string symb)
{
   int total = OrdersTotal();
   for(int i=total-1;i>=0;i--)
   {
      OrderSelect(i, SELECT_BY_POS);
      if(symb==OrderSymbol())
         return(true);
   }
   return(false);
}
void openBucket()
{
   int priceMode;
   
   if(isAllOpen()==true)
      return(0);

   if(bucketMode==OP_BUY)
       priceMode = MODE_ASK;
   else
       priceMode = MODE_BID;      
      
   for(int i=0;i<NUMBER_OF_CCY;i++)   
   {
      if(isPairOpen(pair[i])==false)
      {
         RefreshRates();
         OrderSend(pair[i],bucketMode, Lot, MarketInfo(pair[i],priceMode), Slippage, NULL, NULL, NULL, 0, 0, CLR_NONE);
      }
   }
   Sleep(3000);
}
bool isAllOpen()
{
   for(int i=0;i<NUMBER_OF_CCY;i++)   
   {
      if(isPairOpen(pair[i])==false)
      {
         return(false);
      }
   }
   return(true);
}
void indicateTrend()
{
   string modeStr,entryOKStr;
   double sundayOpen, currentPrice=0, diff, pl;
   double sunOpen[NUMBER_OF_CCY];
   int buysUpCnt, sellsUpCnt;
   // double iOpen(  	string symbol, int timeframe, int shift);
   // int DayOfWeek(  	)
   // Returns the current zero-based day of the week (0-Sunday,1,2,3,4,5,6) of the last known server time.
   profitBuys=0;
   profitSells=0;
   
   if(OpenTradesEnabled==false)         
        out  = "=== OPENING NEW TRADES DISABLED ===\n";
   
   
   for(int i=0;i<NUMBER_OF_CCY;i++)   
   {
      sundayOpen = iOpen(pair[i], PERIOD_W1, 0);
      sunOpen[i] = sundayOpen;      
      if(mode[i]==OP_BUY){
          
          // add spread to open price, because it is bid price and we need ask price as reference
          sundayOpen = sundayOpen + MarketInfo(pair[i],MODE_SPREAD)*MarketInfo(pair[i],MODE_POINT);
          currentPrice = MarketInfo(pair[i],MODE_ASK);
          pl = (currentPrice - sundayOpen)/(MarketInfo(pair[i],MODE_POINT)/pipMultiplier);
          profitBuys = profitBuys + pl;
          modeStr = "BUY";
      }
      else{
          currentPrice = MarketInfo(pair[i],MODE_BID);
          pl = (sundayOpen-currentPrice)/(MarketInfo(pair[i],MODE_POINT)/pipMultiplier);
          profitSells = profitSells + pl;
          modeStr = "SELL";
      }
      orderSorted[i][0] = pl;
      orderSorted[i][1] = i;
   }
   ArraySort(orderSorted,WHOLE_ARRAY,0,MODE_DESCEND);
   buysUpCnt = 0;
   sellsUpCnt = 0;
   for(i=0;i<NUMBER_OF_CCY;i++)   
   {
      if(mode[orderSorted[i][1]]==OP_BUY)
         modeStr = "BUY";
      else
         modeStr = "SELL";
         
      // count buys/sells in the first 7 slots
      if(i<7)
      {
         if(mode[orderSorted[i][1]]==OP_BUY)
            buysUpCnt++;
         else
            sellsUpCnt++;
      }
      
      out = out + "Slot " + (i+1) + ": " + modeStr + "  " + pair[orderSorted[i][1]] + ", Weekly Open: " + 
            DoubleToStr(sunOpen[orderSorted[i][1]],MarketInfo(pair[orderSorted[i][1]],MODE_DIGITS)) +
            ", PL: " + DoubleToStr(orderSorted[i][0],0) + " pips\n";
      if(i==6)
         out = out + "-------------------------------------------------------------------------------\n";
   }   
   out = out + "-------------------------------------------------------------------------------\n";
   diff = profitBuys + profitSells;
   
   if (profitBuys > 0 && diff < 0) { trend = "Trend LONG"; bucketMode = OP_BUY;}
   if (profitBuys > 0 && diff > 0) { trend = "Trend LONG (Strong)"; bucketMode = OP_BUY;}
   if (profitBuys < 0 && diff < 0) { trend = "Trend SHORT"; bucketMode = OP_SELL;}
   if (profitBuys < 0 && diff > 0) { trend = "Trend SHORT (Strong)"; bucketMode = OP_SELL;}
   
   if(bucketMode==OP_BUY){
      if(buysUpCnt>sellsUpCnt && MathAbs(profitBuys)>MinProfitBuysToOpen)    
         entryOK = true;
      else
         entryOK = false;
   }
   else{ 
      if(sellsUpCnt>buysUpCnt && MathAbs(profitBuys)>MinProfitBuysToOpen)    
         entryOK = true;
      else
         entryOK = false;
   }   
   
   if(entryOK == false)
      entryOKStr = "No";
   else
      entryOKStr = "Yes";
 
  if(OpenTradesEnabled==false)
 
   out = out + "Profit Buy Side = " + DoubleToStr(profitBuys,0) + " pips\n" + "Profit Sell Side = " +
         DoubleToStr(profitSells,0)  + " pips\n" + "Difference = " + DoubleToStr(diff,0) + " pips\n" + trend +
         "\nEntry condition: "+entryOKStr;   
}