/*
*/

#property copyright "ForexHackedMod 004.07"
#property link      "http://forum.tradelikeapro.ru"

#include <stderror.mqh>

extern bool Allow1Buy = true;
extern bool Allow1Sell = true;
extern bool Order2Booster = true;
extern bool UseIndicators = true;
extern bool AllowExtraOrders = false;
extern bool UseBuyGridParametersOnly = false;

extern bool CloseAllBuy = false;
extern bool CloseAllSell = false;
extern bool MoveBuyTP = false;
extern bool MoveSellTP = false;
extern double MoveTP = 1.0;
extern int BreakEvenLevel = 0;

extern double BuyLots = 0.02;
extern double BuyBooster = 1.774;
extern double BuyPipStarter = 18.9;
extern double BuyTakeProfit = 38.5;
extern int BuyMaxOrders = 9;

extern double SellLots = 0.02;
extern double SellBooster = 1.774;
extern double SellPipStarter = 19.1;
extern double SellTakeProfit = 38.7;
extern int SellMaxOrders = 9;

// minimal allowed TP for price gap order
extern double PriceGapMinimalTP = 3.0;
// distance between price gap TP and normal pyramid order
extern double PriceGapOrderDistance = 0;

extern bool AllowiStopLoss = true;
extern double iStopLoss = 236;
extern int Slippage = 1;

extern double StopLossPct = 100;
extern double TakeProfitPct = 100;

extern int StartHour = 0;
extern int StartMinute = 0;
extern int StopHour = 0;
extern int StopMinute = 0;
extern int StartingTradeDay = 0;
extern int EndingTradeDay = 7;

extern string MagicNumberInfo = "Magic Number Must be UNIQUE for each chart!";
extern int MagicNumber = 1002401;
extern string TradeChartInfo = "TradeChart = name of currencies pair used for trading";
extern string TradeChart = "GBPUSD";
extern int MAPeriod = 15;
extern double SARStep = 0.0618;
extern string EA_Name = "ForexHackedMod 004.07";

extern bool TrailingOrders = false;
extern int TrailingStartLevel = 9;
extern double TrailingSL = 3.0;
extern double TrailingStep = 0.1;
extern double TrailingMinProfit = 38;
extern double TakeProfitAfterSL = 11.6;

extern bool ManageLocks = true;
extern double LockStep = 10;
extern double LockStepWP = 5;
extern double LockSL = 10;
extern double LockTP = 60;
extern double LockExtraProfit = 5;
extern int LockMagicNumber = 201207;
double VMin;

bool
gIsNotOptimization,
gIsNotReady = false,
gLowBalance = false,
gIsNotTesting,
gModifiedTP[2],
gUseGapTP = false,
gMoveTPMode[2],
gCloseAllMode[2];

int
gMAShift = 0,
gMAMethod = MODE_LWMA,
gMAPrice = PRICE_WEIGHTED,
gLotStepDigits,
gFileHandler = 0,
gStartMinutes,
gStopMinutes,
gGapOrderTicket[2],
gLastTicket[2],
gOrders[2],
gLevel[2],
gTickets[2][20];

double
gSARMaximum = 0.2,
gLotMargin,
gPriceValue,
gTicksPerPrice,
gBaseLotMargin,
gBalanceNeeded,
gSlippage,
gLots[2],
gBooster[2],
gPipStarter[2],
gTakeProfit[2],
gPipStarterInv[2],
gStopLevel,
gSumLots[2],
gSumPriceLots[2],
gMeanPrice[2],
gLastTP[2],
gMinimalGap,
gTrailingThreshold,
sMaxTotalLots;

string gMutexName = "FHMod";

double iif(bool condition, double ifTrue, double ifFalse) {
  if (condition) return (ifTrue);
  return (ifFalse);
}

void Log(string text) {
  if (gFileHandler > 0) FileWrite(gFileHandler, TimeToStr(TimeCurrent(), TIME_DATE | TIME_SECONDS) + ": " + text);
}

int deinit() {
  if (gFileHandler > 0) {
    if (sMaxTotalLots > 0) Log("Maximum of lots total: " + DoubleToStr(sMaxTotalLots, gLotStepDigits));
    FileClose(gFileHandler);
  }
  return (0);
}

void CalcLevel(int type) {
  double booster = gBooster[type];
//   gLevel[type] = MathRound(MathLog(1 + (booster - 1) * gSumLots[type] / gLots[type]) / MathLog(booster));
  gLevel[type] = 1 + MathRound(MathLog(OrderLots() / gLots[type]) / MathLog(booster));
}

bool CloseAll(int type) {
  double price;
  bool repeat_needed = false;
  if (gIsNotTesting) RefreshRates();
  if (type == OP_BUY) price = Bid;
  else price = Ask;
  for (int pos = gOrders[type] - 1; pos >= 0; pos--) {
    if (!OrderSelect(gTickets[type][pos], SELECT_BY_TICKET) || OrderCloseTime() != 0) continue;
    if (gIsNotTesting && RefreshRates()) {
      if (type == OP_BUY) price = Bid;
      else price = Ask;
    }
    if (!OrderClose(OrderTicket(), OrderLots(), price, Slippage)) repeat_needed = true;
  }
  return (repeat_needed);
}

void AnalyzeOrders() {
  int i, n = OrdersTotal(), type;
  double tmp, tp;
  for (i = 0; i < 2; i++) {
    gLevel[i] = 0;
    gOrders[i] = 0;
    gLastTP[i] = 0;
    gLastTicket[i] = 0;
    gGapOrderTicket[i] = 0;
    gModifiedTP[i] = false;
    gSumLots[i] = 0;
    gSumPriceLots[i] = 0;
  }
  // find orders tickets
  for (i = 0; i < n; i++) {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES) || OrderMagicNumber() != MagicNumber) continue;
    type = OrderType();
    if (type > 1) continue;
    gTickets[type][gOrders[type]] = OrderTicket();
    gOrders[type]++;
    tmp = OrderLots();
    gSumLots[type] += tmp;
    gSumPriceLots[type] += tmp * OrderOpenPrice();
  }

  // find last takeprofit
  for (i = 0; i < 2; i++) {
    if (gOrders[i] < 1) continue;
    gMeanPrice[i] = NormalizeDouble(gSumPriceLots[i] / gSumLots[i], Digits);
    gLastTicket[i] = gTickets[i][gOrders[i] - 1];
    OrderSelect(gLastTicket[i], SELECT_BY_TICKET);
    gLevel[i] = 1;
    if (gOrders[i] < 2) continue;
    CalcLevel(i);
    gModifiedTP[i] = true;
    gLastTP[i] = OrderTakeProfit();
    tmp = OrderOpenPrice();
    OrderSelect(gTickets[i][gOrders[i] - 2], SELECT_BY_TICKET);
    tmp -= OrderOpenPrice();
    if (tmp < 0) tmp = -tmp;
    tmp -= gPipStarter[i];
    if (tmp >= gMinimalGap) {
      gGapOrderTicket[i] = gLastTicket[i];
      gLastTP[i] = OrderTakeProfit();
    }
  }

  // move TP in case of SL closing
  for (i = 0; i < 2; i++) {
    if (gOrders[i] < 1) continue;
    OrderSelect(gLastTicket[i], SELECT_BY_TICKET);
    tp = gLastTP[i];
    tmp = TakeProfitAfterSL;
    if (i == OP_SELL) tmp = -tmp;
    tmp = OrderOpenPrice() + tmp;
    if (i == OP_BUY) {
      if (tmp <= tp) continue;
    } else {
      if (tmp >= tp) continue;
    }
    gLastTP[i] = tmp;
    gModifiedTP[i] = true;
    Log("ScaleTP: " + DoubleToStr(tp, Digits) + " -> " + DoubleToStr(tmp, Digits));
  }

  // move TP close to breakeven level
  for (i = 0; i < 2; i++) {
    if (!gMoveTPMode[i]) continue;
    if (BreakEvenLevel <= 0) gMoveTPMode[i] = false;
    if (gOrders[i] < 1) continue;
    gModifiedTP[i] = true;
    if (i == OP_BUY) tmp = MoveTP;
    else tmp = -MoveTP;
    gLastTP[i] = gMeanPrice[i] + tmp;
  }
}

double PipToPrice(double x) {
  return (NormalizeDouble(x * Point, Digits));
}

void LimitPipToPrice(double &limit) {
  limit *= Point;
  limit = iif(limit < gStopLevel, gStopLevel, NormalizeDouble(limit, Digits));
}

int init() {
  if (TradeChart != Symbol()) {
    string msg = "EA is OFF! TradeChart differs from chart symbol: " + TradeChart + " != " + Symbol();
    Alert(msg);
    Log(msg);
    gIsNotReady = true;
    return (1);
  }
  VMin=MarketInfo(Symbol(),MODE_POINT)* (MarketInfo(Symbol(),MODE_STOPLEVEL)+Slippage);  
  gIsNotOptimization = !IsOptimization();
  gIsNotTesting = !IsTesting() && gIsNotOptimization;
  if (gIsNotTesting && !GlobalVariableCheck(gMutexName)) GlobalVariableSet(gMutexName, 0);
  if (gIsNotOptimization) {
    gFileHandler = FileOpen(WindowExpertName() + "_" + Time[0] + "_" + TradeChart + "_" + MagicNumber + ".log", FILE_WRITE);
  }
  gLotStepDigits = MathRound((-MathLog(MarketInfo(TradeChart, MODE_LOTSTEP))) / MathLog(10));
  gStopLevel = MarketInfo(TradeChart, MODE_STOPLEVEL) * Point;

  gMoveTPMode[0] = MoveBuyTP;
  gMoveTPMode[1] = MoveSellTP;
  gCloseAllMode[0] = CloseAllBuy;
  gCloseAllMode[1] = CloseAllSell;

  gLots[0] = BuyLots;
  gBooster[0] = BuyBooster;
  gPipStarter[0] = BuyPipStarter;
  gTakeProfit[0] = BuyTakeProfit;
  if (UseBuyGridParametersOnly) {
    gLots[1] = gLots[0];
    gBooster[1] = gBooster[0];
    gPipStarter[1] = gPipStarter[0];
    gTakeProfit[1] = gTakeProfit[0];
  } else {
    gLots[1] = SellLots;
    gBooster[1] = SellBooster;
    gPipStarter[1] = SellPipStarter;
    gTakeProfit[1] = SellTakeProfit;
  }

  int i;
  if (Digits == 5 || Digits == 3) {
    for (i = 0; i < 2; i++) {
      gPipStarter[i] *= 10.0;
      gTakeProfit[i] *= 10.0;
    }
    iStopLoss *= 10.0;
    PriceGapMinimalTP *= 10.0;
    PriceGapOrderDistance *= 10.0;
    TrailingSL *= 10.0;
    TrailingStep *= 10.0;
    TrailingMinProfit *= 10.0;
    MoveTP *= 10.0;
    TakeProfitAfterSL *= 10.0;
    
    LockStep *= 10.0;
    LockStepWP *= 10.0;
    LockSL *= 10.0;
    LockTP *= 10.0;
    LockExtraProfit *= 10.0;
  }

  LimitPipToPrice(iStopLoss);
  LimitPipToPrice(PriceGapMinimalTP);
  PriceGapOrderDistance = PipToPrice(PriceGapOrderDistance);
  gMinimalGap = PriceGapMinimalTP + PriceGapOrderDistance;
  gUseGapTP = PriceGapOrderDistance > 0;

  gTrailingThreshold = TrailingSL + TrailingStep;
  LimitPipToPrice(TrailingSL);
  LimitPipToPrice(gTrailingThreshold);
  TrailingMinProfit = PipToPrice(TrailingMinProfit);
  MoveTP = PipToPrice(MoveTP);
  TakeProfitAfterSL = PipToPrice(TakeProfitAfterSL);

  for (i = 0; i < 2; i++) {
    gPipStarter[i] = PipToPrice(gPipStarter[i]);
    gTakeProfit[i] = PipToPrice(gTakeProfit[i]);
    gPipStarterInv[i] = 1.0 / ((gBooster[i] - 1.0) * gTakeProfit[i]);
    gSumLots[i] = 0;
  }
  AnalyzeOrders();

  // for "trade time" check
  gStartMinutes = 60 * StartHour + StartMinute;
  gStopMinutes = 60 * StopHour + StopMinute;
  if (gStartMinutes > gStopMinutes) {
    int tmp = gStartMinutes;
    gStartMinutes = gStopMinutes;
    gStopMinutes = tmp;
  }

  // for "close by percentage" check
  StopLossPct = 1.0 - 0.01 * StopLossPct;
  TakeProfitPct = 1.0 + 0.01 * TakeProfitPct;

  // for "no money" check
  gTicksPerPrice = 1.0 / MarketInfo(TradeChart, MODE_TICKSIZE);
  gPriceValue = MarketInfo(TradeChart, MODE_TICKVALUE) * gTicksPerPrice;
  gLotMargin = MarketInfo(TradeChart, MODE_MARGINREQUIRED);
  gBaseLotMargin = MarketInfo(TradeChart, MODE_LOTSIZE) / AccountLeverage();

  // for lot size correction, if gap > slippage
  gSlippage = Point * Slippage;

  if (gIsNotOptimization) Log("Initialization finished. TradeChart = " + TradeChart);
  else EA_Name = "";

  sMaxTotalLots = 0;
  return (0);
}

bool IsTradeTime() {
  int day = DayOfWeek();
  if (day < StartingTradeDay || day > EndingTradeDay) return (false);
  if (gStartMinutes == gStopMinutes) return (true);
  datetime now = TimeCurrent();
  int minuntes = 60 * TimeHour(now) + TimeMinute(now);
  return (gStartMinutes <= minuntes && minuntes < gStopMinutes);
}

double RequiredBalance(double new_lot, double sl, double lots_opened = 0) {
  return (new_lot * gLotMargin + (new_lot + lots_opened) * sl * gPriceValue);
}

int StrategySignal() {
  double sar = iSAR(TradeChart, PERIOD_H1, SARStep, gSARMaximum, 0);
  double ma = iMA(TradeChart, PERIOD_H1, MAPeriod, gMAShift, gMAMethod, gMAPrice, 0);
  if (sar > ma) return (-1);
  if (sar < ma) return (1);
  return (0);
}

// set order limits, return true in case of error
bool SetOrderLimits(double takeprofit = 0, double stoploss = 0) {
  if (OrderCloseTime() != 0) return (false);

  bool skip_modify = true;
  double tp = OrderTakeProfit(), sl = OrderStopLoss();
  if (takeprofit > 0 && tp != takeprofit) {
    skip_modify = false;
    tp = takeprofit;
  }
  if (stoploss > 0 && sl != stoploss) {
    skip_modify = false;
    sl = stoploss;
  }
  if (skip_modify) return (false);

  skip_modify = !OrderModify(OrderTicket(), OrderOpenPrice(), sl, tp, 0, Pink) && GetLastError() > ERR_NO_RESULT;
  if (skip_modify && gIsNotOptimization) {
    Log("OrderModify error! Ticket #" + OrderTicket() +
        ", OpenPrice: " + DoubleToStr(OrderOpenPrice(), Digits) +
        ", TP: " + DoubleToStr(tp, Digits) +
        ", SL: " + DoubleToStr(sl, Digits));
  }
  return (skip_modify);
}

// return true, if repeat is needed
bool SetOrdersTakeProfit(int type, double takeprofit) {
  bool repeat_needed = false;
  double price, tp;
  if (gIsNotTesting) RefreshRates();
  if (type == OP_BUY) price = Bid;
  else price = Ask;
  if (MathAbs(takeprofit - price) < gStopLevel) {
    repeat_needed = true;
    if (type == OP_BUY) tp = price + gStopLevel;
    else tp = price - gStopLevel;
  } else tp = takeprofit;
  int pos = gOrders[type] - 1;
  if (gLastTicket[type] == gGapOrderTicket[type] && !gMoveTPMode[type]) pos--;
  for (; pos >= 0; pos--) {
    if (!OrderSelect(gTickets[type][pos], SELECT_BY_TICKET) || OrderTakeProfit() == takeprofit) continue;
    if (gIsNotTesting && RefreshRates()) {
      if (type == OP_BUY) price = Bid;
      else price = Ask;
      if (MathAbs(takeprofit - price) < gStopLevel) {
        repeat_needed = true;
        if (type == OP_BUY) tp = price + gStopLevel;
        else tp = price - gStopLevel;
      } else tp = takeprofit;
    }
    if (SetOrderLimits(tp, 0)) repeat_needed = true;
  }
  return (repeat_needed);
}

void TrailPyramid(int type) {
  if (gLevel[type] < TrailingStartLevel || !OrderSelect(gLastTicket[type], SELECT_BY_TICKET) || OrderCloseTime() != 0) return;

  double price, delta = OrderOpenPrice();
  if (type == OP_BUY) {
    price = Bid;
    delta = price - delta;
  } else {
    price = Ask;
    delta = delta - price;
  }
  if (delta < TrailingMinProfit) return;

  delta = OrderStopLoss();
  double sl = TrailingSL;
  if (type == OP_BUY) {
    delta = price - delta;
  } else {
    delta = delta - price;
    sl = -sl;
  }
  if (delta < gTrailingThreshold) return;
  sl = price - sl;
  price += sl;
  delta = OrderTakeProfit();
  if (type == OP_BUY) {
    if (price <= delta) price = 0;
  } else {
    if (price >= delta) price = 0;
  }
  SetOrderLimits(price, sl);
//   Log("Trail " + type + " #" + gLastTicket[type] + ", SL: " + DoubleToStr(OrderStopLoss(), Digits) + " -> " + DoubleToStr(sl, Digits));
}

bool OpenDeal(int type, double last_open_price, double gap) {
  int new_ticket, level = gLevel[type];
  double tp, sl, price, lots = gLots[type], booster = gBooster[type];
  bool is_opened = true;
  color arrow_color;
  string msg;

  if (type == OP_BUY) {
    price = Ask;
    arrow_color = Green;
    msg = "BUY";
  } else {
    price = Bid;
    arrow_color = Pink;
    msg = "SELL";
  }

  // calculate order lots
  if (level > 0) {
    if (level > 1) lots *= MathPow(booster, level);
    else if (Order2Booster || gOrders[type] > 1) lots *= booster;
    // price gap case
    if (gap > gSlippage && (gap < gMinimalGap || !gUseGapTP)) lots *= (1.0 + gap * gPipStarterInv[type]);
  }
  lots = NormalizeDouble(lots, gLotStepDigits);

  // check for "no money"
  tp = RequiredBalance(lots, gPipStarter[type], gSumLots[type]);
  if (AccountFreeMargin() < tp) {
    if (!gLowBalance) {
      gLowBalance = true;
      gBalanceNeeded = AccountMargin() + tp;
      if (gIsNotOptimization) Log("ERROR! " + msg + " order canceled! Balance < " + gBalanceNeeded);
    }
    return (false);
  }
  gLowBalance = false;

  new_ticket = OrderSend(TradeChart, type, lots, price, Slippage, 0, 0, EA_Name, MagicNumber, 0, arrow_color);
  if (new_ticket < 0) {
    if (gIsNotOptimization) Log("FAILED " + msg + ", lots: " + lots + ", sl: " + sl + ", tp: " + tp);
    is_opened = false;
  } else {
    gTickets[type][gOrders[type]] = new_ticket;
    gLastTicket[type] = new_ticket;
    gOrders[type]++;
    gGapOrderTicket[type] = 0;
    if (OrderSelect(new_ticket, SELECT_BY_TICKET)) {
      price = OrderOpenPrice();
      tp = gTakeProfit[type];
      if (gap >= gMinimalGap && gUseGapTP) {
        gap = last_open_price - price;
        if (gap < 0) gap = -gap;
        gap -= gPipStarter[type];
        if (gap >= gMinimalGap) {
          gGapOrderTicket[type] = new_ticket;
          tp = gap - PriceGapOrderDistance;
        }
      }
      if (type == OP_BUY) {
        tp = price + tp;
        sl = price - iStopLoss;
      } else {
        tp = price - tp;
        sl = price + iStopLoss;
      }
      if (!AllowiStopLoss) sl = 0;
    }
    gSumLots[type] += lots;
    gSumPriceLots[type] += lots * price;
    if (gOrders[type] > 1) {
      gMeanPrice[type] = NormalizeDouble(gSumPriceLots[type] / gSumLots[type], Digits);
      CalcLevel(type);
    } else {
      gMeanPrice[type] = price;
      gLevel[type] = 1;
    }
    if (gMoveTPMode[type] && gLevel[type] >= BreakEvenLevel) {
      tp = gMeanPrice[type];
      if (type == OP_BUY) tp += MoveTP;
      else tp -= MoveTP;
    }
    OrderModify(new_ticket, price, sl, tp, 0, Black);
    if (gGapOrderTicket[type] == 0 || gMoveTPMode[type]) {
      gLastTP[type] = tp;
      gModifiedTP[type] = (gOrders[type] > 1);
    }
    if (gIsNotOptimization) {
      lots = gSumLots[0] + gSumLots[1];
      if (sMaxTotalLots < lots) sMaxTotalLots = lots;
    }
  }
  return (is_opened);
}

void ManageDeals(int type) {
  if (gIsNotTesting && !GlobalVariableSetOnCondition(gMutexName, MagicNumber, 0)) return;

  bool allow_first_order, has_open_order = false;
  double dd, price;
  int n_max, pos;

  // check that pyramid is open
  for (pos = gOrders[type] - 1; pos >= 0; pos--) {
    if (!OrderSelect(gTickets[type][pos], SELECT_BY_TICKET)) continue;
    if (OrderCloseTime() == 0) {
      has_open_order = true;
      break;
    } else {
      dd = OrderLots();
      gSumLots[type] -= dd;
      gSumPriceLots[type] -= dd * OrderOpenPrice();
    }
  }

  if (has_open_order) {
    price = OrderOpenPrice();
    // some orders were closed
    if (gOrders[type] != pos + 1) {
      gLastTicket[type] = gTickets[type][pos];
      gOrders[type] = pos + 1;
      CalcLevel(type);
      if (pos > 0) gMeanPrice[type] = NormalizeDouble(gSumPriceLots[type] / gSumLots[type], Digits);
      else gMeanPrice[type] = price;

      dd = TakeProfitAfterSL;
      if (type == OP_SELL) dd = -dd;
      gLastTP[type] = price + dd;
      gModifiedTP[type] = true;
    }

    // manage open deals
    if (type == OP_BUY) {
      n_max = BuyMaxOrders;
      dd = price - Ask;
    } else {
      n_max = SellMaxOrders;
      dd = Bid - price;
    }
    if (dd > gPipStarter[type] && (gLevel[type] < n_max || AllowExtraOrders)) {
      dd -= gPipStarter[type];
      OpenDeal(type, price, dd);
    } else if (gModifiedTP[type]) {
      gModifiedTP[type] = SetOrdersTakeProfit(type, gLastTP[type]);
    } else if (TrailingOrders) {
      TrailPyramid(type);
//       TrailTP(type);
    }
    
    if (ManageLocks && (gOrders[type] >= n_max)) {
      ManageLocks(type, price, dd/Point);  
      }
     
  } else {
    gOrders[type] = 0;

    // try to open first order
    if (type == OP_BUY) {
      pos = 1;
      allow_first_order = Allow1Buy;
    } else {
      pos = -1;
      allow_first_order = Allow1Sell;
    }
    if ((!UseIndicators || StrategySignal() == pos) && IsTradeTime() && allow_first_order) {
      gLevel[type] = 0;
      gSumLots[type] = 0;
      gSumPriceLots[type] = 0;
      OpenDeal(type, 0, 0);
    }
  }
  if (gIsNotTesting) GlobalVariableSet(gMutexName, 0);
}

double BalanceDrawdown() {
  double dd = 100.0 * (1.0 - AccountEquity() / AccountBalance());
  if (dd < 0.01) dd = 0;
  return (dd);
}

void ChartComment() {
  string msg =
    EA_Name + " Loaded Successfully" +
    "\nAccount Leverage:  1 : " + AccountLeverage() +
    "\nAccount Type:  " + AccountServer() +
    "\nServer Time:  " + TimeToStr(TimeCurrent(), TIME_SECONDS) +
    "\nAccount Equity:  " + DoubleToStr(AccountEquity(), 2) +
    "\nFree Margin:  " + DoubleToStr(AccountFreeMargin(), 2) +
    "\nDrawdown:  " + DoubleToStr(BalanceDrawdown(), 2) + "%";
  if (gLowBalance) {
    msg = msg + "\n\nLOW BALANCE! Needed:  " + DoubleToStr(gBalanceNeeded, 2);
  }
  Comment(msg+GetStatInfo());
}

void CloseByPercentage() {
  bool need_close = false;
  if (AccountEquity() < StopLossPct * AccountBalance()) {
    if (gIsNotOptimization) Log("Percentage Stop Loss: Balance = " + AccountBalance() + ", Equity = " + AccountEquity());
    need_close = true;
  }
  if (AccountEquity() > TakeProfitPct * AccountBalance()) {
    if (gIsNotOptimization) Log("Percentage Take Profit: Balance = " + AccountBalance() + ", Equity = " + AccountEquity());
    need_close = true;
  }
  if (need_close) {
    gCloseAllMode[0] = true;
    gCloseAllMode[1] = true;
  }
}

int start() {
  if (gIsNotReady) return (1);
  if (gIsNotOptimization) {
    ChartComment();
    CloseByPercentage();
    for (int i = 0; i < 2; i++) if (gCloseAllMode[i]) gCloseAllMode[i] = CloseAll(i);
    if (gCloseAllMode[0] || gCloseAllMode[1]) return (2);
  }
  if (gIsNotTesting) {
    gPriceValue = MarketInfo(TradeChart, MODE_TICKVALUE) * gTicksPerPrice;
    gLotMargin = MarketInfo(TradeChart, MODE_MARGINREQUIRED);
    gStopLevel = MarketInfo(TradeChart, MODE_STOPLEVEL) * Point;
  } else {
    gLotMargin = gBaseLotMargin * Ask;
  }
  ManageDeals(OP_BUY);
  ManageDeals(OP_SELL);
  LockModify(OP_BUY);
  LockModify(OP_SELL);
  return (0);
}

double GetProfitOnPrice(int type, double price) {
 if (gOrders[type] < 1) { return (0); }
 double sum=0;
 for(int n=0; n<gOrders[type]; n++ ) {
    OrderSelect(gTickets[type][n], SELECT_BY_TICKET);
    sum+=(price-OrderOpenPrice())/Point*OrderLots();
 }
 if(type==OP_BUY)
   return (sum);
 else
   return (-sum);
}


double GetLotForLock(int type, double price_open_lock, double tp_pips) {
 if (gOrders[type] < 1) { return (0); }
 double sum=0;
 double lot=0;
 
 int k=1;
 if(type==OP_BUY)
   k = -1;
   
 for(int n=0; n<gOrders[type]; n++ ) {
    OrderSelect(gTickets[type][n], SELECT_BY_TICKET);
    //sum+=( (price_open_lock-OrderOpenPrice())/Point+tp_pips)*OrderLots()*Point*10000; // /price_close //sell
    sum+=( (k*(price_open_lock-OrderOpenPrice()))/Point+tp_pips)*OrderLots()*Point*10000; // /price_close //buy
 }
 //if(type==OP_BUY)
   //lot = -sum/ (tp_pips*Point*10000);
 //else
   lot= sum/ (tp_pips*Point*10000);

   //double LockLotProfitProcente=1;
   //lot+= lot*LockLotProfitProcente/100;
   
   lot=NormalizeDouble(lot, gLotStepDigits);
   //Print("GetLotForLock type: "+type+" price_open: "+price_open_lock+" pips: "+tp_pips+" lot="+lot);
 //return ( (gSumLots[type]*3));
   return (lot);   
}


string GetStatInfo() {
 string info;
 info="\n=== Статистика ===\n";
 for(int type=0; type<2; type++) {
   if(type==0) info=info+"BUY:\n";
   else info=info+"SELL:\n";
   info=info+"Кол-во открытых колен: "+gOrders[type]+"\n";
   //info=info+"Планируемая прибыль: "+DoubleToStr(GetProfitOnPrice(type, gLastTP[type]),2)+"\n";
   info=info+"Планируемая прибыль: "+DoubleToStr(GetProfitOnPrice(type, gLastTP[type]),6)+"\n";
   //info=info+"Возможный убыток: "+DoubleToStr(GetProfitOnPrice(type, gLastSL[type]),2)+"\n";
   info=info+"Локирующий лот: "+DoubleToStr(GetLotForLock(type, Bid, (LockTP-LockExtraProfit)),2)+"\n";
 }

return (info);
}

void ManageLocks(int type, double price_last_order, double current_pips) {
 double gLockStep[2];
 double gLockTP[2];
 gLockStep[type]=LockStep;
 gLockTP[type]=LockTP;

 LockModify(type);
 //Print("LAST ORDER:"+type+" "+price_last_order+" "+Bid+" "+current_pips);

 if(current_pips>gLockStep[type])
 { int n_max;
   if (type == OP_BUY) {
      n_max = BuyMaxOrders;
    } else {
      n_max = SellMaxOrders;
    } 
   if(LockIsOpened(type)<0 && (gLevel[type] >= n_max)) { 
   // открываем новый лок
      //Print("ОТКРЫВАЙ ЛОК1!!!!!!!!!");
      double lot = GetLotForLock(type, Bid, gLockTP[type]-LockExtraProfit);
      Print("NEW LOCK: "+type+" lot="+lot+" "+(gLockTP[type]-LockExtraProfit));
      double price_lock=LockOpen(type, lot);
      LockModify(type);
      //Print("ОТКРЫВАЙ ЛОК2!!!!!!!!!");
   } 
   
      
 }

}

int LockIsOpened(int type) {
if (type == OP_BUY)
   type = OP_SELL;
else 
   type = OP_BUY;
   
  int i, n = OrdersTotal();
  // find orders tickets
  for (i = 0; i < n; i++) {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES) || OrderMagicNumber() != LockMagicNumber) continue;
    if(OrderType()==type)
      return (OrderTicket());
  }
return (-1);
}


double LockOpen(int type, double lots) 
{
if (type == OP_BUY)
   type = OP_SELL;
else 
   type = OP_BUY;

double price, sl=0, tp=0;
color arrow_color;
string msg;


  if (type == OP_BUY) {
    price = Ask;
    arrow_color = Green;
    msg = "BUY";
  } else {
    price = Bid;
    arrow_color = Pink;
    msg = "SELL";
  }
 //Print ("LOTS!! "+lots);
  int new_ticket = OrderSend(TradeChart, type, lots, price, Slippage, 0, 0, EA_Name, LockMagicNumber, 0, arrow_color);
  if (new_ticket < 0) {
    if (gIsNotOptimization) Print("FAILED " + msg + ", lots: " + lots + ", sl: " + sl + ", tp: " + tp);
  } else {
    if (OrderSelect(new_ticket, SELECT_BY_TICKET)) {
      price = OrderOpenPrice();
      return (price);
    } 
  } 
}

bool LockModify(int type) {
 /*   if (OrderSelect(new_ticket, SELECT_BY_TICKET)) {
      SetOrderLimits(tp, sl);
      return (1);
    } 
 return(0);
*/
 int i,n=OrdersTotal();
 double priceTP, priceSL;
 for (i = 0; i < n; i++) {
    if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES) || OrderMagicNumber() != LockMagicNumber || OrderType() ==type) continue;

            //double VMin=MarketInfo(Symbol(),MODE_POINT)* (MarketInfo(Symbol(),MODE_STOPLEVEL)+Slippage); 
            double VStopLossLong=Bid- VMin ;
            double VTakeProfitLong=Ask+ VMin ;
            double VStopLossShort=Ask+ VMin ;
            double VTakeProfitShort=Bid-VMin ;
            
            
            if( OrderType() == OP_BUY ){
               if(Ask < OrderOpenPrice()-LockSL*Point || gOrders[type]< SellMaxOrders) {
                    OrderClose(OrderTicket(), OrderLots(), Bid, Slippage,Blue);
                    return(0);
                  }
                   priceTP= OrderOpenPrice()+LockTP*Point;
                   priceSL= OrderOpenPrice()-LockSL*Point;
               
               if(priceTP<VTakeProfitLong) {    
                     priceTP = VTakeProfitLong;
                     }
               if(priceSL>VStopLossLong)   { 
                     priceSL = VStopLossLong;
                     }
               if(OrderStopLoss()==0 || OrderTakeProfit()==0)    {
                  SetOrderLimits(priceTP, priceSL);
                  SetOrdersSL(type, priceTP); //устанавливаем СЛ для пирамиды
               }
            }
            else if( OrderType() == OP_SELL ){
               
               if(Bid > OrderOpenPrice()+ LockSL*Point || gOrders[type]< BuyMaxOrders) {
                  OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, Red);
                  return(0);
                  }
                  
               priceTP=OrderOpenPrice()-LockTP*Point;
               priceSL=OrderOpenPrice()+LockSL*Point;          
               
               if(priceTP>VTakeProfitShort)    {
                     priceTP = VTakeProfitShort;
                     }
               if(priceSL<VStopLossLong)    {
                     priceSL = VStopLossLong;
                     }
               if(OrderStopLoss()==0 || OrderTakeProfit()==0)    {
                  SetOrderLimits(priceTP, priceSL);
                  SetOrdersSL(type, priceTP); //устанавливаем СЛ для пирамиды
               }
               //OrderModify(OrderTicket(),OrderOpenPrice(),priceSL,priceTP,Slippage,Red);
            }   
    


 }

}

int SetOrdersSL(int type, double SL) {
 int pos = gOrders[type] - 1;
 for (; pos >= 0; pos--) {
    if (!OrderSelect(gTickets[type][pos], SELECT_BY_TICKET) || OrderTakeProfit() == SL) continue;
    if (SetOrderLimits(0, SL)) return (1);
  }
 return(0);
}