//+------------------------------------------------------------------+
//| 3 Tier London Breakout.mq4
//+------------------------------------------------------------------+

/*+------------------------------------------------------------------+
 *
 * Version history:
 *
 * V1.1: added ResetMartAtFirstProfit input;
 *
 * V1.0: original version by Squalou, posted on forexfactory.com
 *       (thread: http://www.forexfactory.com/showthread.php?t=247220)
 *     - "3 Tier London Breakout.mq4" indicator can be used for visual help;
 *
 *+------------------------------------------------------------------+
 */

#property copyright "by Squalou - modded by Xmph"
#property link      "http://www.forexfactory.com/showthread.php?t=247220"

#property show_inputs
#include <WinUser32.mqh>
#include <stdlib.mqh>
#include <stderror.mqh>


#define  EA_VERSION    "V1.1"

//+------------------------------------------------------------------+

// "SendTrades": you must set this to true if you want the EA to place trades;
// by default, the EA will NOT send trades, but only show indicator's boxes and fibs (if "ShowBoxes" is true)
extern bool       SendTrades = true;

extern double     Lot  = 0.01;
extern double     MaxRisk = 0; // if 0 then use fixed Lots above
extern int        MagicNumber=0; // if 0 then will autocreate a unique MagicNumber

extern string     StartTime    = "05:00";    // time for start of price establishment window
extern string     EndTime      = "08:14";    // time for end of price establishment window
extern string     SessionEndTime= "15:00";   // end of daily session; tomorrow is another day!
extern int        MinBoxSizeInPips = 30;     // min tradable box size; when box is smaller than that, you should at least reduce your usual lot-size if you decide to trade it;
extern int        MaxBoxSizeInPips = 60;     // max tradable box size; don't trade when box is larger than that value
extern bool       LimitBoxToMaxSize = true;  // when true, a box larger than MaxBoxSizeInPips will be limited to MaxBoxSizeInPips, and centered on the EMA(box_time_range) value.
extern double     TP1Factor    = 0.618;      // if >=10, this will be taken as FIXED PIPs rather than a factor of the box size;
       double     TP2Factor; // set to half-way between TP1Factor and TP3Factor;
extern double     TP3Factor    = 2.000;      // if >=10, this will be taken as FIXED PIPs rather than a factor of the box size;
extern double     SLFactor     = 1.000;      // if >=10, this will be taken as FIXED PIPs rather than a factor of the box size;
extern double     LevelsResizeFactor = 1.0;

extern string     TradeComment="3 Tier London Breakout";
//extern string     _mta1="----MaxTradesAllowedPerSession = 0";
//extern string     _mta2="----means no limit on trades";
extern int        MaxTradesAllowedPerSession=0;
//extern string     _mla1="----MaxLossesAllowedPerSession = 0";
//extern string     _mla2="----means no limit on losses";
extern int        MaxLossesAllowedPerSession=0;
//extern string     _tm="----Trade management----";
//extern int        BreakEvenPips=0; // when >0, will move SL to BE+BreakEvenProfitInPips when price has reached BE+BreakEvenPips
extern int        BreakEvenProfitInPips=1;  // lock-in this amount of pips when HalfClose is hit;
//extern int        TrailingStopPips = 0;// Pips to trail the StopLoss; if 0 then no trailing stop
//extern int        TrailingStopStep = 1;// trailing SL jumping step in pips

extern string     _mart1="----MARTINGALE multipliers sequence:";
extern string     _mart2="----not used when first multiplier is 0:";
extern string     martingale_sequence="1,2,3,4,6,9,13,19,28,41,60,88,129,189,277,406";
extern bool       AutoMartingale=false;
extern bool       ResetMartAtFirstProfit=false; // when true, the Mart sequence is reset as soon as profitable trade is reached; else it will scale-in until recouped ALL losses;

extern int        Slippage = 3; // pips
extern bool       brokerIsECN=true;

//indicator settings
extern bool       ShowBoxes    = true; // when true, will show boxes and fibs just like the Indicator would do; this avoids using the indicator AND the EA together;
extern color      BoxColorOK   = LightBlue;
extern color      BoxColorNOK  = Red;
extern color      BoxColorMAX  = Orange;
extern color      LevelColor   = Black;
extern color      SessionColor = Linen; // show Session periods with a different background color
extern int        FibLength    = 14;
extern bool       showProfitZone = true;
extern color      ProfitColor  = LightGreen;
extern string     objPrefix = "LB2-";  // Prefixes of London Breakout Indicator objects;
//extern int    MinExtentInPips = 0; // actual box extent min limit for computing trade levels;
//extern int    MaxExtentInPips = 0; // actual box extent max limit for computing trade levels; 0 will default to MaxBoxSizeInPips;
extern string     Gap="          ";

extern string     _other_marts="---Other martingale sequences, in decreasing aggressivity(risk) order:";
extern string     aggressive_fibonacci_sequence="1,2,5,13,34,89,233,610,1597,4181"; // =every second term of fibonacci sequence;
extern string     powers_of_2_sequence="1,2,4,8,16,32,64,128,256,512,1024,2048,4096";
extern string     fibonacci_sequence="1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597"; // F(n)=F(n-1)+F(n-2);
extern string     mild_fibonacci_sequence="1,2,3,4,6,9,13,19,28,41,60,88,129,189,277,406"; // F(n)=F(n-1)+F(n-3);

// Gmt_Dst configuration
extern int        GMT_Datasets = 2;
bool              GMT_Adjusted_winter = false;
bool              GMT_Adjusted_summer = false;
extern bool       BacktestingMode = true;
extern bool       NoTradeLastDSTWeek = false;

//+------------------------------------------------------------------+

#define  NL    "\n"
#define  Status_initializing "????? I N I T I A L I Z I N G ?????"
#define  Status_initialized  "..... I N I T I A L I Z E D ....."
#define  Status_trading "+++++ T R A D I N G +++++"
#define  Status_not_trading "!!!!! N O T   T R A D I N G (Box size) !!!!!"
#define  Status_finished "----- F I N I S H E D   F O R   T O D A Y -----"

//breakout levels
//Entry, stop and tp
double BuyEntry,BuyTP1,BuyTP2,BuyTP3,BuySL;
double SellEntry,SellTP1,SellTP2,SellTP3,SellSL;
int SL_pips,TP1_pips,TP2_pips,TP3_pips;
double TP1FactorInput,TP2FactorInput,TP3FactorInput,SLFactorInput;
//box and session
datetime tBoxStart,tBoxEnd,tSessionStart,tSessionEnd,tLastComputedSessionStart,tLastComputedSessionEnd;
double boxHigh,boxLow,boxExtent,boxMedianPrice;

int magic1,magic2,magic3;

string symbol;

string            TradingStatus;

//Trading hours
bool              ClosedForFriday;
bool              ClosedForSaturday;
double            LondonOpens;
double            LondonCloses;
bool              TradeHour = false, prev_TradeHour=false;


//Martingale globals:
int               LastOrderProfitable=false;
bool              UseMartingale=false;
double            MartFactor[],LastLotMultiplier=0;
int               MartDepth=0;
int               Mart_Idx=0;

//Aggregates
int               TradesOpen, TradesOpenThisSession, TradesWon, TradesLost, TradesTotal;

//Misc stuff
string            comment;
bool              RobotDisabled;
string            DisabledMessage;

// GMT_DST
#include "libraries\GMT_DST_TimeMod.mq4"

// market info
int pipMultTab[]={0,0,1,10,1,10,100}; // multiplier to convert pips to Points;
double pip,pipvalue;
int digits;
double minlot;
int SLIPPAGE;

double previousAsk,currentAsk,previousBid,currentBid;

//Risk Management
int    maxSL;

string op_str[5] = { "BUY","SELL","BUYLIMIT","SELLLIMIT","BUYSTOP","SELLSTOP"};

//+------------------------------------------------------------------+
void DisplayUserFeedback()
//+------------------------------------------------------------------+
{
   static bool alreadyPrinted=false;
  
   if (IsTesting()&& !IsVisualMode() && alreadyPrinted) return; // saves cpu time when backtesting

   comment = Gap+WindowExpertName()+ " "+ EA_VERSION+ NL;
   if (MaxRisk <= 0) {
     comment = comment+Gap+ "Lot size = "+ Lot+ " (MaxRisk not used)"+ NL;
   } else {
     comment = comment+Gap+ "MaxRisk = "+ MaxRisk+ "% => "+ DoubleToStr(Lot,2)+ "Lots, AccountFreeMargin = "+ DoubleToStr(AccountFreeMargin(),0)+ AccountCurrency()+ NL;
   }
   comment = comment+Gap+ "Magic number = "+ MagicNumber+ ", objPrefix = "+ objPrefix+ NL
                    +Gap+ "Trade comment = "+ TradeComment+ NL
                    +Gap+ "Local time: "+ TimeToStr(TimeLocal())+ " Broker time: "+ TimeToStr(TimeCurrent())+ " Current Bar time: "+ TimeToStr(Time[0])+ NL
                    +Gap+" ["+StartTime+"-"+EndTime+"] end "+SessionEndTime+" min "+DoubleToStr(MinBoxSizeInPips,0)+"p,"+" max "+DoubleToStr(MaxBoxSizeInPips,0)+"p,"+DoubleToStr(TP1Factor,1)+"/"+DoubleToStr(TP2Factor,1)+"/"+DoubleToStr(TP3Factor,1)+NL
                    +Gap+" GMT From Server is " +GMT_Datasets
                    +Gap+ "Buy  Entry: "+ DoubleToStr(BuyEntry, Digits)+  ", TP: "+ DoubleToStr(BuyTP1, Digits)+  ", SL: "+ DoubleToStr(BuySL, Digits)+ NL
                    +Gap+ "Sell Entry: "+ DoubleToStr(SellEntry, Digits)+ ", TP: "+ DoubleToStr(SellTP1, Digits)+ ", SL: "+ DoubleToStr(SellSL, Digits)+ NL
                    +Gap+ "Trading Hours: "+ DoubleToStr(LondonOpens/100,2)+ " - "+ DoubleToStr(LondonCloses/100,2)+ NL
                    +Gap+ "Moving to BE+"+BreakEvenProfitInPips+" pips when reached TP1"+ NL
                    ;
/*
    if (TrailingStopPips>0) {
      comment = comment+Gap+ "     Trailing stops at "+TrailingStopPips+" pips, by steps of "+TrailingStopStep+" pips"+ NL;
    }
*/

   if (UseMartingale) {
     if (AutoMartingale) {
        comment =comment+Gap+"     AUTO-MARTINGALE ENABLED"+NL;
     } else {
        comment =comment+Gap+"     MARTINGALE FACTORS: ";
        for (int i=0;i<MartDepth;i++) {
          if (i==Mart_Idx) comment=comment+" ->";
          comment=comment+DoubleToStr(MartFactor[i],0);
          if (i==Mart_Idx) comment=comment+"<- ";
          comment=comment+",";
        }
        comment=comment+NL;
     }
   } else {
     comment =comment+Gap+"     Martingale NOT used"+NL;
   }
   
   comment = comment+Gap+ EA_VERSION+ " " + TradingStatus+ NL;

   if (IsTesting() && !IsVisualMode()) {
      Print(comment);
      alreadyPrinted = true;
      return;
   }

   Comment(comment);
}//void DisplayUserFeedback()


//+------------------------------------------------------------------+
int init()
//+------------------------------------------------------------------+
{
  GmtDst_SessionAdjusted();

  RobotDisabled = false;
  TradingStatus = Status_initializing;

  getpip(); // get pip multiplier and digit

  SLIPPAGE = Slippage*pipMultTab[Digits];
  
  symbol = Symbol(); // used throughout all functions

  //Create a MagicNumber if it is 0
  if (MagicNumber == 0) {
    MagicNumber = create_MagicNumber("");
  }

  //setup 3 different magic numbers to track each trade separately
  magic1 = MagicNumber;
  magic2 = MagicNumber+1;
  magic3 = MagicNumber+2;
  
  RemoveObjects(objPrefix);	

  if (Lot < minlot) {
    DisabledMessage = "Lot must be >= "+minlot+NL;
    RobotDisabled = true;
    _Alert (DisabledMessage);
    Comment("The robot is NOT trading because ", DisabledMessage);
    return;
  }

  //Check MaxRisk value
  if (MaxRisk > 100) {
    MessageBox("INVALID MaxRisk value " + MaxRisk + NL 
               + "Please reload the robot with valid MaxRisk.");
    DisabledMessage = "MaxRisk value ("+MaxRisk+") is INVALID";
    RobotDisabled = true;
    _Alert (DisabledMessage);
    Comment("The robot is NOT trading because ", DisabledMessage);
    return;
  }

  //save input Factors;
  TP1FactorInput = TP1Factor;
  TP3FactorInput = TP3Factor;
  SLFactorInput  = SLFactor;


  // compute box values:
  compute_LB_Indi_LEVELS(TimeCurrent());

  //Check MartFactor and MartDepth for "Martigaling"
  if (AutoMartingale) {
    UseMartingale = true;
  } else {
    MartDepth = string_list_to_double_array(martingale_sequence, ",", MartFactor);
    if (MartFactor[0] != 0) {
      UseMartingale = true;
    }
  }

  if (SendTrades == false) {
    DisabledMessage = "SendTrades is set to FALSE"+NL;
    RobotDisabled = true;
  }


  TradingStatus = Status_initialized;
  DisplayUserFeedback();
  _Alert(TradingStatus);
  return(0);
}
  
//+------------------------------------------------------------------+
int deinit()
//+------------------------------------------------------------------+
{
  //RemoveObjects(objPrefix);	

  Comment("");

  return(0);

}
  
//+------------------------------------------------------------------+
int start()
//+------------------------------------------------------------------+
{
  GmtDst_SessionAdjusted();
  GetRates();

  //-----------------------
  // compute box values:
  compute_LB_Indi_LEVELS(TimeCurrent());

  //show box and levels if required
  if (ShowBoxes == true) {
    show_boxes(TimeCurrent());
  }

  //-----------------------

  if (RobotDisabled)
  {
    Comment("The robot is NOT trading because ", DisabledMessage);
    return;
  }

  if (!IsTradeAllowed())
  {
    Comment("!!!!!!!!!!!!!!!!!!!!!!!!!\n"
           +"!!!!!!!!!!!!!!!!!!!!!!!!!\n"
           +"--- TRADING NOT ALLOWED YET---\n"
           +"!!!!!!!!!!!!!!!!!!!!!!!!!\n"
           +"!!!!!!!!!!!!!!!!!!!!!!!!!\n"
           );
    return;
  }//if (!IsTradeAllowed)

  //-----------------------
  //check Trading hours

  LondonOpens  = roundup_hhmm_time_to_TF (time_string_to_int(EndTime), Period());
  LondonCloses = time_string_to_int(SessionEndTime);
  int now = TimeHour(Time[0])*100 + TimeMinute(Time[0]);
  prev_TradeHour = TradeHour;
  
  if (BacktestingMode == true || NoTradeLastDSTWeek == true)
  {
      // To simplify things, we prevent trading the last week of March and last week of October
      // which corresponds to GMT/DST changes for London. (way simpler that writing a sub-routine
      // that determines the last sunday of March and October
      
     if (Day() >= 25 && (Month() == 3 || Month() == 10) /* && DayOfWeek() == 0*/)
     {TradeHour = false;}
     else if (  (LondonOpens <= LondonCloses && (now >= LondonOpens && now < LondonCloses))
     || (LondonOpens >  LondonCloses && (now >= LondonOpens || now < LondonCloses)) ) 
     {TradeHour = true;}
     else{TradeHour = false;}
  }
  else 
  {
     if (  (LondonOpens <= LondonCloses && (now >= LondonOpens && now < LondonCloses))
     || (LondonOpens >  LondonCloses && (now >= LondonOpens || now < LondonCloses)) ) 
     {TradeHour = true;} 
     else {TradeHour = false;}
  }

  if (prev_TradeHour==false && TradeHour==true) {
    //new session starting now: display new levels
    Print ("New Session Starting: "+ TimeToStr(TimeLocal())+NL
          +"Buy  Entry: "+ DoubleToStr(BuyEntry, Digits)+  ", TP: "+ DoubleToStr(BuyTP1, Digits)+  ", SL: "+ DoubleToStr(BuySL, Digits)+ NL
          +"Sell Entry: "+ DoubleToStr(SellEntry, Digits)+ ", TP: "+ DoubleToStr(SellTP1, Digits)+ ", SL: "+ DoubleToStr(SellSL, Digits)+ NL);
  }

  //-----------------------

  // manage open trades even when outside trading hours;
  // however, we won't open new trades
  GetAggregatePosition();
  if (TradesOpen > 0) {
    //-----------------------
      ManageOpenTrades();
    //-----------------------
  }
    
  if (!TradeHour)
  {
     if (TradingStatus != Status_finished) {
       _Alert (Status_finished+" Session End at "+DoubleToStr(LondonCloses/100,2));
     }
     TradingStatus = Status_finished;
     DisplayUserFeedback();
     return;
  }//if (!TradeHour)

  //-----------------------

  GetTradingStatus();
  if (TradingStatus == Status_not_trading)
  {
    DisplayUserFeedback();
    return;
  }

  //-----------------------

  // eventually recompute box and levels values, they may have been altered by ManageOpenTrades();
  compute_LB_Indi_LEVELS(TimeCurrent());

  // open new trades if conditions are met
  if (TradesOpenThisSession == 0) {
    //-----------------------
      CheckForOpen();
    //-----------------------
  }

  //-----------------------

  DisplayUserFeedback();
  return(0);

}//End start()

//+------------------------------------------------------------------+
void compute_LB_Indi_LEVELS(datetime now)
//+------------------------------------------------------------------+
{
  int boxStartShift,boxEndShift;

  if (now >= tSessionStart && now <= tSessionEnd) return; // box already up-to-date, no need to recompute

  //determine box and session times 
  tBoxStart = StrToTime(TimeToStr(now,TIME_DATE) + " "  + StartTime);
  tBoxEnd   = StrToTime(TimeToStr(now,TIME_DATE) + " "  + EndTime);
  if (tBoxStart > tBoxEnd) tBoxStart -= 86400; // midnight wrap fix
  if (now < tBoxEnd) { // consider the last PAST box
    tBoxStart -= 86400;
    tBoxEnd   -= 86400;
    while ((TimeDayOfWeek(tBoxStart)==0 || TimeDayOfWeek(tBoxStart)==6)
        && (TimeDayOfWeek(tBoxEnd)==0 || TimeDayOfWeek(tBoxEnd)==6) ) {
      // box on saturday or sunday: move back 24hours again
      tBoxStart -= 86400;
      tBoxEnd   -= 86400;
    }
  }

  tSessionStart = tBoxEnd;
  tSessionEnd = StrToTime(TimeToStr(tSessionStart,TIME_DATE) + " "  + SessionEndTime);
  if (tSessionStart > tSessionEnd) tSessionEnd = tSessionEnd + 86400; // midnight wrap fix
  // save the computed session start&end times to avoid recomputing them for each handled trade;
  tLastComputedSessionStart = tSessionStart;
  tLastComputedSessionEnd   = tSessionEnd;

  //determine hi/lo
  boxStartShift = iBarShift(NULL,0,tBoxStart);
  boxEndShift   = iBarShift(NULL,0,tBoxEnd);
  boxHigh = High[iHighest(NULL,0,MODE_HIGH,(boxStartShift-boxEndShift+1),boxEndShift)];
  boxLow  = Low[iLowest(NULL,0,MODE_LOW,(boxStartShift-boxEndShift+1),boxEndShift)];
  boxMedianPrice = (boxHigh+boxLow)/2;
  //apply LevelsResizeFactor
  boxExtent = (boxHigh - boxLow) * LevelsResizeFactor;

  if (boxExtent >= MaxBoxSizeInPips * pip && LimitBoxToMaxSize==true) { // box too large, but we allow to trade it at its max acceptable value
    // adjust box parameters to recenter it on the EMA(box_time_range) value
    boxExtent      = MaxBoxSizeInPips * pip;
    boxMedianPrice = iMA(NULL,0,boxStartShift-boxEndShift,0,MODE_EMA,PRICE_MEDIAN,boxEndShift);
  }
  
  //recompute box hi/lo prices based on adjusted median price and extent
  boxHigh = NormalizeDouble(boxMedianPrice + boxExtent/2,Digits);
  boxLow  = NormalizeDouble(boxMedianPrice - boxExtent/2,Digits);

  //restore input Factors;
  TP1Factor = TP1FactorInput;
  TP3Factor = TP3FactorInput;
  SLFactor  = SLFactorInput;

  //compute breakout levels
  BuyEntry  = boxHigh;
  SellEntry = boxLow;

  // when a Factor is >=10, it is considered as FIXED PIPs rather than a Factor of the box size;
  if (TP1Factor < 10) TP1_pips = boxExtent*TP1Factor/pip;
  else { TP1_pips = TP1Factor; TP1Factor = TP1_pips*pip/boxExtent; }
  BuyTP1  = NormalizeDouble(BuyEntry  + TP1_pips*pip,Digits);
  SellTP1 = NormalizeDouble(SellEntry - TP1_pips*pip,Digits);

  if (TP3Factor < 10) TP3_pips = boxExtent*TP3Factor/pip;
  else { TP3_pips = TP3Factor; TP3Factor = TP3_pips*pip/boxExtent; }
  BuyTP3  = NormalizeDouble(BuyEntry  + TP3_pips*pip,Digits);
  SellTP3 = NormalizeDouble(SellEntry - TP3_pips*pip,Digits);

  TP2Factor = (TP1Factor+TP3Factor)/2;
  if (TP2Factor < 10) TP2_pips = boxExtent*TP2Factor/pip;
  else { TP2_pips = TP2Factor; TP2Factor = TP2_pips*pip/boxExtent; }
  BuyTP2  = NormalizeDouble(BuyEntry  + TP2_pips*pip,Digits);
  SellTP2 = NormalizeDouble(SellEntry - TP2_pips*pip,Digits);

  if (SLFactor < 10) SL_pips = boxExtent*SLFactor/pip;
  else { SL_pips = SLFactor; SLFactor = SL_pips*pip/boxExtent; }
  BuySL  = NormalizeDouble(BuyEntry  - SL_pips*pip,Digits);
  SellSL = NormalizeDouble(SellEntry + SL_pips*pip,Digits);

}//compute_LB_Indi_LEVELS


//+------------------------------------------------------------------+
void show_boxes(datetime now)
//+------------------------------------------------------------------+
{
  static datetime alreadyDrawn=0;

  // show session period with a different "background" color
  drawBoxOnce (objPrefix+"Session-"+TimeToStr(tSessionStart,TIME_DATE | TIME_SECONDS),tSessionStart,0,tSessionEnd,BuyEntry*2,SessionColor,1, STYLE_SOLID, true);

  // draw pre-breakout box blue/red once per Session:
  if (alreadyDrawn != tBoxEnd) {
    alreadyDrawn = tBoxEnd; // won't redraw until next box
  
    // draw pre-breakout box blue/red:
    string boxName = objPrefix+"Box-"+TimeToStr(now,TIME_DATE)+"-"+StartTime+"-"+EndTime;
    if (boxExtent >= MaxBoxSizeInPips * pip) { // box too large: DON'T TRADE !
      if (LimitBoxToMaxSize==false) { // box too large, but we allow to trade it at its max acceptable value
        drawBox (boxName,tBoxStart,boxLow,tBoxEnd,boxHigh,BoxColorNOK,1, STYLE_SOLID, true);
        DrawLbl(objPrefix+"Lbl-"+TimeToStr(now,TIME_DATE)+"-"+StartTime+"-"+EndTime, "NO TRADE! ("+DoubleToStr(boxExtent/pip,0)+"p)", tBoxStart+(tBoxEnd-tBoxStart)/2,boxLow, 12, "Arial Black", LevelColor, 3);
      } else {
        drawBox (boxName,tBoxStart,boxLow,tBoxEnd,boxHigh,BoxColorMAX,1, STYLE_SOLID, true);
        DrawLbl(objPrefix+"Lbl-"+TimeToStr(now,TIME_DATE)+"-"+StartTime+"-"+EndTime, "MAX LIMIT! ("+DoubleToStr(boxExtent/pip,0)+"p)", tBoxStart+(tBoxEnd-tBoxStart)/2,boxLow, 12, "Arial Black", LevelColor, 3);
      }
    } else if (boxExtent >= MinBoxSizeInPips * pip) { // box OK
      drawBox (boxName,tBoxStart,boxLow,tBoxEnd,boxHigh,BoxColorOK,1, STYLE_SOLID, true);
      DrawLbl(objPrefix+"Lbl-"+TimeToStr(now,TIME_DATE)+"-"+StartTime+"-"+EndTime, DoubleToStr(boxExtent/pip,0)+"p", tBoxStart+(tBoxEnd-tBoxStart)/2,boxLow, 12, "Arial Black", LevelColor, 3);
    } else { // "Caution!" box
      drawBox (boxName,tBoxStart,boxLow,tBoxEnd,boxHigh,BoxColorNOK,1, STYLE_SOLID, true);
      DrawLbl(objPrefix+"Lbl-"+TimeToStr(now,TIME_DATE)+"-"+StartTime+"-"+EndTime, "Caution! ("+DoubleToStr(boxExtent/pip,0)+"p)", tBoxStart+(tBoxEnd-tBoxStart)/2,boxLow, 12, "Arial Black", BoxColorNOK, 3);
    }

    // draw profit/loss boxes for the session
    if (showProfitZone) {
      drawBox (objPrefix+"BuyProfitZone-" +TimeToStr(tSessionStart,TIME_DATE),tSessionStart,BuyTP1,tSessionEnd,BuyTP3,ProfitColor,1, STYLE_SOLID, true);
      drawBox (objPrefix+"SellProfitZone-"+TimeToStr(tSessionStart,TIME_DATE),tSessionStart,SellTP1,tSessionEnd,SellTP3,ProfitColor,1, STYLE_SOLID, true);
    }

    // draw "fib" lines for entry+stop+TP levels:
    string objname = objPrefix+"Fibo-" + tBoxEnd;
    ObjectCreate(objname,OBJ_FIBO,0,tBoxStart,SellEntry,tBoxStart+FibLength*60*10,BuyEntry);
    ObjectSet(objname,OBJPROP_RAY,false);
    ObjectSet(objname,OBJPROP_LEVELCOLOR,LevelColor); 
    ObjectSet(objname,OBJPROP_FIBOLEVELS,8);
    ObjectSet(objname,OBJPROP_LEVELSTYLE,STYLE_SOLID);
    _SetFibLevel(objname,0,0.0,"Entry Buy= %$");  
    _SetFibLevel(objname,1,1.0,"Entry Sell= %$");
    _SetFibLevel(objname,2,-TP1Factor, "Buy Target 1= %$  (+"+DoubleToStr(TP1_pips,0)+"p)");
    _SetFibLevel(objname,3,1+TP1Factor,"Sell Target 1= %$  (+"+DoubleToStr(TP1_pips,0)+"p)");
    _SetFibLevel(objname,4,-TP2Factor, "Buy Target 2= %$  (+"+DoubleToStr(TP2_pips,0)+"p)");
    _SetFibLevel(objname,5,1+TP2Factor,"Sell Target 2= %$  (+"+DoubleToStr(TP2_pips,0)+"p)");
    _SetFibLevel(objname,6,-TP3Factor, "Buy Target 3= %$  (+"+DoubleToStr(TP3_pips,0)+"p)");
    _SetFibLevel(objname,7,1+TP3Factor,"Sell Target 3= %$  (+"+DoubleToStr(TP3_pips,0)+"p)");
  }

}//show_boxes()

//+------------------------------------------------------------------+
void _SetFibLevel(string objname, int level, double value, string description)
//+------------------------------------------------------------------+
{
    ObjectSet(objname,OBJPROP_FIRSTLEVEL+level,value);
    ObjectSetFiboDescription(objname,level,description);
}

//+------------------------------------------------------------------+
void CheckForOpen()
//+------------------------------------------------------------------+
{
  double lotsToTrade;

  //Max trades filter
  if (MaxTradesAllowedPerSession > 0)// 0 means unlimited trades
  {                
    if (TradesTotal >= MaxTradesAllowedPerSession) 
    {
      if (TradingStatus != Status_finished) {
        _Alert (Status_finished+"Reached Max Trades at "+TimeToStr(TimeCurrent(),TIME_SECONDS));
      }
      TradingStatus = Status_finished;
      return;
   }
  }

  //Max loss filter
  if (MaxLossesAllowedPerSession > 0)
  {
    if (TradesLost >= MaxLossesAllowedPerSession*3) 
    {
      if (TradingStatus != Status_finished) {
        _Alert (Status_finished+"Reached MAX LOSSES at "+TimeToStr(TimeCurrent(),TIME_SECONDS));
      }
      TradingStatus = Status_finished;
      return;
    }
  }


  if (IsTradeAllowed())
  {                    
    TradingStatus = Status_trading;
    
    //buy(sell) on Buy(Sell)Entry crossup(down);
    //Strategy suggests to re-enter only once a (M15) candle closes inside the box.

    GetRates();
    //buy on BuyEntry crossup: (make sure price is still closer to Entry than TP1, to allow a minimum profit, and keep SL as low as possible)
    if (iClose(NULL,PERIOD_M15,1) <= BuyEntry && Ask >= BuyEntry && Ask < (BuyEntry+BuyTP1)/2) {
      lotsToTrade = get_lotsToTrade(magic1, 3*TP2_pips,SL_pips);
      _OrderSend(symbol,OP_BUY,lotsToTrade,Ask,SLIPPAGE,BuySL, BuyTP1,TradeComment,magic1,0,Blue);
      GetRates();
      _OrderSend(symbol,OP_BUY,lotsToTrade,Ask,SLIPPAGE,BuySL, BuyTP2,TradeComment,magic2,0,Blue);
      GetRates();
      _OrderSend(symbol,OP_BUY,lotsToTrade,Ask,SLIPPAGE,BuySL, BuyTP3,TradeComment,magic3,0,Blue);
    }
    else //sell on SellEntry crossdown:
    if (iClose(NULL,PERIOD_M15,1) >= SellEntry && Bid <= SellEntry && Bid > (SellEntry+SellTP1)/2) {
      lotsToTrade = get_lotsToTrade(magic1, 3*TP2_pips,SL_pips);
      _OrderSend(symbol,OP_SELL,lotsToTrade,Bid,SLIPPAGE,SellSL, SellTP1,TradeComment,magic1,0,Blue);
      GetRates();
      _OrderSend(symbol,OP_SELL,lotsToTrade,Bid,SLIPPAGE,SellSL, SellTP2,TradeComment,magic2,0,Blue);
      GetRates();
      _OrderSend(symbol,OP_SELL,lotsToTrade,Bid,SLIPPAGE,SellSL, SellTP3,TradeComment,magic3,0,Blue);
    }
  }

}//CheckForOpen

//+------------------------------------------------------------------+
void ManageOpenTrades()
//+------------------------------------------------------------------+
{
  int magic;
  double initialSL;
  
  for (int cc = OrdersTotal() - 1; cc >=0; cc--)
  {
    if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
    magic = OrderMagicNumber();
    if (OrderSymbol() == symbol && OrderCloseTime() == 0)
    {
      if (OrderOpenTime() < tSessionStart || OrderOpenTime() > tSessionEnd) {
        // this trade belongs to a different box/session:
        // we must recompute the corresponding box and levels values to handle it properly
        compute_LB_Indi_LEVELS(OrderOpenTime());
      }

      //Trade1 goes to TP1 or SL only: do nothing with it
      if (magic==magic1) continue;

      //Trades 2: move to BE+ when TP1 is hit
      if (magic==magic2) {
        MoveSLwhenProfit(TP1_pips, BreakEvenProfitInPips);
      }

      //Trade 3: move SL to BE+ when TP1 is hit
      //    then move SL to TP1 when TP2 is hit
      if (magic==magic3) {
        if (OrderType() == OP_BUY) initialSL = BuySL;
        else initialSL = SellSL;
        if (MathAbs(initialSL-OrderStopLoss())<=Point) { // trade3 has not yet moved to BE
          MoveSLwhenProfit(TP1_pips, BreakEvenProfitInPips);
        } else {// already moved to BE, now move SL to TP1 if TP2 is reached
          MoveSLwhenProfit(TP2_pips, TP1_pips);
        }
      }
    }
  }

}//ManageOpenTrades()

//+------------------------------------------------------------------+
void MoveSLwhenProfit(int profit_pips_target, int pips_to_lock)
//+------------------------------------------------------------------+
{
  //Lock-in some profit after a minimum profit target is reached:
  double SL;

  if ( (OrderType() == OP_BUY  && OrderClosePrice()-OrderOpenPrice() >= profit_pips_target*pip)
    || (OrderType() == OP_SELL && OrderOpenPrice()-OrderClosePrice() >= profit_pips_target*pip)
    ) {
    if (OrderType() == OP_BUY)  SL = NormalizeDouble(OrderOpenPrice() + (pips_to_lock*pip), Digits);
    if (OrderType() == OP_SELL) SL = NormalizeDouble(OrderOpenPrice() - (pips_to_lock*pip), Digits);
    if (MathAbs(SL-OrderStopLoss())>Point) { // ?!? surprisingly, "if (SL != OrderStopLoss()) ..." won't work... even when both seem to have the same value (using Print())
      _Alert(" #"+OrderTicket()+" ("+OrderMagicNumber()+") moving to BE+"+pips_to_lock+"pips");
      _OrderModify(OrderTicket(), OrderOpenPrice(), SL, OrderTakeProfit(), OrderExpiration(), Green);
    }
  }
}

//+------------------------------------------------------------------+
double get_lotsToTrade(int magic, int TP_pips, int SL_pips)
//+------------------------------------------------------------------+
{
  double lotsToTrade,profitpips=0,currentProfit=0,pips_to_recoup=0,factor=1,last_trade_profit=0;
  int last_ticket=0;

  if (!UseMartingale)
  {
    //adjust Lot based on MaxRisk and SL value
    if (MaxRisk > 0) return (MathMin(Lot,GetMaxLot(SL_pips,MaxRisk)));
    else return (Lot);
  }

  // if orders are still open, then use the same multiplier as the open trades
  if (TradesOpen>0) {
    Print ("### Trades still open, using same MartFactor=",LastLotMultiplier);
    return (LastLotMultiplier * Lot);
  }

  // No open trades;
  // Martingale is used: determine the martingale factor based on results of last trades
  //cumulate past profits/losses
  //search trade history for past trades, start from end of history(=newest trades)
  //if no past buy/sell trade can be found, then the mart factor will not change;
  for (int cc = OrdersHistoryTotal()-1; cc >= 0; cc--)
  {      
    OrderSelect(cc, SELECT_BY_POS, MODE_HISTORY);
    if (OrderMagicNumber()!=magic1 && OrderMagicNumber()!=magic2 && OrderMagicNumber()!=magic3) continue;
    if (OrderSymbol()!=symbol || OrderType()>OP_SELL) continue;
    if (last_ticket==0) {
      last_ticket = OrderTicket(); 
      factor = MathFloor(OrderLots()/Lot + 0.5); // compute nearest integer value to prevent division approximation
      last_trade_profit = OrderProfit();
    }
    profitpips = (OrderClosePrice()-OrderOpenPrice())/pip*OrderLots()/Lot;
    if (OrderType()==OP_SELL) profitpips = -profitpips;
    //OrderProfit()/pipvalue/Lot;
    currentProfit += profitpips;
    //if (maxProfit < currentProfit) maxProfit = currentProfit;
    //Print ("   #",OrderTicket()," profit=",profitpips,"pips, currentProfit=",currentProfit,"pips");
    // we stop scanning past trades when we have reached the first trade of the last series, ie the trade with a factor of 1
    if (OrderMagicNumber()==magic1 && OrderLots()==Lot) break;
  }

  // if no pips to recoup, then go back to initial Lot size
  if (currentProfit >= 0) {
    Print ("###  Current Profit=",currentProfit,"pips; No pips to recoup, MartFactor=1");
    Mart_Idx = 0;//reset mart sequence
    return (Lot);
  }

  // when ResetMartAtFirstProfit option is true, then reset the Mart sequence at the first profitable trade;
  if (ResetMartAtFirstProfit==true && last_trade_profit>=0) {
    Print ("###  Current Profit=",currentProfit,"pips; No pips to recoup, MartFactor=1");
    Mart_Idx = 0;//reset mart sequence
    return (Lot);
  }

  // there are pips to recoup, compute the next Mart factor
  pips_to_recoup = -currentProfit;

  if (AutoMartingale) { // auto-martingale: recoup past losses, and add current TP
    factor = (pips_to_recoup+TP_pips) / TP_pips;
    if (factor<2) factor = 2; // prevent using factor=1 when martingaling
    
  } else { // use fixed Martingale sequence: move to next mart factor

    // if the last "series" was in profit, then don't change the multiplier
    if (last_trade_profit>=0) {
      factor = MartFactor[Mart_Idx];
      Print ("### Last trade in profit, using same MartFactor=",factor);
    } else {
      // move to next multiplier
      for (Mart_Idx=0; Mart_Idx < MartDepth-1; Mart_Idx++) {
        if (MartFactor[Mart_Idx] > factor) break;
      }
      factor = MartFactor[Mart_Idx];
    }
  }

  if (AutoMartingale) {
    Print ("### AutoMartingale: ",pips_to_recoup," pips to recoup, +",TP_pips," pips TP => MartFactor is ", factor);
  } else {
    Print ("### ",pips_to_recoup," pips to recoup, now using MartFactor[",Mart_Idx,"]=",factor);
  }
  
  return (Lot*factor);

}//get_lotsToTrade()

//+------------------------------------------------------------------+
double GetMaxLot(int SL, double Risk)
//+------------------------------------------------------------------+
{
  double lotsize,tickvalue,pipvaluefor1lot,SLfor1lotfor1percent,maxLot;
  lotsize   = MarketInfo(Symbol(),MODE_LOTSIZE);
  tickvalue = MarketInfo(Symbol(),MODE_TICKVALUE);
  pipvaluefor1lot = lotsize*tickvalue*pip;
  maxLot    = NormalizeDouble(AccountFreeMargin()*Risk/100/pipvaluefor1lot/SL,2);
  return(maxLot);
}

//+------------------------------------------------------------------+
void GetAggregatePosition()
//+------------------------------------------------------------------+
{
  int magic;
   //returns the total trades for the pair - TradesOpen, TradesWon, TradesLost
   
   TradesOpen = 0;
   TradesOpenThisSession = 0;
   TradesWon = 0;
   TradesLost = 0;
   TradesTotal = 0;
   LastLotMultiplier = 0;
   
   //Count Open trades
   if (OrdersTotal() > 0)
   {
      for (int cc = OrdersTotal() - 1; cc >= 0; cc--)
      {      
         if (!OrderSelect(cc, SELECT_BY_POS, MODE_TRADES) ) continue;
         magic = OrderMagicNumber();
         if ((magic==magic1 || magic==magic2 || magic==magic3) && OrderSymbol() == symbol && OrderCloseTime() == 0)
         {
            TradesOpen++;
            if (OrderOpenTime() >= tSessionStart) {
              TradesOpenThisSession++;
            }
            if (LastLotMultiplier == 0) LastLotMultiplier = OrderLots()/Lot;
         }
      }
   }
   
   //Count won/lost trades in the past 24 hours from trades History
   for (cc = OrdersHistoryTotal() - 1; cc >= 0; cc--)
   {      
      OrderSelect(cc, SELECT_BY_POS, MODE_HISTORY);
      magic = OrderMagicNumber();
      if ((magic==magic1 || magic==magic2 || magic==magic3) && OrderSymbol() == symbol)
      {
        if (OrderCloseTime()<tBoxEnd) break;// past 24 hours, stop counting;
        if (OrderProfit() < 0) TradesLost++;
        if (OrderProfit() > 0) TradesWon++;         
        if (LastLotMultiplier == 0) LastLotMultiplier = OrderLots()/Lot;
      }
   }
   
   TradesTotal = TradesOpen + TradesLost + TradesWon;
   if (LastLotMultiplier == 0) LastLotMultiplier = 1;

}//void GetAggregatePosition()

//+------------------------------------------------------------------+
void GetTradingStatus()
//+------------------------------------------------------------------+
{
   //The initial trade stops are the extent of the box
   
  if (MinBoxSizeInPips > 0 && boxExtent < MinBoxSizeInPips * pip) TradingStatus = Status_not_trading;
  if (LimitBoxToMaxSize==false && MaxBoxSizeInPips > 0 && boxExtent > MaxBoxSizeInPips * pip) TradingStatus = Status_not_trading;

}//End void GetTradingStatus()

/*
//+------------------------------------------------------------------+
bool BreakEvenStopLoss(int profit_pips_target, int pips_to_lock)
//+------------------------------------------------------------------+
{
  //Lock-in some profit after a minimum profit target is reached:
  //(move SL to BE+pips_to_lock)
  double SL;
   
  //Long
  if (OrderType() == OP_BUY && Bid >= OrderOpenPrice()+profit_pips_target*pip && OrderStopLoss() < OrderOpenPrice()) {
    SL = NormalizeDouble(OrderOpenPrice() + (pips_to_lock*pip), Digits);
    _Alert(" #"+OrderTicket()+" moving to BE+"+pips_to_lock+"pips");
    _OrderModify(OrderTicket(), OrderOpenPrice(), SL, OrderTakeProfit(), OrderExpiration(), Green);
    return(true);
  }
   
  //Short
  if (OrderType() == OP_SELL && Ask <= OrderOpenPrice()-profit_pips_target*pip && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss()==0)) {
    SL = NormalizeDouble(OrderOpenPrice() - (pips_to_lock*pip), Digits);
    _Alert(" #"+OrderTicket()+" moving to BE+"+pips_to_lock+"pips");
    _OrderModify(OrderTicket(), OrderOpenPrice(), SL, OrderTakeProfit(), OrderExpiration(), Green);
    return(true);
  }

  return(false);//did not move to BE
   
}//BreakEvenStopLoss()
*/

/*
//+------------------------------------------------------------------+
void Trail_order(int _TrailingStopPips, int _TrailingStopStep)
//+------------------------------------------------------------------+
{
  if (_TrailingStopPips<=0) return;
  if (_TrailingStopStep>_TrailingStopPips) return;
  if (MathAbs(OrderStopLoss()-OrderClosePrice()) > (_TrailingStopPips+_TrailingStopStep)*pip) {										    
    if (OrderType() == OP_BUY)  _OrderModify(OrderTicket(),OrderOpenPrice(),OrderClosePrice()-_TrailingStopPips*pip,OrderTakeProfit(),OrderExpiration(),Purple);
    if (OrderType() == OP_SELL) _OrderModify(OrderTicket(),OrderOpenPrice(),OrderClosePrice()+_TrailingStopPips*pip,OrderTakeProfit(),OrderExpiration(),Purple);
  }
}
*/

//+------------------------------------------------------------------+
bool GetRates()
//+------------------------------------------------------------------+
{
  bool ret = RefreshRates();
  //remember Ask/Bid changes -- used to detect levels crossing
  if (currentAsk != previousAsk || currentBid != previousBid) {
    previousAsk = currentAsk;
    previousBid = currentBid;
  }
  currentAsk = Ask; currentBid = Bid;
  return(ret);
}//GetRates()


//+------------------------------------------------------------------+
int _OrderSend(string symbol, int type, double lots, double price, int slippage, double stoploss, double takeprofit, string comment, int magic, datetime expiration, color arrow_color)
//+------------------------------------------------------------------+
{
#define MAX_RETRIES 10
//"reliable" version of OrderSend(): will retry 10 times before failing
  int i,j,err,ticket;
  
  for (i=0;i<MAX_RETRIES;i++) {
    for (j=0;j<MAX_RETRIES && !IsTradeAllowed();j++) Sleep(500); // wait for Trade to be allowed(free context)
    if (j==MAX_RETRIES) {
      _Alert("OrderSend() FAILED: trade not allowed");
      return (-1);
    }

    // send order with 0 SL, then modify order to apply SL -- a common counter-ECN method found in SteveHopwood's bots...
    if (!brokerIsECN) {
      ticket = OrderSend(symbol, type, lots, price, slippage, stoploss, takeprofit, comment, magic, expiration, arrow_color);
      if (ticket != -1) return (ticket);
      err = GetLastError();
      if (err==130) break;//130=invalid stops: SL or TP are set too close to open price;
    } else {

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                 START EDITED BY XMPH
   reason for edition: not compatible with ECN brokers
   
   note: Squalou, I added what I use in order to check if SL
            respect Broker's SL limit. However, I did not code
            the logic for handling what happen if SL is too tight
   note2: In a similar manner, there should be a check and associated logic
            for the lots to be send.
                                                                          
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
       double MinSL = (MarketInfo(Symbol(), MODE_STOPLEVEL)+MarketInfo(Symbol(), MODE_SPREAD))*Point;
      // SLPrice will store the Long or Short StopLoss price.
      double SLPrice;
      
      if (type == OP_BUY) {SLPrice = NormalizeDouble(Ask,Digits) - MinSL;}
      else {SLPrice = NormalizeDouble(Bid,Digits) + MinSL;}
      
      if ((type == OP_BUY) && (stoploss <= SLPrice) )
      {ticket = OrderSend(symbol, type, lots, price, slippage, 0/*stoploss*/, 0/*takeprofit*/, comment, magic, expiration, arrow_color);}
      else if ((type == OP_SELL) && (stoploss >= SLPrice) )
      {ticket = OrderSend(symbol, type, lots, price, slippage, 0/*stoploss*/, 0/*takeprofit*/, comment, magic, expiration, arrow_color);}
      else {Print("Error: SL @Buy does not respect Broker Minimal SL for OrderModify: Desired = ", stoploss, "Minimal = ", SLPrice);return(-1);}
      
      if (ticket != -1) {       
        _OrderModify(ticket, price, stoploss, takeprofit, expiration, arrow_color);
    
//++++++++++++++++++++++++++++++++++++++++++++++++
/*          END EDITED BY XMPH - ECN issue      */
//++++++++++++++++++++++++++++++++++++++++++++++++

        return(ticket);//success
      }
      err = GetLastError();
      if (err==130) break;//130=invalid stops: SL or TP are set too close to open price;
    }
  }
  _Alert("OrderSend() FAILED at "+DoubleToStr(price,Digits)+",SL="+DoubleToStr(stoploss,Digits)+",TP="+DoubleToStr(takeprofit,Digits)+",Ask="+DoubleToStr(Ask,Digits)+" ("+err+"): "+ErrorDescription(err));
  return(-1);
}

//+------------------------------------------------------------------+
bool _OrderModify(int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color) 
//+------------------------------------------------------------------+
{
//"reliable" version of OrderModify(): will retry 10 times before failing
  int i,j,err,ret;
  
  for (i=0;i<MAX_RETRIES;i++) {
    for (j=0;j<MAX_RETRIES && !IsTradeAllowed();j++) Sleep(500); // wait for Trade to be allowed(free context)
    if (j==MAX_RETRIES) {
      _Alert("OrderModify(#"+ticket+") FAILED: trade not allowed");
      return(false);
    }

    ret = OrderModify(ticket, price, stoploss, takeprofit, expiration, arrow_color); 
    if (ret == true) return(true);//success
    err = GetLastError();
    switch (err) {
      case ERR_NO_RESULT:    // error 1 means OrderModify() was called with unchanged values
      case ERR_INVALID_STOPS:// error 130 "invalid stops" when SL is set too close to current price
        return(true);
    }
  }

  _Alert("OrderModify(#"+ticket+") FAILED ("+err+"): "+ErrorDescription(err));
  return(false);
}

/*
//+------------------------------------------------------------------+
bool _OrderClose( int ticket, double lots, double price, int slippage, color Color) 
//+------------------------------------------------------------------+
{
//"reliable" version of OrderClose(): will retry 10 times before failing
  int i,j,err;
  bool ret;
  
  for (i=0;i<MAX_RETRIES;i++) {
    for (j=0;j<MAX_RETRIES && !IsTradeAllowed();j++) Sleep(500); // wait for Trade to be allowed(free context)
    if (j==MAX_RETRIES) {
      _Alert("OrderClose(#"+ticket+") FAILED: trade not allowed");
      return(false);
    }

    ret = OrderClose(ticket, lots, price, slippage, Color);
    if (ret == true) return(true);//success
  }

  err = GetLastError();
  _Alert("OrderClose(#"+ticket+") FAILED ("+err+"): "+ErrorDescription(err));
  return(false);
}
*/

/*
//+------------------------------------------------------------------+
bool _OrderDelete(int ticket, color Color) 
//+------------------------------------------------------------------+
{
//"reliable" version of OrderDelete(): will retry 10 times before failing
  int i,j,err;
  bool ret;
  
  for (i=0;i<MAX_RETRIES;i++) {
    for (j=0;j<MAX_RETRIES && !IsTradeAllowed();j++) Sleep(500); // wait for Trade to be allowed(free context)
    if (j==MAX_RETRIES) {
      _Alert("OrderDelete(#"+ticket+") FAILED: trade not allowed");
      return(false);
    }

    ret = OrderDelete(ticket, Color);
    if (ret == true) return(true);//success
  }

  err = GetLastError();
  _Alert("OrderDelete(#"+ticket+") FAILED ("+err+"): "+ErrorDescription(err));
  return(false);
}
*/

//+------------------------------------------------------------------+
int create_MagicNumber(string s)
//+------------------------------------------------------------------+
{
  // create a magic number that is "unique" for a given {EA_name,Symbol,Period} combo
  int magic=0;
  s = s+WindowExpertName()+Symbol()+Period()+objPrefix;
  magic = hash_string(s);
  while (magic < 10000) {// magic number is not long enough, make another one
    s = s+magic;
    magic = hash_string(s);
  }
  return (magic);
}

//+------------------------------------------------------------------+
int hash_string(string s)
//+------------------------------------------------------------------+
{
  // this is the djb2 string hash algo
  int h = 5381, l = StringLen(s), i;
  for (i=0; i<l; i++) h = h * 33 + StringGetChar(s,i);
  return (h);
} /*hash_string()*/


//+------------------------------------------------------------------+
void getpip()
//+------------------------------------------------------------------+
{
  pip = pipMultTab[Digits]*Point;
      
	if (Digits == 3 || Digits == 2) digits = 2;
	else digits = 4;
	
	minlot   = MarketInfo(Symbol(),MODE_MINLOT);
  pipvalue = pipValueFor1Lot();
	
} /* getpip*/

//+------------------------------------------------------------------+
double pipValueFor1Lot()
//+------------------------------------------------------------------+
{
  double lotsize,tickvalue;
  lotsize   = MarketInfo(Symbol(),MODE_LOTSIZE);
  tickvalue = MarketInfo(Symbol(),MODE_TICKVALUE);
  return (lotsize*tickvalue*pip);
}


//--------------------------------------------------------------------------------------
int time_string_to_int(string ts)
//--------------------------------------------------------------------------------------
// convert a time string "hh:mm" into an integer hhmm
{
  int t;
  t = StrToTime("1970.01.01 " + ts)/60;
  return (MathRound(t/60)*100+(t%60));
}

//--------------------------------------------------------------------------------------
int roundup_hhmm_time_to_TF (int hhmm, int tf)
//--------------------------------------------------------------------------------------
{
  double t  = hhmm; // convert to double for divisions
  double hh = MathFloor(t/100);
  int mm = (t/100-hh)*100;
  mm = (MathFloor(mm/tf)+1)*tf;
  return ( (hh+MathFloor(mm/60))*100 + mm-60*MathFloor(mm/60) );
}



//--------------------------------------------------------------------------------------
void _Alert(string text)
//--------------------------------------------------------------------------------------
{
  Alert ("LB EA: ", symbol, " ", text);
}

//+------------------------------------------------------------------+
int string_list_to_double_array(string list, string sep, double &array[])
//+------------------------------------------------------------------+
{
//create an array of double from a string containing the list of values
//ex: n = string_list_to_double_array ("1,2,3,5,8" , "," , &array);

   int i=0,j,k;
   string ls;

   while (true) {
   	j=StringFind(list,sep,i);
   	if (j<0) break;

 		ls=StringTrimRight(StringTrimLeft(StringSubstr(list,i,j-i)));
 		i=j+1;
 		k++;
 		ArrayResize(array,k);
 		array[k-1]=StrToDouble(ls);
  }
  //last element in the list:
	ls=StringTrimRight(StringTrimLeft(StringSubstr(list,i)));
	k++;
	ArrayResize(array,k);
	array[k-1]=StrToDouble(ls);

  return (ArraySize(array));
}//string_list_to_double_array

//--------------------------------------------------------------------------------------
// RemoveObjects
//--------------------------------------------------------------------------------------

void RemoveObjects(string Pref)
{   
   int i;
   string objname = "";

   for (i = ObjectsTotal(); i >= 0; i--) {
      objname = ObjectName(i);
      if (StringFind(objname, Pref, 0) > -1) ObjectDelete(objname);
   }
} /* RemoveObjects*/


//--------------------------------------------------------------------------------------
// drawBox
//--------------------------------------------------------------------------------------

void drawBox (
  string objname,
  datetime tStart, double vStart, 
  datetime tEnd,   double vEnd,
  color c, int width, int style, bool bg
)
{
  if (ObjectFind(objname) == -1) {
    ObjectCreate(objname, OBJ_RECTANGLE, 0, tStart,vStart,tEnd,vEnd);
  } else {
    ObjectSet(objname, OBJPROP_TIME1, tStart);
    ObjectSet(objname, OBJPROP_TIME2, tEnd);
    ObjectSet(objname, OBJPROP_PRICE1, vStart);
    ObjectSet(objname, OBJPROP_PRICE2, vEnd);
  }

  ObjectSet(objname,OBJPROP_COLOR, c);
  ObjectSet(objname, OBJPROP_BACK, bg);
  ObjectSet(objname, OBJPROP_WIDTH, width);
  ObjectSet(objname, OBJPROP_STYLE, style);
} /* drawBox */


//--------------------------------------------------------------------------------------
// drawBoxOnce: draw a Box only once; if it already exists, do nothing
//--------------------------------------------------------------------------------------

void drawBoxOnce (
  string objname,
  datetime tStart, double vStart, 
  datetime tEnd,   double vEnd,
  color c, int width, int style, bool bg
)
{
  if (ObjectFind(objname) != -1) return;
  
  ObjectCreate(objname, OBJ_RECTANGLE, 0, tStart,vStart,tEnd,vEnd);
  ObjectSet(objname,OBJPROP_COLOR, c);
  ObjectSet(objname, OBJPROP_BACK, bg);
  ObjectSet(objname, OBJPROP_WIDTH, width);
  ObjectSet(objname, OBJPROP_STYLE, style);
} /* drawBoxOnce */

/*
//--------------------------------------------------------------------------------------
void drawFixedLbl(string objname, string s, int Corner, int DX, int DY, int FSize, string Font, color c, bool bg)
//--------------------------------------------------------------------------------------
{
   if (ObjectFind(objname) < 0) ObjectCreate(objname, OBJ_LABEL, 0, 0, 0);
   
   ObjectSet(objname, OBJPROP_CORNER, Corner);
   ObjectSet(objname, OBJPROP_XDISTANCE, DX);
   ObjectSet(objname, OBJPROP_YDISTANCE, DY);
   ObjectSet(objname,OBJPROP_BACK, bg);      
   ObjectSetText(objname, s, FSize, Font, c);
} //drawFixedLbl
*/

//--------------------------------------------------------------------------------------
// DrawLbl
//--------------------------------------------------------------------------------------

void DrawLbl(string objname, string s, int LTime, double LPrice, int FSize, string Font, color c, int width)
{
  if (ObjectFind(objname) < 0) {
    ObjectCreate(objname, OBJ_TEXT, 0, LTime, LPrice);
  } else {
    if (ObjectType(objname) == OBJ_TEXT) {
      ObjectSet(objname, OBJPROP_TIME1, LTime);
      ObjectSet(objname, OBJPROP_PRICE1, LPrice);
    }
  }

  ObjectSet(objname, OBJPROP_FONTSIZE, FSize);
  ObjectSetText(objname, s, FSize, Font, c);
} /* DrawLbl*/


//end

