//+------------------------------------------------------------------+
//|                                                       Martyr.mq4 |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <TradeUtils.mqh>

input int MagicNumber   = 102030;
input double TakeProfitPips = 10;
input int SacrificePercent = 30;
input bool StraightToNegative = true;
#include <LotManagerFullDefine.mqh>
input bool EnforceSpacing = true;
#include <StepManagerFullDefine.mqh>
#include <MQL4OrderPool.mqh>

SymbolInfo* symbolInfo;
TradeStats* tradeStats;
TradeUtils* tradeUtils;
LotManager* lotManager;
StepManager* stepManager;

//datetime lastBarTime;
double historyProfit;
int lastHistoryCount;
datetime lastTimeOpen[2];
double takeProfitDouble;
double sacrifice;
int selectedWorst;

int OnInit() {
   symbolInfo = new SymbolInfo(_Symbol);
   tradeStats = new TradeStats(symbolInfo, MagicNumber);
   tradeUtils = new TradeUtilsImpl(symbolInfo);
   lotManager = CreateLotManager(symbolInfo);   
   stepManager = CreateStepManager(symbolInfo);
   lastHistoryCount = 0;
   tradeStats.UpdatePosition();
   for(int type = OP_BUY; type <= OP_SELL; type++){
      if(tradeStats.GetCount(type) > 0) {
         lastTimeOpen[type] = tradeStats.GetLastTrade(type).GetTimeOpen();
         lotManager.SetGridStartLot(type, tradeStats.GetFirstTrade(type).GetLots());
      }
   }
   selectedWorst = -1;
   takeProfitDouble = TakeProfitPips * symbolInfo.pip;
   sacrifice = SacrificePercent / 100.0;
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   delete(tradeUtils);
   delete(tradeStats);
   delete(symbolInfo);   
   delete(lotManager);
   delete(stepManager);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   MqlTick mqlTick;
   SymbolInfoTick(_Symbol, mqlTick);
   tradeStats.UpdatePosition();
   if(lastTimeOpen[OP_BUY] < iTime(NULL, 0, 0)
   && iClose(NULL, 0, 0) > iHigh(NULL, 0, 1)
   && stepManager.IsAllowedToOpen(tradeStats, OP_BUY, mqlTick)) {
      if(tradeUtils.OpenOrderStopsInPips(OP_BUY, MagicNumber, lotManager.GetLots(OP_BUY, tradeStats.GetCount(OP_BUY)), 0.0, TakeProfitPips) > 0)
         lastTimeOpen[OP_BUY] = TimeCurrent();
   } else if(lastTimeOpen[OP_SELL] < iTime(NULL, 0, 0)
   && iClose(NULL, 0, 0) < iLow(NULL, 0, 1)
   && stepManager.IsAllowedToOpen(tradeStats, OP_SELL, mqlTick)) {
      if(tradeUtils.OpenOrderStopsInPips(OP_SELL, MagicNumber, lotManager.GetLots(OP_SELL, tradeStats.GetCount(OP_SELL)), 0.0, TakeProfitPips) > 0)
         lastTimeOpen[OP_SELL] = TimeCurrent();
   }
   Trade* worst = selectedWorst >= 0 ? tradeStats.FindTicket(selectedWorst) : NULL;
   if(CheckPointer(worst) == POINTER_INVALID) {
      selectedWorst = -1;
      double worstDDdistance[2] = {0.0, 0.0};
      for(int type = OP_BUY; type <= OP_SELL; type++)
         if(tradeStats.GetCount(type) > 0)
            worstDDdistance[type] = sgn(type) * (tradeStats.GetWorstLocatedTrade(type).GetBreakEven(mqlTick, tradeStats.GetTickValue(type), tradeStats.GetTickSize()) - cp(mqlTick, type));
      int ddType = -1;
      if(worstDDdistance[OP_BUY] > DBL_EPSILON)
         ddType = OP_BUY;
      else
         worstDDdistance[OP_BUY] = 0.0;
      if(worstDDdistance[OP_SELL] > worstDDdistance[OP_BUY])
         ddType = OP_SELL;
      if(ddType >= OP_BUY) {
         worst = tradeStats.GetWorstLocatedTrade(ddType);
         selectedWorst = worst.GetTicket();
      }
   }
   if(CheckPointer(worst) != POINTER_INVALID) {
      double allowance = GetHistoryProfit();
      if(allowance > DBL_EPSILON) {
         double giveUpDistance = allowance * sacrifice * tradeStats.GetTickSize() / tradeStats.GetTickValue(worst.GetType()) / worst.GetLots();
         double tp = NormalizeDouble(worst.GetBreakEven(mqlTick, tradeStats.GetTickValue(worst.GetType()), tradeStats.GetTickSize()) 
            + sgn(worst.GetType()) * ((StraightToNegative ? 0.0 : takeProfitDouble) - giveUpDistance), symbolInfo.digits);
         if(tradeUtils.VerifyTakeProfit(worst.GetType(), tp, mqlTick)
         && NormalizeDouble(sgn(worst.GetType()) * (worst.GetTakeProfit() - tp), symbolInfo.digits) > symbolInfo.point)
            tradeUtils.ModifyTicket(worst.GetTicket(), worst.GetStopLoss(), tp, 0.1);
      }
   }
}
//+------------------------------------------------------------------+
double GetHistoryProfit() {
   if(lastHistoryCount < OrdersHistoryTotal()) {
      lastHistoryCount = OrdersHistoryTotal();
      historyProfit = 0;
      MQL4OrderPool pool;
      pool.Init(Symbol(), MagicNumber, MODE_HISTORY, ORDERS_FILLED, SORT_CLOSE_TIME_DESCENDING);
      for(int i = 0; i < pool.Total(); i++) {
         if(pool[i].OrderProfit() > 0)
            historyProfit += pool[i].OrderProfit() + pool[i].OrderSwap() + pool[i].OrderCommission();
         else
            break;
      }
   }
   return (historyProfit);
}