//+------------------------------------------------------------------+
//|                                               PartialCloseEA.mq5 |
//+------------------------------------------------------------------+

#include <PartialCloseEngine/PartialCloseEngine.mqh>
#include <PartialCloseEngine/LadderLevel.mqh>

input double InpStopLossPoints = 500;      // Stop loss distance in points
input double InpLotSize        = 0.10;     // Entry lot size
input int    InpMagicNumber    = 20260609; // Magic Number

CPartialCloseEngine g_engine;
ulong               g_managed_ticket = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   g_engine.SetChartId(::ChartID());

   if(!OpenDemoPosition())
     {
      ::Print("PartialCloseEA: failed to open demo position, EA will idle");
      return(INIT_SUCCEEDED);
     }

   CLadderLevel levels[3];
   levels[0] = CLadderLevel(1.0, 50.0, true);
   levels[1] = CLadderLevel(2.0, 25.0, false);
   levels[2] = CLadderLevel(3.0, 25.0, false);

   if(g_engine.Register(g_managed_ticket, levels, 3))
     {
      ::PrintFormat("PartialCloseEA: registered ticket=%I64u with a three-level ladder "
                    "(1R/50%%/BE, 2R/25%%, 3R/25%%)", g_managed_ticket);
     }

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| SelectFillingMode                                                |
//| Picks a filling mode the symbol actually supports. Hardcoding    |
//| ORDER_FILLING_IOC causes the client terminal to reject the       |
//| request outright (ERR_TRADE_SEND_FAILED, error 4756) on any      |
//| symbol or broker that only supports FOK or Return fills.         |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING SelectFillingMode(const string symbol)
  {
   long filling_flags = ::SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);

   if((filling_flags & SYMBOL_FILLING_FOK) != 0)
      return(ORDER_FILLING_FOK);
   if((filling_flags & SYMBOL_FILLING_IOC) != 0)
      return(ORDER_FILLING_IOC);

//--- neither FOK nor IOC advertised, fall back to Return, which is
//--- always accepted on exchange/market execution accounts
   return(ORDER_FILLING_RETURN);
  }

//+------------------------------------------------------------------+
//| OpenDemoPosition                                                 |
//| Opens a single market position with a hardcoded stop loss and    |
//| stores its ticket for registration with the engine.              |
//+------------------------------------------------------------------+
bool OpenDemoPosition(void)
  {
   string symbol = ::Symbol();
   double point  = ::SymbolInfoDouble(symbol, SYMBOL_POINT);
   double ask    = ::SymbolInfoDouble(symbol, SYMBOL_ASK);
   int    digits = (int)::SymbolInfoInteger(symbol, SYMBOL_DIGITS);

   double sl = ::NormalizeDouble(ask - InpStopLossPoints * point, digits);

   MqlTradeRequest request;
   MqlTradeResult  result;
   ::ZeroMemory(request);
   ::ZeroMemory(result);

   request.action       = TRADE_ACTION_DEAL;
   request.symbol       = symbol;
   request.volume       = InpLotSize;
   request.type         = ORDER_TYPE_BUY;
   request.price        = ask;
   request.sl           = sl;
   request.tp           = 0.0;
   request.deviation    = 10;
   request.magic        = InpMagicNumber;
   request.type_filling = SelectFillingMode(symbol);

   if(!::OrderSend(request, result))
     {
      ::PrintFormat("PartialCloseEA: OrderSend failed, error=%d", ::GetLastError());
      return(false);
     }

   if(result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_PLACED)
     {
      ::PrintFormat("PartialCloseEA: order rejected, retcode=%d comment=%s",
                    result.retcode, result.comment);
      return(false);
     }

   g_managed_ticket = result.order;

//--- the position ticket is the same as the deal's order ticket for a
//--- market fill; re-select to confirm before handing off to the engine
   if(!::PositionSelectByTicket(g_managed_ticket))
     {
      ::PrintFormat("PartialCloseEA: could not select new position, ticket=%I64u",
                    g_managed_ticket);
      return(false);
     }

   ::PrintFormat("PartialCloseEA: opened ticket=%I64u volume=%.2f entry=%.5f sl=%.5f",
                 g_managed_ticket, InpLotSize, ask, sl);
   return(true);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   if(g_managed_ticket == 0)
      return;

   g_engine.OnTick();
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_managed_ticket != 0)
      g_engine.Deregister(g_managed_ticket);
  }
//+------------------------------------------------------------------+