//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
#property strict
//------------------------------------------------------------------
#include <stdlib.mqh>

//
//
//

input bool              TrailAllSymbols    = true;           // Trail all symbols?
input bool              TrailOnlyInProfit  = false;          // Trail only orders already profitable?
input double            CloseWhenProfit    = 0.0;            // Close when profit reaches:
input bool              showMessages       = true;           // Display messages?

extern int               KcTimeFrame       = 0;              // Keltner channel stop time frame
input int                Length            = 20;             // Keltner ma period
input ENUM_MA_METHOD     MaMethod          = MODE_SMA;       // Keltner ma method
input ENUM_APPLIED_PRICE Price             = PRICE_CLOSE;    // Keltner ma price
input int                AtrPeriod         = 20;             // Range period
input double             AtrMultiplier     = 2.0;            // Range multiplier
enum  enAtrMode
      {
         atr_Rng,                                            // Calculate using range
         atr_Atr                                             // Calculate using ATR
      };
input enAtrMode          AtrMode           = atr_Rng;         // Range calculating mode
input int                KcShift           = 2;               // Keltner stop bar to use
input int                InitialStop       = 100;             // Initial stop loss
input int                magicNumberfrom   = 0;               // Magic number from
input int                magicNumberto     = 0;               // Magic number to

bool dummyResult;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

int OnInit() { return(INIT_SUCCEEDED); }
void OnDeinit(const int reason)
{
   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);
   }
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
      
void OnTick()
{
   if (!CheckTerminalStatus()) return; showTwoStateMessage("working","Keltner channel 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 = pow(10,fmod(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        = buyValue(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        = sellValue(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; 
            }                     
      }
   }   
}

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 buyValue(string symbol)
{
   double kcbuy  = iCustom(symbol,KcTimeFrame,"keltner bands stop",Length,MaMethod,Price,AtrPeriod,AtrMultiplier,AtrMode,0,KcShift);
   return(kcbuy);
}

double sellValue(string symbol)
{
   double kcsell = iCustom(symbol,KcTimeFrame,"keltner bands stop",Length,MaMethod,Price,AtrPeriod,AtrMultiplier,AtrMode,1,KcShift);
   return(kcsell);
}

//----------------------------------------------------------------------------------------
//       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();
}