//+------------------------------------------------------------------+
//|                                             BreakevenManager.mqh |
//+------------------------------------------------------------------+
#ifndef BREAKEVENMANAGER_MQH
#define BREAKEVENMANAGER_MQH

//+------------------------------------------------------------------+
//| CBreakevenManager                                                |
//| Moves a position's stop loss to breakeven after the first        |
//| qualifying ladder level is confirmed closed. The new stop is set |
//| one point past the original entry price rather than exactly at   |
//| it, because a stop placed exactly at entry can still be filled at|
//| a small loss once spread and slippage are applied on the closing |
//| side. One point is enough to guarantee the remaining volume can  |
//| only be closed at a profit or scratch, never a loss.             |
//+------------------------------------------------------------------+
class CBreakevenManager
  {
public:
                     CBreakevenManager(void);
                    ~CBreakevenManager(void);

   bool              MoveToBreakeven(const ulong position_ticket,
                                     const string symbol,
                                     const long position_type,
                                     const double entry_price,
                                     const double point,
                                     const double current_tp);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBreakevenManager::CBreakevenManager(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBreakevenManager::~CBreakevenManager(void)
  {
  }

//+------------------------------------------------------------------+
//| MoveToBreakeven                                                  |
//| Computes the breakeven stop and applies it with PositionModify().|
//| For a long the new SL is entry + 1 * point; for a short it is    |
//| entry - 1 * point, which is the mirror case. The take profit is  |
//| passed through unchanged since this method only ever touches the |
//| stop loss.                                                       |
//+------------------------------------------------------------------+
bool CBreakevenManager::MoveToBreakeven(const ulong position_ticket,
                                        const string symbol,
                                        const long position_type,
                                        const double entry_price,
                                        const double point,
                                        const double current_tp)
  {
   if(!::PositionSelectByTicket(position_ticket))
     {
      ::PrintFormat("CBreakevenManager: position %I64u no longer exists, skipping breakeven move",
                    position_ticket);
      return(false);
     }

   double old_sl = ::PositionGetDouble(POSITION_SL);

   double new_sl = (position_type == POSITION_TYPE_BUY)
                   ? entry_price + 1.0 * point
                   : entry_price - 1.0 * point;

   int digits = (int)::SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   new_sl = ::NormalizeDouble(new_sl, digits);

   MqlTradeRequest request;
   MqlTradeResult  result;
   ::ZeroMemory(request);
   ::ZeroMemory(result);

   request.action   = TRADE_ACTION_SLTP;
   request.position = position_ticket;
   request.symbol   = symbol;
   request.sl       = new_sl;
   request.tp       = current_tp;

   bool send_ok = ::OrderSend(request, result);

   if(send_ok && (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED))
     {
      ::PrintFormat("CBreakevenManager: ticket=%I64u old_sl=%.5f new_sl=%.5f",
                    position_ticket, old_sl, new_sl);
      return(true);
     }

   ::PrintFormat("CBreakevenManager: failed to move ticket=%I64u to breakeven, "
                 "old_sl=%.5f attempted_sl=%.5f retcode=%d comment=%s",
                 position_ticket, old_sl, new_sl, result.retcode, result.comment);
   return(false);
  }

#endif // BREAKEVENMANAGER_MQH
//+------------------------------------------------------------------+