//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
#property strict
//------------------------------------------------------------------
#include <stdlib.mqh>

enum enPrices
{
   pr_close,      // Close
   pr_open,       // Open
   pr_high,       // High
   pr_low,        // Low
   pr_median,     // Median
   pr_typical,    // Typical
   pr_weighted,   // Weighted
   pr_average,    // Average (high+low+open+close)/4
   pr_medianb,    // Average median body (open+close)/2
   pr_tbiased,    // Trend biased price
   pr_tbiased2,   // Trend biased (extreme) price
   pr_haclose,    // Heiken ashi close
   pr_haopen ,    // Heiken ashi open
   pr_hahigh,     // Heiken ashi high
   pr_halow,      // Heiken ashi low
   pr_hamedian,   // Heiken ashi median
   pr_hatypical,  // Heiken ashi typical
   pr_haweighted, // Heiken ashi weighted
   pr_haaverage,  // Heiken ashi average
   pr_hamedianb,  // Heiken ashi median body
   pr_hatbiased,  // Heiken ashi trend biased price
   pr_hatbiased2, // Heiken ashi trend biased (extreme) price
   pr_habclose,   // Heiken ashi (better formula) close
   pr_habopen ,   // Heiken ashi (better formula) open
   pr_habhigh,    // Heiken ashi (better formula) high
   pr_hablow,     // Heiken ashi (better formula) low
   pr_habmedian,  // Heiken ashi (better formula) median
   pr_habtypical, // Heiken ashi (better formula) typical
   pr_habweighted,// Heiken ashi (better formula) weighted
   pr_habaverage, // Heiken ashi (better formula) average
   pr_habmedianb, // Heiken ashi (better formula) median body
   pr_habtbiased, // Heiken ashi (better formula) trend biased price
   pr_habtbiased2 // Heiken ashi (better formula) trend biased (extreme) price
};
enum enMaTypes
{
   ma_sma,     // Simple moving average
   ma_ema,     // Exponential moving average
   ma_smma,    // Smoothed MA
   ma_lwma,    // Linear weighted MA
   ma_slwma,   // Smoothed LWMA
   ma_dsema,   // Double Smoothed Exponential average
   ma_tema,    // Triple exponential moving average - TEMA
   ma_lsma     // Linear regression value (lsma)
};

extern bool               TrailAllSymbols      = true;       // Trail all symbols?
extern bool               TrailOnlyInProfit    = false;      // Trail only orders already profitable?
extern double             CloseWhenProfit      = 0.0;        // Close when profit reaches:
extern bool               showMessages         = true;       // Display messages?

extern int                BBTimeFrame          = 0;          // Bband stop time frame
extern int                BandsPeriod          = 20;         // Bands period
extern double             BandsDeviation       = 1;          // Bands deviation
extern bool               BandsDeviationSample = false;      // Bands deviation with sample correction?
extern double             BandsRisk            = 1;          // Bands risk
extern enMaTypes          BandsMaType          = ma_sma;     // Bands average type
extern enPrices           Price                = pr_close;   // Bands price
extern int                BBShift              = 2;          // Bands stop bar to use
extern int                InitialStop          = 100;        // Initial stop loss
extern int                magicNumberfrom      =  0;         // Magic number from
extern int                magicNumberto        =  0;         // Magic number to

bool dummyResult;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()   { return(0); }
int deinit()
{
   switch(UninitializeReason())
   {
      case REASON_CHARTCHANGE:
      case REASON_PARAMETERS:  break;
      case REASON_RECOMPILE:
      case REASON_CHARTCLOSE:
      case REASON_REMOVE:
      case REASON_ACCOUNT:     if (showMessages) for(int i=0; i<10; i++)  ObjectDelete("msg.que"+(string)i);
   }
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//
      
int start()
{
   if (!CheckTerminalStatus()) return(0); showTwoStateMessage("working","BBand Stop trailing EA working",true);

   //
   //
   //
   //
   //

   datetime startTime   = TimeCurrent();
   double   totalProfit = 0;
   int      err,c;
   
   //
   //
   //
   //
   //

   for (int i=OrdersTotal()-1; i>=0; i--)
   { 
      dummyResult = OrderSelect(i, SELECT_BY_POS,MODE_TRADES);
      if (!TrailAllSymbols)
         if (OrderSymbol()!=Symbol())              continue;
         if (OrderMagicNumber() < magicNumberfrom) continue;
         if (OrderMagicNumber() > magicNumberto)   continue;
      RefreshRates();
      //
      //
      //
      //
      //
         
         int    digits     = (int)MarketInfo(OrderSymbol(),MODE_DIGITS);
         double point      = MarketInfo(OrderSymbol(),MODE_POINT);
         double PointRatio = MathPow(10,MathMod(digits,2));
      
      //
      //
      //
      //
      //

      
      if (OrderType()==OP_BUY) 
      {
         totalProfit += OrderProfit();
         double buyStop,currentBuyStop;
         double bid        = MarketInfo(OrderSymbol(),MODE_BID);
         double maxBuyStop = NormalizeDouble(bid-MarketInfo(OrderSymbol(),MODE_STOPLEVEL)*point,digits);
            if (OrderStopLoss()==0 && InitialStop > 0)
               {
                  buyStop        = bid-InitialStop*point*PointRatio;
                  currentBuyStop = buyStop-point;
               }
            else
               {             
                  buyStop        = BBbuyValue(OrderSymbol());
                  currentBuyStop = OrderStopLoss(); if(currentBuyStop == 0) currentBuyStop = OrderOpenPrice();
                  currentBuyStop = fmax(currentBuyStop,OrderOpenPrice());
               }                                          
         buyStop        = NormalizeDouble(buyStop       ,digits);
         currentBuyStop = NormalizeDouble(currentBuyStop,digits);
         
         //
         //
         //
         //
         //

          bool doModifyBuy = (buyStop > NormalizeDouble(OrderStopLoss(),digits)); if (TrailOnlyInProfit) doModifyBuy = (buyStop > currentBuyStop);
          if ( doModifyBuy && (buyStop < maxBuyStop))
          for(c=0 ; c<3; c++)
          {
             dummyResult = OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(buyStop,Digits),OrderTakeProfit(),0,CLR_NONE);
                err=GetLastError();
                checkError(OrderSymbol()+" buy stop loss set to "+DoubleToStr(buyStop,digits),err);
                if(err==4 || err==136 || err==137 || err==138 || err==146)
                {
                   RefreshRates();
                   continue;
                }
            break; 
         }                  
      }   


      //
      //
      //
      //
      //
      
      if (OrderType()==OP_SELL)
      {
         totalProfit += OrderProfit();
         double sellStop,currentSellStop;
         double ask         = MarketInfo(OrderSymbol(),MODE_ASK);
         double minSellStop = NormalizeDouble(ask+MarketInfo(OrderSymbol(),MODE_STOPLEVEL)*point,digits);
            if (OrderStopLoss()==0 && InitialStop > 0)
               {
                  sellStop        = ask+InitialStop*point*PointRatio;
                  currentSellStop = sellStop+point;
               }
            else
               {             
                  sellStop        = BBsellValue(OrderSymbol()); if (sellStop==EMPTY_VALUE) sellStop=0;
                  currentSellStop = OrderStopLoss(); if(currentSellStop == 0) currentSellStop = OrderOpenPrice();
                  currentSellStop = MathMin(currentSellStop,OrderOpenPrice());
               }                                          
         sellStop        = NormalizeDouble(sellStop       ,digits);
         currentSellStop = NormalizeDouble(currentSellStop,digits);
         
         //
         //
         //
         //
         //
            
         bool doModifySell = (sellStop < NormalizeDouble(OrderStopLoss(),digits) || OrderStopLoss()==0); if (TrailOnlyInProfit) doModifySell = (sellStop < currentSellStop);
         if ( doModifySell && (sellStop > minSellStop))
         for(c=0 ; c<3; c++)
         {
            dummyResult = OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(sellStop,digits),OrderTakeProfit(),0,CLR_NONE);
               err=GetLastError();
               checkError(OrderSymbol()+" sell stop loss set to "+DoubleToStr(sellStop,digits),err);
               if(err==4 || err==136 || err==137 || err==138 || err==146)
               {
                  RefreshRates();
                  continue;
               }
            break; 
         }                     
      }
   }
   
   //
   //
   //
   //
   //
   
   if (CloseWhenProfit>0)
   {
      if ((TimeCurrent()-startTime)>15) totalProfit = colectProfit();
      
      //
      //
      //
      //
      //
      
      if (CloseWhenProfit<totalProfit)
      for (int i=OrdersTotal()-1; i>=0; i--)
      { 
         dummyResult = OrderSelect(i, SELECT_BY_POS,MODE_TRADES);
         if (!TrailAllSymbols)
            if (OrderSymbol()!=Symbol())              continue;
            if (OrderMagicNumber() < magicNumberfrom) continue;
            if (OrderMagicNumber() > magicNumberto)   continue;
      
            //
            //
            //
            //
            //
            
            if (OrderType()==OP_BUY || OrderType()==OP_SELL)
            for(c=0 ; c<3; c++)
            {
               dummyResult = OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,CLR_NONE);
                  err=GetLastError();
                  checkError(OrderSymbol()+" order closed",err);
                  if(err==4 || err==136 || err==137 || err==138 || err==146)
                  {
                     RefreshRates();
                     continue;
                  }
                  break; 
            }                     
      }
   }
   return(0);      
}

double colectProfit()
{
   double profitSoFar=0;
   
   //
   //
   //
   //
   //
   
   for (int i=OrdersTotal()-1; i>=0; i--)
   { 
      dummyResult = OrderSelect(i, SELECT_BY_POS,MODE_TRADES);
      if (!TrailAllSymbols)
         if (OrderSymbol()!=Symbol())               continue;
         if (OrderMagicNumber() < magicNumberfrom) continue;
         if (OrderMagicNumber() > magicNumberto)   continue;
         if (OrderType()==OP_BUY || OrderType()==OP_SELL)
               profitSoFar += (OrderProfit()+OrderSwap()+OrderCommission());
   }         
   return(profitSoFar);
}

//
//
//
//
//

double BBbuyValue(string symbol)
{
   double BBbuy  = iCustom(symbol,BBTimeFrame,"BB stops (new format) 1.4",PERIOD_CURRENT,BandsPeriod,BandsDeviation,BandsDeviationSample,BandsRisk,BandsMaType,Price,3,BBShift);
   return(BBbuy);
}

double BBsellValue(string symbol)
{
   double BBsell = iCustom(symbol,BBTimeFrame,"BB stops (new format) 1.4",PERIOD_CURRENT,BandsPeriod,BandsDeviation,BandsDeviationSample,BandsRisk,BandsMaType,Price,4,BBShift);
   return(BBsell);
}

//----------------------------------------------------------------------------------------
//       terminal status handling
//----------------------------------------------------------------------------------------
//
//
//
//
//

bool CheckTerminalStatus()
{
   bool status=false;
   
   //
   //
   //
   //
   //
   
   while(true)
   {
         if (!IsConnected())
             { showTwoStateMessage("connected","No connection to server",false); break; }
         else  showTwoStateMessage("connected","Conected to server",true);
         if ( IsStopped())
             { showTwoStateMessage("stopped","EA stopped",false); break; }  
         else  showTwoStateMessage("stopped","EA started",true);
         if (!IsTradeAllowed())
             { showTwoStateMessage("allowed","Trading not allowed",false); break; }
         else  showTwoStateMessage("allowed","Trading allowed",true);
         if (!IsExpertEnabled() && !IsTesting())
            { showTwoStateMessage("disabled","EA''s are disabled",false); break; }
         else showTwoStateMessage("disabled","EA''s are enabled",true);
         if ( IsTradeContextBusy())
            { showTwoStateMessage("busy","Trade context busy",false); break; }
         else showTwoStateMessage("busy","Trade context ready",true);
         
      //
      //
      //
      //
      //
      
      status=true;
      break;
   }
   return(status);
}

//----------------------------------------------------------------------------------------
//       messages handling
//----------------------------------------------------------------------------------------
//
//
//
//
//
//

bool   msgerrState[];
string msgerrNames[];
string msgmessageText[10];
color  msgmessageColor[10];
int    msglastMessage=0;

//
//
//
//
//

void checkError(string what,int err)
{
   showMessage(what);
   if(err!=0)
         showMessage("error occured :"+ErrorDescription(err),Red);

}

//
//
//
//
//

void showTwoStateMessage(string name, string message, bool state)
{
   int i=ArraySize(msgerrNames)-1; for(; i>-1; i--) if (msgerrNames[i]==name) break;
   if (i==-1)
      {
         int size = ArraySize(msgerrNames)+1;
         i = size-1;
            ArrayResize(msgerrNames,size); msgerrNames[i] = name;
            ArrayResize(msgerrState,size); msgerrState[i] = -1;
      }

   //
   //
   //
   //
   //

   if (msgerrState[i]!= state)
   {   
      msgerrState[i] = state;
      if (state==false)
            showMessage(message,Red);
      else  showMessage(message,Green);
   }      
}

//
//
//
//
//

void showMessage(string text, color theColor=Gray)
{
   if(!showMessages) { Print(text); return; }
   if(msglastMessage>9)
   {
      for(int i=0; i<9; i++)
      {
         msgmessageText[i] =msgmessageText[i+1];
         msgmessageColor[i]=msgmessageColor[i+1];
      }
      msglastMessage = 9;
   }

   //
   //
   //    set message que text and color
   //
   //
   
      msgmessageText[msglastMessage]  = text+" - "+TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS);
      msgmessageColor[msglastMessage] = theColor;
   
   //
   //
   //
   //
   //
      
   for(int i=0; i<=msglastMessage; i++)
   {
      string name = "msg.que"+(string)i;
      if (ObjectFind(name) == -1)
      {
         ObjectCreate(name,OBJ_LABEL,0,0,0);
            ObjectSet(name,OBJPROP_CORNER  ,3);
            ObjectSet(name,OBJPROP_XDISTANCE,5);
            ObjectSet(name,OBJPROP_YDISTANCE,5+14*i);
      }
      ObjectSetText(name,msgmessageText[i],9,"Arial",msgmessageColor[i]);
   }
   msglastMessage++;
   WindowRedraw();
}