//+------------------------------------------------------------------+
//|                                     FxCraftVisualOrderEditor.mq4 |
//|                                           Copyright 2013 FxCraft |
//|                                  licence: Creative Commons BY-SA |
//|                                 available on http://fxcraft.biz/ |
//+------------------------------------------------------------------+

#include <stderror.mqh>
#include <stdlib.mqh>

extern bool   use_timer             = true      ;
extern bool   delete_on_deinit      = true      ;

extern string ________STOP_LOSS                 ;
extern int    default_sl_level      = 20        ;
extern int    default_trailing_stop = 0         ;
extern color  sl_color              = Orange    ;
extern int    sl_style              = STYLE_DASH;

extern string ________TAKE_PROFIT               ;
extern int    default_tp_level      = 120       ;
extern color  tp_color              = DarkGray  ;
extern int    tp_style              = STYLE_DASH;

extern string ________BREAK_EVEN                ;
extern bool   use_be                = true      ;
extern int    default_be_level      = 15        ;
extern int    be_offset             = 3         ;
extern color  be_color              = Brown     ;
extern int    be_style              = STYLE_DASH;

extern string ________CANCEL_LEVEL              ;
extern bool   use_cl                = false     ;
extern int    default_cl_level      = 100       ;
extern color  cl_color              = Purple    ;
extern int    cl_style              = STYLE_DASH;

extern string ________CLOSE_PART                ;
extern bool   use_cp                = false     ;
extern bool   cp_size_or_percent    = false     ;
extern string cp_levels             = "10,15,20";
extern string cp_lots               = "25,50,90";
extern color  cp_color              = Pink      ;
extern int    cp_style              = STYLE_DASH;
double cp_lvl[]   ;
double cp_lts[]   ;
int    cp_size = 0;

extern string ________OPEN_LEVEL                ;
extern bool   showOLforOpenOrders   = false     ;//
extern color  ol_sell_color         = Red       ;
extern int    ol_sell_style         = STYLE_DASH;
extern color  ol_buy_color          = Blue      ;
extern int    ol_buy_style          = STYLE_DASH;

void init()
{
   if(use_cp)
      cp_size = MathMin(listToTab(cp_levels,cp_lvl),listToTab(cp_lots,cp_lts));
   
   if(use_timer)  
      timer();
}

void timer()
{
   while(!IsStopped())
   {
      Sleep(500);
      
      if(IsExpertEnabled())
         start();
   }
}

void deinit()
{
   if(delete_on_deinit)
   {
      for(int x=0; x<10; x++) for(int i=0; i<ObjectsTotal(); i++)
      {
         string name=ObjectName(i);
         
         if(StringSubstr(name,0,4)=="fvoe")
            ObjectDelete(name);
      }
   }
}

void start()
{
   RefreshRates();
   
   for(int i=0; i<OrdersTotal(); i++)
   {
      if(OrderSelect(i,SELECT_BY_POS))
      {
         if(OrderSymbol()==Symbol())
         {
            double point   = MarketInfo(Symbol(),MODE_POINT );
            int    dgts    = MarketInfo(Symbol(),MODE_DIGITS);
            int    oDir;
            double BidAsk;
            
            int oType   = OrderType();
            int oTicket = OrderTicket();
            
            double oOpenPrice    = OrderOpenPrice();
            double oStopLoss     = OrderStopLoss();
            double oTakeProfit   = OrderTakeProfit();
            double oLots         = OrderLots();
            
            datetime oExpiration = OrderExpiration();
            
            string oComment      = OrderComment();
            
            if(oType%2) //sell
            {
               oDir   = -1;
               BidAsk = MarketInfo(Symbol(),MODE_ASK);
            }
            else              //buy
            {
               oDir   =  1;
               BidAsk = MarketInfo(Symbol(),MODE_BID);            
            }
            
            if(-1 == ObjectFind("fvoe_ol_" + oTicket))
            {
               if(oType==OP_SELLLIMIT || oType==OP_SELLSTOP)
               {
                  ObjectCreate("fvoe_ol_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice);
                  ObjectSet("fvoe_ol_" + oTicket,OBJPROP_COLOR,ol_sell_color);
                  ObjectSet("fvoe_ol_" + oTicket,OBJPROP_STYLE,ol_sell_style);
                  ObjectSetText("fvoe_ol_" + oTicket,
                     StringConcatenate("#",oTicket," ",orderName( oType )," ",DoubleToStr(oLots,2))
                     ,11);
               }
               else 
               if(oType==OP_BUYLIMIT || oType==OP_BUYSTOP)
               {
                  ObjectCreate("fvoe_ol_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice);
                  ObjectSet("fvoe_ol_" + oTicket,OBJPROP_COLOR,ol_buy_color);
                  ObjectSet("fvoe_ol_" + oTicket,OBJPROP_STYLE,ol_buy_style);
                  ObjectSetText("fvoe_ol_" + oTicket,
                     StringConcatenate("#",oTicket," ",orderName( oType )," ",DoubleToStr(oLots,2))
                     ,11);                  
               }
               else
               if(showOLforOpenOrders)
               {
                  if(oType)//sell
                  {
                     ObjectCreate("fvoe_ol_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice);
                     ObjectSet("fvoe_ol_" + oTicket,OBJPROP_COLOR,ol_sell_color);
                     ObjectSet("fvoe_ol_" + oTicket,OBJPROP_STYLE,ol_sell_style);
                     ObjectSetText("fvoe_ol_" + oTicket,
                        StringConcatenate("#",oTicket," ",orderName( oType )," ",DoubleToStr(oLots,2))
                        ,11);                     
                  }
                  else //buy
                  {
                     ObjectCreate("fvoe_ol_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice);
                     ObjectSet("fvoe_ol_" + oTicket,OBJPROP_COLOR,ol_buy_color);
                     ObjectSet("fvoe_ol_" + oTicket,OBJPROP_STYLE,ol_buy_style);           
                     ObjectSetText("fvoe_ol_" + oTicket,
                        StringConcatenate("#",oTicket," ",orderName( oType )," ",DoubleToStr(oLots,2))
                        ,11);                            
                  }               
               }
            }
            else
            {               
               if(oType < 2 )                  
               {
                  if( ! showOLforOpenOrders )
                     ObjectDelete("fvoe_ol_" + oTicket);
                  else                  
                     ObjectSet("fvoe_ol_" + oTicket,OBJPROP_PRICE1,oOpenPrice);
               }   
               else
               {                                 
                  double setOL = NormalizeDouble(ObjectGet("fvoe_ol_" + oTicket,OBJPROP_PRICE1),dgts);
                  ObjectSet("fvoe_ol_" + oTicket,OBJPROP_PRICE1,setOL);
                  if(NormalizeDouble(oOpenPrice,dgts) != setOL)
                  {
                     if(!OrderModify(oTicket,setOL,oStopLoss,oTakeProfit,oExpiration,CLR_NONE)) 
                     {
                        errorPrint(StringConcatenate("Modify OL #",oTicket),GetLastError());
                     }
                     continue;
                  }                                
               }
            }

            if(oStopLoss>0 || default_sl_level>0)
            {
               if(-1 == ObjectFind("fvoe_sl_" + oTicket))
               {
                  if(0 == oStopLoss)
                     ObjectCreate("fvoe_sl_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice-oDir*default_sl_level*point);
                  else
                     ObjectCreate("fvoe_sl_" + oTicket,OBJ_HLINE,0,Time[0],oStopLoss);
                     
                  ObjectSet("fvoe_sl_" + oTicket,OBJPROP_COLOR,sl_color);
                  ObjectSet("fvoe_sl_" + oTicket,OBJPROP_STYLE,sl_style);
                  
                  if(default_trailing_stop>0)
                     ObjectSetText("fvoe_sl_" + oTicket,"#"+oTicket+" stop loss, ts="+default_trailing_stop,11);
                  else
                     ObjectSetText("fvoe_sl_" + oTicket,"#"+oTicket+" stop loss",11);
               }
               else
               {
                  int tspos = StringFind(ObjectDescription("fvoe_sl_"+oTicket),"ts=");

                  if(-1 != tspos && oType < 2)
                  {
                     int ts = StrToInteger(StringSubstr(ObjectDescription("fvoe_sl_"+oTicket),tspos+3));

                     if(oDir*(BidAsk - oStopLoss) > ts*point )
                        ObjectSet("fvoe_sl_"+oTicket,OBJPROP_PRICE1,BidAsk - oDir*ts*point);
                  }

                  double setSL = NormalizeDouble(ObjectGet("fvoe_sl_" + oTicket,OBJPROP_PRICE1),dgts); 
                  ObjectSet("fvoe_sl_" + oTicket,OBJPROP_PRICE1,setSL);
                  if(NormalizeDouble(oStopLoss,dgts) != setSL)
                  {
                     if(!OrderModify(oTicket,oOpenPrice,setSL,oTakeProfit,oExpiration,CLR_NONE))
                     {
                        errorPrint(StringConcatenate("Modify SL #",oTicket),GetLastError());
                     }
                     continue;
                  }
               }
            }
            else
               clean("fvoe_sl_" + oTicket);

            if(oTakeProfit>0 || default_tp_level>0)
            {
               if(-1 == ObjectFind("fvoe_tp_" + oTicket))
               {
                  if(0 == oTakeProfit)
                     ObjectCreate("fvoe_tp_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice+oDir*default_tp_level*point);
                  else
                     ObjectCreate("fvoe_tp_" + oTicket,OBJ_HLINE,0,Time[0],oTakeProfit);
                     
                  ObjectSet("fvoe_tp_" + oTicket,OBJPROP_COLOR,tp_color);
                  ObjectSet("fvoe_tp_" + oTicket,OBJPROP_STYLE,tp_style);
                  ObjectSetText("fvoe_tp_" + oTicket,"#"+oTicket+" take profit",11);
               }
               else
               {
                  double setTP = NormalizeDouble(ObjectGet("fvoe_tp_" + oTicket,OBJPROP_PRICE1),dgts);
                  ObjectSet("fvoe_tp_" + oTicket,OBJPROP_PRICE1,setTP);                  
                  if(NormalizeDouble(oTakeProfit,dgts)!=setTP)
                  {
                     if(!OrderModify(oTicket,oOpenPrice,oStopLoss,setTP,oExpiration,CLR_NONE))
                     {
                        errorPrint(StringConcatenate("Modify TP #",oTicket),GetLastError());
                     }
                     continue;
                  }
               }
            }
            else
               clean("fvoe_tp_" + oTicket);

            if(use_cp && cp_size > 0)
            {              
               if(-1 == ObjectFind("fvoe_cp_" + oTicket))
               {
                  if( StringLen(oComment) < 5 || (-1 == StringFind(oComment,"from") && -1 == StringFind(oComment,"split")))
                  {
                     //if(oDir*(oStopLoss - oOpenPrice) < 0) 
                     {
                        ObjectCreate("fvoe_cp_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice+oDir*cp_lvl[0]*point);
                        ObjectSet("fvoe_cp_" + oTicket,OBJPROP_COLOR,cp_color);
                        ObjectSet("fvoe_cp_" + oTicket,OBJPROP_STYLE,cp_style);
                     }
                  }
               }
               else if (oType<2)
               {
                  if(oDir*(BidAsk - ObjectGet("fvoe_cp_" + oTicket,OBJPROP_PRICE1)) >= 0) 
                  {
                     int    deep        = 0;
                     double firstLot    = oLots;                     
                     string description = ObjectDescription("fvoe_cp_" + oTicket);
                     
                     if(StringLen(description) > 0)
                     {
                        int temp = StringFind(description,"_");
                        deep    = StrToInteger(StringSubstr(description,0,temp));
                        firstLot = StrToDouble (StringSubstr(description,temp+1));
                     }
                                                               
                     if(OrderClose(oTicket,cpCountLots(oLots, firstLot, deep),BidAsk,0))
                     {   
                        deep++;
                        if(deep < cp_size)
                        {
                           int newTicket = searchNewTicket(oTicket);
                           if( newTicket > 0 ) 
                           {                                               
                              ObjectCreate ("fvoe_cp_" + newTicket,OBJ_HLINE,0,Time[0],oOpenPrice+oDir*cp_lvl[deep]*point);
                              ObjectSet("fvoe_cp_" + newTicket,OBJPROP_COLOR,cp_color);
                              ObjectSet("fvoe_cp_" + newTicket,OBJPROP_STYLE,cp_style);
                              ObjectSetText("fvoe_cp_" + newTicket,StringConcatenate(deep,"_",DoubleToStr(firstLot,dgts)),11);
                           }  
                        }
                        ObjectDelete("fvoe_cp_"+oTicket);
                     }
                     else
                        errorPrint(StringConcatenate("Close Part #",oTicket),GetLastError());

                     continue;
                  }
               }
            }

            if(use_be)
            {
               if(-1 == ObjectFind("fvoe_be_" + oTicket))
               {
                  if(oDir*(oStopLoss-oOpenPrice)<0)
                  {
                     ObjectCreate("fvoe_be_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice+oDir*default_be_level*point);
                     ObjectSet("fvoe_be_" + oTicket,OBJPROP_COLOR,be_color);
                     ObjectSet("fvoe_be_" + oTicket,OBJPROP_STYLE,be_style);
                  }
               }
               else if(oType < 2 && oDir*(BidAsk-ObjectGet("fvoe_be_" + oTicket,OBJPROP_PRICE1))>=0) 
               {
                  ObjectSet("fvoe_sl_" + oTicket,OBJPROP_PRICE1,oOpenPrice+oDir*be_offset*point);
                  ObjectDelete("fvoe_be_" + oTicket);
                  continue;
               }
            }

            if(use_cl)
            {
               if(-1 == ObjectFind("fvoe_cl_" + oTicket))
               {
                  if(oType > 1)
                  {
                     if(oType==OP_BUYSTOP || oType==OP_SELLLIMIT)
                        ObjectCreate("fvoe_cl_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice-default_cl_level*point);
                     else
                        ObjectCreate("fvoe_cl_" + oTicket,OBJ_HLINE,0,Time[0],oOpenPrice+default_cl_level*point);
                     
                     ObjectSet("fvoe_cl_" + oTicket,OBJPROP_COLOR,cl_color);
                     ObjectSet("fvoe_cl_" + oTicket,OBJPROP_STYLE,cl_style);
                  }
               }
               else
               {
                  if(     (oType==OP_BUYSTOP  || oType==OP_SELLLIMIT) && BidAsk <= ObjectGet("fvoe_cl_" + oTicket,OBJPROP_PRICE1)) 
                     OrderDelete(oTicket);
                  else if((oType==OP_BUYLIMIT || oType==OP_SELLSTOP ) && BidAsk >= ObjectGet("fvoe_cl_" + oTicket,OBJPROP_PRICE1))
                     OrderDelete(oTicket);                  
                  else
                     ObjectDelete("fvoe_cl_" + oTicket);
               }
            }
         }
      }
   }

   for(i=0; i<OrdersHistoryTotal(); i++)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
      {
         clean("fvoe_ol_" + OrderTicket());
         clean("fvoe_tp_" + OrderTicket());
         clean("fvoe_sl_" + OrderTicket());
         clean("fvoe_be_" + OrderTicket());
         clean("fvoe_cl_" + OrderTicket());
         clean("fvoe_cp_" + OrderTicket());
      }
   }
   
   WindowRedraw(); 
}
void clean(string name)
{
   if(-1 != ObjectFind(name))  ObjectDelete(name);
}
double cpCountLots(double lots, double orygLots, int deep)
{
   double loty;
   
   if(cp_size_or_percent) loty = cp_lts[deep];
   else                   loty = lots - (100-cp_lts[deep])*orygLots*0.01;
      
   if(loty > lots)        loty = lots;
      
   return (normalizeLots(loty));
}
double normalizeLots(double value) 
{
   double minLots=MarketInfo(Symbol(),MODE_MINLOT);
   double maxLots=MarketInfo(Symbol(),MODE_MAXLOT);
   
   if     (value < minLots)
      value = minLots;
   else if(value > maxLots)
      value = maxLots;
    
   if(minLots < 0.1) 
      return(NormalizeDouble(value,2));
   else
      return(NormalizeDouble(value,1));
}
int listToTab(string list, double &tab[]) //zwraca rozmiar tablicy
{
   if(StringLen(list) > 0)
   {
      ArrayResize(tab,1);
      int i     = 0;
      int start = 0;
      int end   = StringFind(list,",");
      
      while( end != -1 )
      {
         tab[i] = StrToDouble( StringSubstr(list,start,end-start) );
         start = end+1;
         end = StringFind(list,",",start);
         i++;
         ArrayResize(tab,i+1);
      }
      
      tab[i] = StrToDouble( StringSubstr(list,start) );
      
      return (i+1);
   }
   else
      return (0);
}
int searchNewTicket(int oldTicket)
{
   for(int i=OrdersTotal()-1; i>=0; i--)
      if(OrderSelect(i,SELECT_BY_POS) && StrToInteger(StringSubstr(OrderComment(),StringFind(OrderComment(),"#")+1)) == oldTicket )
         return (OrderTicket());
   return (-1);
}
void errorPrint(string type, int err)
{  
   Print(type," ERROR(",err,") = ",ErrorDescription(err));
}
//
string orderName( int oType )
{
   switch(oType)
   {
      case OP_BUY       : return("buy");
      case OP_SELL      : return("sell");
      case OP_BUYLIMIT  : return("buy limit");
      case OP_SELLLIMIT : return("sell limit");
      case OP_BUYSTOP   : return("buy stop");
      case OP_SELLSTOP  : return("sell stop");
   }
}