//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
#property strict
//------------------------------------------------------------------
#include <stdlib.mqh>

//
//
//

enum ENUM_PRICE
{
   close,               // Close
   open,                // Open
   high,                // High
   low,                 // Low
   median,              // Median
   typical,             // Typical
   weightedClose,       // Weighted Close
   medianBody,          // Median Body (Open+Close)/2
   average,             // Average (High+Low+Open+Close)/4
   trendBiased,         // Trend Biased
   trendBiasedExt,      // Trend Biased(extreme)
   demark,              // Demark Weighted Price
   wmedianBody,         // Weighted Median Body
   haClose,             // HA Close
   haOpen,              // HA Open
   haHigh,              // HA High   
   haLow,               // HA Low
   haMedian,            // HA Median
   haTypical,           // HA Typical
   haWeighted,          // HA Weighted Close
   haMedianBody,        // HA Median Body
   haAverage,           // HA Average
   haTrendBiased,       // HA Trend Biased
   haTrendBiasedExt,    // HA Trend Biased(extreme)   
   haDemark,            // HA Demark Weighted Price
   hawmedianBody        // HA Weighted Median Body
};

enum ENUM_ATRMODE
{
   atr,                 // ATR
   watr                 // WATR
}; 

input bool                    TrailAllSymbols    =            false;    // 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                    SmTimeFrame        =            0;       //  Step ma time frame
input string                  FastMA             = "=== Fast StepMA ===";
input ENUM_PRICE              FastPrice          =           0;       // Fast StepMA Price
input int                     FastLength         =           5;       // Fast StepMA Period
input double                  FastStepSize       =           0;       // Fast Step Size in pips 
input double                  FastMultiplier     =           2;       // Fast Volatility's Factor or Multiplier
input double                  FastMinStep        =           0;       // Fast Min Step in pips 
input ENUM_ATRMODE            FastATRMode        =           0;       // Fast ATR Mode:0-ATR,1-WATR  
input bool                    FastUseHighLow     =       false;       // Use High/Low for Fast StepMA 
input int                     FastShift          =           0;       // Fast MA Displace
input string                  SlowMA             = "=== Slow StepMA ===";
input ENUM_PRICE              SlowPrice          =           0;       // Slow StepMA Price
input int                     SlowLength         =           5;       // Slow StepMA Period
input double                  SlowStepSize       =           0;       // Slow Step Size in pips 
input double                  SlowMultiplier     =           4;       // Slow Volatility's Factor or Multiplier
input double                  SlowMinStep        =           0;       // Slow Min Step in pips 
input ENUM_ATRMODE            SlowATRMode        =           0;       // Slow ATR Mode:0-ATR,1-WATR  
input bool                    SlowUseHighLow     =       false;       // Use High/Low for Slow StepMA 
input int                     SlowShift          =           0;       // Slow MA Displace
input string                  TrendMA            = "=== Trend StepMA ===";
input bool                    UseTrendStepMA     =        true;       // Use Trend StepMA      
input ENUM_PRICE              TrendPrice         =           0;       // Trend StepMA Price
input int                     TrendLength        =           5;       // Trend StepMA Period
input double                  TrendStepSize      =           0;       // Trend Step Size in pips 
input double                  TrendMultiplier    =           8;       // Trend Volatility's Factor or Multiplier
input double                  TrendMinStep       =           0;       // Trend Min Step in pips 
input ENUM_ATRMODE            TrendATRMode       =           0;       // Trend ATR Mode:0-ATR,1-WATR  
input bool                    TrendUseHighLow    =       false;       // Use High/Low for Trend StepMA 
input int                     TrendShift         =           0;       // Trend MA Displace
input double                  PointMultiplier    =           0;       // Point Multiplier
input int                     CountBars          =           0;       // Number of bars counted: 0-all bars   
input int                     SmShift            =           2;       // StepMa bar to use

input int                     InitialStop        =           100;     // Initial stop loss
input int                     magicNumberfrom    =           0;       // Magic number from
input int                     magicNumberto      =           0;       // Magic number to

struct sGloStruct
{
   int      digits,err,c;
   double   point,PointRatio,totalProfit;
   bool     dummyResult;
   datetime startTime;
};
sGloStruct glo;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

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","Step Ma cross trailing EA working",true);

   //
   //
   //

   glo.startTime   = TimeCurrent();
   glo.totalProfit = 0;
   
   //
   //
   //

   for (int i=OrdersTotal()-1; i>=0; i--)
   { 
      glo.dummyResult = OrderSelect(i, SELECT_BY_POS,MODE_TRADES);
      if (!TrailAllSymbols)
         if (OrderSymbol()!=_Symbol)               continue;
         if (OrderMagicNumber() < magicNumberfrom) continue;
         if (OrderMagicNumber() > magicNumberto)   continue;
      RefreshRates();
      
      //
      //
      //
         
         glo.digits     = (int)MarketInfo(OrderSymbol(),MODE_DIGITS);
         glo.point      = MarketInfo(OrderSymbol(),MODE_POINT);
         glo.PointRatio = pow(10,fmod(glo.digits,2));
      
      //
      //
      //

      if (OrderType()==OP_BUY) 
      {
         glo.totalProfit += OrderProfit();
         double buyStop,currentBuyStop;
         double bid        = MarketInfo(OrderSymbol(),MODE_BID);
         double maxBuyStop = NormalizeDouble(bid-MarketInfo(OrderSymbol(),MODE_STOPLEVEL)*glo.point,glo.digits);
            if (OrderStopLoss()==0 && InitialStop > 0)
            {
               buyStop        = bid-InitialStop*glo.point*glo.PointRatio;
               currentBuyStop = buyStop-glo.point;
            }
            else
            {             
               buyStop        = buyValue(OrderSymbol());
               currentBuyStop = OrderStopLoss(); if(currentBuyStop == 0) currentBuyStop = OrderOpenPrice();
               currentBuyStop = fmax(currentBuyStop,OrderOpenPrice());
            }                                          
            buyStop        = NormalizeDouble(buyStop       ,glo.digits);
            currentBuyStop = NormalizeDouble(currentBuyStop,glo.digits);
         
            //
            //
            //

          bool doModifyBuy = (buyStop > NormalizeDouble(OrderStopLoss(),glo.digits)); if (TrailOnlyInProfit) doModifyBuy = (buyStop > currentBuyStop);
          if ( doModifyBuy && (buyStop < maxBuyStop))
          for(glo.c=0 ; glo.c<3; glo.c++)
          {
             glo.dummyResult = OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(buyStop,glo.digits),OrderTakeProfit(),0,CLR_NONE);
             glo.err = GetLastError();
                checkError(OrderSymbol()+" buy stop loss set to "+DoubleToStr(buyStop,glo.digits),glo.err);
                if(glo.err==4 || glo.err==136 || glo.err==137 || glo.err==138 || glo.err==146)
                {
                   RefreshRates();
                   continue;
                }
            break; 
         }                  
      }   

      //
      //
      //
      
      if (OrderType()==OP_SELL)
      {
         glo.totalProfit += OrderProfit();
         double sellStop,currentSellStop;
         double ask         = MarketInfo(OrderSymbol(),MODE_ASK);
         double minSellStop = NormalizeDouble(ask+MarketInfo(OrderSymbol(),MODE_STOPLEVEL)*glo.point,glo.digits);
            if (OrderStopLoss()==0 && InitialStop > 0)
            {
               sellStop        = ask+InitialStop*glo.point*glo.PointRatio;
               currentSellStop = sellStop+glo.point;
            }
            else
            {             
               sellStop        = sellValue(OrderSymbol()); if (sellStop==EMPTY_VALUE) sellStop=0;
               currentSellStop = OrderStopLoss(); if(currentSellStop == 0) currentSellStop = OrderOpenPrice();
               currentSellStop = fmin(currentSellStop,OrderOpenPrice());
           }                                          
           sellStop        = NormalizeDouble(sellStop       ,glo.digits);
           currentSellStop = NormalizeDouble(currentSellStop,glo.digits);
         
           //
           //
          //
            
          bool doModifySell = (sellStop<NormalizeDouble(OrderStopLoss(),glo.digits) || OrderStopLoss()==0); if (TrailOnlyInProfit) doModifySell = (sellStop<currentSellStop);
          if ( doModifySell && (sellStop > minSellStop))
          for(glo.c=0 ; glo.c<3; glo.c++)
          {
            glo.dummyResult = OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(sellStop,glo.digits),OrderTakeProfit(),0,CLR_NONE);
            glo.err = GetLastError();
               checkError(OrderSymbol()+" sell stop loss set to "+DoubleToStr(sellStop,glo.digits),glo.err);
               if(glo.err==4 || glo.err==136 || glo.err==137 || glo.err==138 || glo.err==146)
               {
                  RefreshRates();
                  continue;
               }
            break; 
         }                     
      }
   }
   
   //
   //
   //
   
   if (CloseWhenProfit>0)
   {
      if ((TimeCurrent()-glo.startTime)>15) glo.totalProfit = colectProfit();
      
      //
      //
      //
      
      if (CloseWhenProfit<glo.totalProfit)
      for (int i=OrdersTotal()-1; i>=0; i--)
      { 
         glo.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(glo.c=0 ; glo.c<3; glo.c++)
            {
               glo.dummyResult = OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,CLR_NONE);
                glo.err = GetLastError();
                  checkError(OrderSymbol()+" order closed",glo.err);
                  if(glo.err==4 || glo.err==136 || glo.err==137 || glo.err==138 || glo.err==146)
                  {
                     RefreshRates();
                     continue;
                  }
                  break; 
            }                     
      }
   }   
}

//
//
//

double colectProfit()
{
   double profitSoFar=0;
   
   //
   //
   //
   //
   //
   
   for (int i=OrdersTotal()-1; i>=0; i--)
   { 
      glo.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 smbuy = iCustom(symbol,SmTimeFrame,"StepMACrossover_v3.4ea 600+","",FastPrice,FastLength,FastStepSize,FastMultiplier,FastMinStep,FastATRMode,FastUseHighLow,FastShift,"",SlowPrice,SlowLength,SlowStepSize,SlowMultiplier,SlowMinStep,SlowATRMode,SlowUseHighLow,SlowShift,
                  "",UseTrendStepMA,TrendPrice,TrendLength,TrendStepSize,TrendMultiplier,TrendMinStep,TrendATRMode,TrendUseHighLow,TrendShift,PointMultiplier,CountBars,3,SmShift);   
return(smbuy);
}

double sellValue(string symbol)
{
   double smsell = iCustom(symbol,SmTimeFrame,"StepMACrossover_v3.4ea 600+","",FastPrice,FastLength,FastStepSize,FastMultiplier,FastMinStep,FastATRMode,FastUseHighLow,FastShift,"",SlowPrice,SlowLength,SlowStepSize,SlowMultiplier,SlowMinStep,SlowATRMode,SlowUseHighLow,SlowShift,
                   "",UseTrendStepMA,TrendPrice,TrendLength,TrendStepSize,TrendMultiplier,TrendMinStep,TrendATRMode,TrendUseHighLow,TrendShift,PointMultiplier,CountBars,4,SmShift);   
return(smsell);
}

//----------------------------------------------------------------------------------------
//       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();
}