//+------------------------------------------------------------------+
//|                                             Compuforex_Trail.mq4 |
//|                            Copyright © 2010, www.compu-forex.com |
//|                                              www.compu-forex.com |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2010, www.compu-forex.com"
#property link      "www.compu-forex.com"

//---- input parameters
extern int        MagicNumber    = -1;
//extern bool       Enable_Trade    = false;
extern bool       Own_Symbol_Only = false;
extern string     Note1          = "Trail_Start $ Trail_Max are pip values";
extern double     Trail_Start    = 20;
extern double     Trail_Max      = 50;
extern string     Note2          = "Bigger Trail % = longer trail";
extern string     Note3          = "Trail_Percent & Trail_Min are % values";
extern double     Trail_Percent  = 50;
extern double     Trail_Min      = 5;
extern bool       Use_Decay      = true;
extern string     Note4          = "Decay rate reduces Trail_Percent per bar";
extern double     Decay_Rate     = 1;
extern bool       Exp_Decay      = true;
extern int        Wait_Bars      = 4;
extern double     Min_Mod        = 0.5;
extern bool       Audio_Alert    = true;

int
   D_Factor = 1,
   Trade_Info[][2];
double   
   Stop_Level;
color
   Text_Color = White;
string
   Orders_Filename;


//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
   if(Digits==3||Digits==5){D_Factor=10;}
//----
   Orders_Filename = StringConcatenate(WindowExpertName(),"_Ordersfile.csv");
   int Loaded_Orders = Load_Orders();
   if(!Loaded_Orders){
      Create_OrderState();
      int Num_Orders_Saved = Save_Orders();
      Print("Order matrix re-created...." + Num_Orders_Saved + " orders saved to file.");
   }
   else Print("Orders file loaded....." + Loaded_Orders + " orders found.");
   Permanent_Display();
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   ObjectsDeleteAll();
   Save_Orders();
   if(IsTesting())FileDelete(Orders_Filename);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
//------------------------------------------------------------------- simple trade to enable testing
/*  if(Enable_Trade){
      if (OrdersTotal() < 2){
         OrderSend(Symbol(), 0, 0.1, Ask, 3, 0 ,0 ,0, MagicNumber, 0,Lime);
         OrderSend(Symbol(), 1, 0.1, Bid, 3, 0 ,0 ,0, MagicNumber, 0,Red);
      }
   }*/
//-------------------------------------------------------------------
bool
   mod,
   Profit_Order;
double
   l_Trail_Start,
   l_Trail_Max,
   l_Min_Mod,
   My_Profit,
   My_Trail,
   My_SL,
   l_Trail_Percent,
   Pip;
int
   Bar_Shift;

//----
   for (int i=OrdersTotal()-1;i>=0;i--){
      if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)){
         if(Own_Symbol_Only){
            if(OrderSymbol()!=Symbol())continue;
         }
         Pip = MarketInfo(OrderSymbol(),MODE_POINT)*D_Factor;
         mod=true;
         l_Trail_Start  = Trail_Start * Pip;
         l_Trail_Max    = Trail_Max * Pip;
         l_Min_Mod      = Min_Mod * Pip;
         Stop_Level = MarketInfo(Symbol(),MODE_STOPLEVEL)/D_Factor;
         Stop_Level *= Pip;
         if(OrderMagicNumber()==MagicNumber||MagicNumber==-1){
            l_Trail_Percent = Get_Trail_Percent(OrderTicket(),OrderSymbol());
            RefreshRates();
            switch(OrderType()){
               case OP_BUY :  My_Profit = OrderClosePrice() - OrderOpenPrice();
                              My_Trail = MathMin(My_Profit * (l_Trail_Percent/100),l_Trail_Max);
                              My_Trail = MathMax(My_Trail,Stop_Level);
                              My_SL = NormalizeDouble(OrderClosePrice()-My_Trail,Digits);
                              if(My_Profit > l_Trail_Start){
                                 Update_Info(OrderTicket());
                                 if(OrderStopLoss() == 0)mod = OrderModify(OrderTicket(),OrderOpenPrice(),My_SL,OrderTakeProfit(),0, CLR_NONE);
                                 else{
                                    if(My_SL-OrderStopLoss() > l_Min_Mod)mod = OrderModify(OrderTicket(),OrderOpenPrice(),My_SL,OrderTakeProfit(),0, CLR_NONE);
                                 }
                              }
                              break;
                        
               case OP_SELL : My_Profit = OrderOpenPrice() - OrderClosePrice();
                              My_Trail = MathMin(My_Profit * (l_Trail_Percent/100),l_Trail_Max);
                              My_Trail = MathMax(My_Trail,Stop_Level);
                              My_SL = NormalizeDouble(OrderClosePrice()+My_Trail,Digits);
                              if(My_Profit > l_Trail_Start){
                                 Update_Info(OrderTicket());
                                 if(OrderStopLoss() == 0)mod = OrderModify(OrderTicket(),OrderOpenPrice(),My_SL,OrderTakeProfit(),0, CLR_NONE);
                                 else{
                                    if(OrderStopLoss()-My_SL > l_Min_Mod)mod = OrderModify(OrderTicket(),OrderOpenPrice(),My_SL,OrderTakeProfit(),0,CLR_NONE);
                                 }
                              }
                              break;
            }
            if(!mod)Print("Error entering Trailing Stop - Error (" + GetLastError() + "} " );
         }
      }
      else Print("Error selecting order in Trailing_Stop function. Error = " + GetLastError());
   } 
   if(Trade_Count()>ArrayRange(Trade_Info,0))Add_Ticket();
   if(Trade_Count()<ArrayRange(Trade_Info,0))Delete_Ticket();
   Save_Orders();
   CreateTextLable("Trade_No2","Trade Status",Text_Color,80,80);
   CreateTextLable("Trade_No2_Und","______________",Text_Color,70,82);
   CreateTextLable("Trade_No","Number of Open Trades : " + Trade_Count(),Text_Color,0,100);
   if(!Trade_Count()){
      Clean_Up();
      CreateTextLable("Trade_No","No Open trades for us at the moment.........",Text_Color,0,100);
      return;
   }
   Profit_Order=false;
   for(int t = 0;t<ArrayRange(Trade_Info,0);t++){
      if(Trade_Info[t][1]>0)Profit_Order=true;
   }
   string label_name,label_name2,label_name3,label_name4;
   Clean_Up();
   if(!Profit_Order){
      CreateTextLable("Trade_No3","No trails active at the moment.......waiting",Yellow,0,120);
      return;
   }
   CreateTextLable("Trade_No3","Ticket #         Pair          Trail Start Time",Text_Color,0,120);
   CreateTextLable("Trade_No4","_______________________________________________",Text_Color,0,122);
   CreateTextLable("Trade_No5"," Trail %    Locked Pips",Text_Color,230,120);
   CreateTextLable("Trade_No6","______________________",Text_Color,240,122);
   int line_cnt=0;
   for(t = 0;t<ArrayRange(Trade_Info,0);t++){
      label_name = StringConcatenate("Trade_Show",t);
      label_name2 = StringConcatenate("Trade_Show2",t);
      label_name3 = StringConcatenate("Trade_Show3",t);
      label_name4 = StringConcatenate("Trade_Show4",t);
      if(Trade_Info[t][1]==0)continue;
      CreateTextLable(label_name,Trade_Info[t][0] + "   " + Get_Symbol(Trade_Info[t][0]) + "              " + TimeToStr(Trade_Info[t][1],TIME_MINUTES),Text_Color,0,140+(line_cnt*20));
      l_Trail_Percent = Get_Trail_Percent(OrderTicket(),OrderSymbol());
      CreateTextLable(label_name2,DoubleToStr(l_Trail_Percent,0) + "%                " + DoubleToStr(Get_Safe_Pips(Trade_Info[t][0]),1),Text_Color,240,140+(line_cnt*20));
      
      line_cnt++;
   }
   CreateTextLable("My_Pips","Total Safe Pips : " + DoubleToStr(Locked_Pips(),1),LawnGreen,0,140+(line_cnt*20));
//----

   return(0);
  }
//+------------------------------------------------------------------+
void Clean_Up()
{
string 
   label_name,
   label_name2,
   label_name3,
   label_name4;
   ObjectDelete("Trade_No3");
   ObjectDelete("Trade_No4");
   ObjectDelete("Trade_No5");
   ObjectDelete("Trade_No6");
   ObjectDelete("My_Pips");
   for(int oc = 0;oc<=ObjectsTotal(OBJ_LABEL);oc++){
      label_name = StringConcatenate("Trade_Show",oc);
      label_name2 = StringConcatenate("Trade_Show2",oc);
      label_name3 = StringConcatenate("Trade_Show3",oc);
      label_name4 = StringConcatenate("Trade_Show4",oc);
      if(ObjectFind(label_name)==-1)continue;
      ObjectDelete(label_name);
      ObjectDelete(label_name2);
   }
}
//+------------------------------------------------------------------+

int Trade_Count()
{
int
   counter = 0;
   for(int i = OrdersTotal()-1;i>=0;i--){
      OrderSelect(i,SELECT_BY_POS);
      if(Own_Symbol_Only){
         if(OrderSymbol()!=Symbol())continue;
      }
      if(OrderMagicNumber()==MagicNumber||MagicNumber==-1)counter++;
   }
   return(counter);
}
//+------------------------------------------------------------------+
void CreateTextLable(string TextLableName,string Text,color l_Text_Color,int X,int Y)
{ 
int
   TextSize = 10,
   Corner = 0;
   if(TextLableName=="Heading")TextSize = 15;
   ObjectCreate(TextLableName, OBJ_LABEL, 0, TimeCurrent(), 0);
   ObjectSet(TextLableName, OBJPROP_CORNER, Corner);
   ObjectSet(TextLableName, OBJPROP_XDISTANCE, X);
   ObjectSet(TextLableName, OBJPROP_YDISTANCE, Y);
   ObjectSetText(TextLableName,Text,TextSize,"New Times Roman",l_Text_Color);
}
//+------------------------------------------------------------------+
void Permanent_Display()
{
   Text_Color = White;
   CreateTextLable("Heading","Compuforex Trailing Stop EA",Text_Color,0,10);
   CreateTextLable("Underline1","_____________________________________",Text_Color,0,20);
   if(Own_Symbol_Only)CreateTextLable("Pairs","Managing " + Symbol() + " Trades ONLY.",Text_Color,0,40);
   else CreateTextLable("Pairs","Managing Trade on ALL pairs",Text_Color,0,40);
   if(MagicNumber==-1)CreateTextLable("Magic_No","Managing trades with ANY MagicNumber",Text_Color,0,60);
   else CreateTextLable("Magic_No","Managing trades ONLY with MagicNumber : " + MagicNumber,Text_Color,0,60);
}
//+------------------------------------------------------------------+ 
double Locked_Pips()
{
double
   Pips = 0,
   l_Pip;
   for(int i = OrdersTotal()-1;i>=0;i--){
      OrderSelect(i,SELECT_BY_POS);
      if(Own_Symbol_Only){
         if(OrderSymbol()==Symbol())continue;
      }
      l_Pip = MarketInfo(OrderSymbol(),MODE_POINT)*D_Factor;
      if(OrderMagicNumber()==MagicNumber||MagicNumber==-1){
         if(Profit_Time(OrderTicket())>0){
            if(OrderStopLoss()==0)continue;
            if(OrderType()==OP_BUY)Pips += (OrderStopLoss()-OrderOpenPrice())/l_Pip;
            if(OrderType()==OP_SELL)Pips += (OrderOpenPrice() - OrderStopLoss())/l_Pip;
         }
      }
   }
   return(Pips);
}
//+------------------------------------------------------------------+
void Add_Ticket()
{
bool
   Found_it;
int
   Array_Size = ArrayRange(Trade_Info,0),
   Temp_Array[][2];
   if(Array_Size>0){
      ArrayResize(Temp_Array,Array_Size);
      ArrayCopy(Temp_Array,Trade_Info); 
      ArrayResize(Trade_Info,Trade_Count());
      ArrayCopy(Trade_Info,Temp_Array);
   }
   for (int i = 0;i<OrdersTotal();i++){
      OrderSelect(i,SELECT_BY_POS);
      Found_it=false;
      for (int cnt = 0;cnt<ArrayRange(Trade_Info,0);cnt++){
         if(OrderTicket()==Trade_Info[cnt][0]){
            Found_it=true;
         }
      }
      if(!Found_it){
         ArrayResize(Trade_Info,ArrayRange(Trade_Info,0)+1);
         Trade_Info[ArrayRange(Trade_Info,0)-1][0] = OrderTicket();
         Trade_Info[ArrayRange(Trade_Info,0)-1][1] = 0;
      }
   }
}
//+------------------------------------------------------------------+
void Delete_Ticket()
{
   if(!Trade_Count()){
      ArrayResize(Trade_Info,0);
      return;
   }
int 
   Temp_Array[][2];
   ArrayResize(Temp_Array,Trade_Count());
   for(int y = OrdersTotal()-1;y>=0;y--){
      if(OrderSelect(y,SELECT_BY_POS)){
         if(Own_Symbol_Only){
            if(OrderSymbol()!=Symbol())continue;
         }
         if(OrderMagicNumber()==MagicNumber||MagicNumber==-1){
            for(int x = ArrayRange(Trade_Info,0)-1;x>=0;x--){
               if(OrderTicket()==Trade_Info[x][0]){
                  Temp_Array[y][0] = Trade_Info[x][0];
                  Temp_Array[y][1] = Trade_Info[x][1];
               }
            }
         }
      }
      else Print("Error selecting order in Delete_Ticket function. Error - " + GetLastError());
   }
   ArrayResize(Trade_Info,ArrayRange(Temp_Array,0));
   ArrayCopy(Trade_Info,Temp_Array);
}
//+------------------------------------------------------------------+
void Update_Info(int l_Ticket)
{
   for(int x = ArrayRange(Trade_Info,0)-1;x>=0;x--){
      if(Trade_Info[x][0]==l_Ticket){
         if(Trade_Info[x][1]==0){
            Trade_Info[x][1] = iTime(Symbol(),0,0);
            PlaySound("alert2.wav");
            return;
         }
      }
   }
   return;
}
//+------------------------------------------------------------------+
bool Create_OrderState()
{
int
   cnt=0;
   ArrayResize(Trade_Info,Trade_Count());
   for (int i = OrdersTotal()-1;i>=0;i--){
      if(!OrderSelect(i,SELECT_BY_POS))return(true);
      if(OrderSymbol()==Symbol()){
         if(OrderMagicNumber()==MagicNumber||MagicNumber==-1){
            Trade_Info[i][0]=OrderTicket();
            Trade_Info[i][1]=0;
         }
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
int Load_Orders()
{
bool
   Order_OK;
int
   Temp_Orders[][2],
   handle=FileOpen(Orders_Filename, FILE_BIN|FILE_READ);
   if(handle<0){
      Print("No previous records exist.......");
      return(0);
   }
   ArrayResize(Temp_Orders,OrdersTotal());
   ArrayResize(Trade_Info,FileSize(handle)/4);
   int Loaded_Orders = FileReadArray(handle,Trade_Info,0,FileSize(handle)/4);
   FileClose(handle);
   Print("Previous Order File found.......verifying integrity.");
   if(!OrdersTotal()){
      ArrayResize(Trade_Info,0);
      Print("No trades exist.....Order File discarded.");
      return(0);
   }
   for(int i=0;i<OrdersTotal();i++){
      Order_OK=false;
      OrderSelect(i,SELECT_BY_POS);
      for(int x=0;x<ArrayRange(Trade_Info,0);x++){
         if(OrderTicket()==Trade_Info[x][0]){
            Temp_Orders[i][0]=Trade_Info[x][0];
            Temp_Orders[i][1]=Trade_Info[x][1];
            Order_OK=true;
         }
      }
      if(!Order_OK){
         Temp_Orders[i][0]=OrderTicket();
         Temp_Orders[i][1]=0;
         Print("New order discovered....added to file.");
      }
   }
   ArrayResize(Trade_Info,ArrayRange(Temp_Orders,0));
   ArrayCopy(Trade_Info,Temp_Orders);
   return(ArrayRange(Trade_Info,0));
}
//+------------------------------------------------------------------+
int Save_Orders()
{
int
   handle=FileOpen(Orders_Filename, FILE_BIN|FILE_WRITE),
   Num_Orders = FileWriteArray(handle,Trade_Info,0,ArraySize(Trade_Info));
   FileClose(handle);
   return(Num_Orders/2);
}
//+------------------------------------------------------------------+
int Profit_Time(int l_Ticket)
{
   for(int y = ArrayRange(Trade_Info,0)-1;y>=0;y--){
      if(Trade_Info[y][0]==l_Ticket)return(Trade_Info[y][1]);
   }
   return(0);
}
//+------------------------------------------------------------------+
double Get_Safe_Pips(int l_Ticket)
{
   OrderSelect(l_Ticket,SELECT_BY_TICKET);
   if(OrderStopLoss()==0)return(0);
   double l_Pip = MarketInfo(OrderSymbol(),MODE_POINT)*D_Factor;
   return(MathAbs(OrderOpenPrice()-OrderStopLoss())/l_Pip);
}
//+------------------------------------------------------------------+
int Get_Trail_Percent(int l_Ticket, string l_Pair)
{
double
   l_Trail_Percent;
int
   Bar_Shift;
   if(Profit_Time(l_Ticket)>0)Bar_Shift = iBarShift(l_Pair,0,Profit_Time(l_Ticket));
   else Bar_Shift=0;
   if(Use_Decay){
      if(Bar_Shift>=Wait_Bars){
         Bar_Shift = Bar_Shift - Wait_Bars;
         if(Exp_Decay)Decay_Rate = Bar_Shift;
         l_Trail_Percent = Trail_Percent - (Bar_Shift * Decay_Rate);
      }
      else l_Trail_Percent = Trail_Percent;
   }
   else l_Trail_Percent = Trail_Percent;
   l_Trail_Percent = MathMax(l_Trail_Percent,Trail_Min);
   return(l_Trail_Percent);
}
//+------------------------------------------------------------------+
string Get_Symbol(int l_Ticket)
{
   OrderSelect(l_Ticket, SELECT_BY_TICKET);
   return(StringSubstr(OrderSymbol(),0,6));
}
//+------------------------------------------------------------------+


