//+------------------------------------------------------------------+
//|                                 KK_ManualTrader_Pro_v5.5.mq5     |
//|       PROFESSIONAL MANUAL TRADING ASSISTANT + STATS + BUTTONS    |
//|       v5.5 MT5: BreakEven sets SL to open price (zero offset)    |
//+------------------------------------------------------------------+
#property copyright "KK"
#property link      ""
#property version   "5.5"
#property strict

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\HistoryOrderInfo.mqh>
#include <Trade\DealInfo.mqh>

// === LOT SIZE ===
input group "══════ LOT SIZE ══════"
input double Default_LotSize = 0.01;
input double Force_Max_Lot = 50.0;

// === SL/TP (PERCENTAGE) ===
input group "══════ SL/TP (%) ══════"
input double SL_Percent = 0.1;
input double TP_Percent = 0.0;

// === AUTO BREAK EVEN ===
input group "══════ AUTO BREAK EVEN ══════"
input bool   UseAutoBE = false;
input double AutoBE_Trigger_Percent = 0.01;

// === TRAILING STOP (PERCENTAGE) ===
input group "══════ TRAILING STOP (%) ══════"
input bool   UseTrailingStop = false;
input bool   UseHiddenTrailing = false;
input double Trailing_Percent = 0.02;
input double Trailing_Step_Percent = 0.01;

// === HOTKEYS ===
input group "══════ HOTKEYS ══════"
input int Key_InstantReverse = 49;
input int Key_CloseChart = 50;
input int Key_CloseAll = 51;
input int Key_TrailingToggle = 52;
input int Key_AutoBEToggle = 53;

// === PENDING ORDERS (PERCENTAGE) ===
input group "══════ PENDING ORDERS (%) ══════"
input double PendingNear_Percent = 0.1;
input double PendingFar_Percent = 0.2;

// === DISPLAY POSITION ===
input group "══════ DISPLAY ══════"
input int Display_X = 10;
input int Display_Y = 20;
input int LineSpacing = 20;
input int FontSize = 10;

// === BUTTON SETTINGS ===
input group "══════ BUTTONS ══════"
input bool   ShowHotkeyButtons = true;
input bool   Button_TopCorner = true;
input int Button_X = 5;
input int Button_Y = 20;
input int Button_Width = 80;
input int Button_Height = 50;

// === DISPLAY COLORS ===
input group "══════ COLORS ══════"
input color Color_Title = clrWhite;
input color Color_TrailON = clrLime;
input color Color_TrailOFF = clrRed;
input color Color_Spread = clrYellow;
input color Color_BuyButton = clrGreen;
input color Color_SellButton = clrRed;
input color Color_Commission = clrAqua;

const int Slippage = 30;
const long BUTTON_ZORDER = 1000;

CTrade g_trade;
CPositionInfo g_position;
COrderInfo g_order;
CDealInfo g_deal;

bool g_trailingEnabled;
bool g_autoBEEnabled;
double g_lastBid = 0;
double g_lastAsk = 0;
double g_currentLotSize;
double g_pipValue = 0;
double g_myMaxLot = 0;

double g_commissionPerLot = 0;
bool g_commissionCalculated = false;

#define MAX_HIDDEN_POSITIONS 50
double g_hiddenSL[MAX_HIDDEN_POSITIONS];
ulong g_hiddenTickets[MAX_HIDDEN_POSITIONS];
int g_hiddenCount = 0;

int OnInit()
{
   g_trailingEnabled = UseTrailingStop;
   g_autoBEEnabled = UseAutoBE;

   g_trade.SetExpertMagicNumber(0);
   g_trade.SetDeviationInPoints(Slippage);
   g_trade.SetTypeFilling(ORDER_FILLING_IOC);

   if(Force_Max_Lot > 0) g_myMaxLot = Force_Max_Lot;
   else g_myMaxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);

   g_pipValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(_Digits == 3 || _Digits == 5) g_pipValue *= 10;

   g_currentLotSize = LoadLotSize();

   ArrayInitialize(g_hiddenSL, 0);
   ArrayInitialize(g_hiddenTickets, 0);
   g_hiddenCount = 0;
   g_commissionCalculated = false;
   g_commissionPerLot = 0;

   EventSetMillisecondTimer(100);

   CreateDisplay();
   CreateButtons();
   UpdateDisplay();
   CalculateCommission();

   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   EventKillTimer();
   ObjectsDeleteAll(0, "MTP_");
}

void OnTick()
{
   g_lastBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_lastAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(g_autoBEEnabled) ApplyAutoBreakEven();
   if(g_trailingEnabled) ManageTrailingStop();

   UpdateSpreadCommission();
}

void OnTimer()
{
   g_lastBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_lastAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   UpdateSpreadCommission();

   static datetime lastTimeCheck = 0;
   if(TimeCurrent() - lastTimeCheck > 5)
   {
      CalculateCommission();
      lastTimeCheck = TimeCurrent();
   }
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_KEYDOWN)
   {
      int key = (int)lparam;
      if(key == Key_InstantReverse) InstantReverse();
      else if(key == Key_CloseChart) CloseAllOnChart();
      else if(key == Key_CloseAll) CloseAllPositions();
      else if(key == Key_TrailingToggle) { g_trailingEnabled = !g_trailingEnabled; UpdateDisplay(); }
      else if(key == Key_AutoBEToggle)   { g_autoBEEnabled   = !g_autoBEEnabled;   UpdateDisplay(); }
   }

   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(sparam == "MTP_BuyBtn")    { OpenBuy();                                          ObjectSetInteger(0, "MTP_BuyBtn",    OBJPROP_STATE, false); }
      else if(sparam == "MTP_SellBtn")   { OpenSell();                                        ObjectSetInteger(0, "MTP_SellBtn",   OBJPROP_STATE, false); }
      else if(sparam == "MTP_LotPlus")   { IncreaseLot();                                     ObjectSetInteger(0, "MTP_LotPlus",   OBJPROP_STATE, false); }
      else if(sparam == "MTP_LotMinus")  { DecreaseLot();                                     ObjectSetInteger(0, "MTP_LotMinus",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_BE")        { ApplyBreakEven();                                   ObjectSetInteger(0, "MTP_BE",        OBJPROP_STATE, false); }
      else if(sparam == "MTP_RevBtn")    { InstantReverse();                                   ObjectSetInteger(0, "MTP_RevBtn",    OBJPROP_STATE, false); }
      else if(sparam == "MTP_ClsChBtn")  { CloseAllOnChart();                                  ObjectSetInteger(0, "MTP_ClsChBtn",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_ClsAllBtn") { CloseAllPositions();                                ObjectSetInteger(0, "MTP_ClsAllBtn", OBJPROP_STATE, false); }
      else if(sparam == "MTP_BL_N")  { OpenPending(ORDER_TYPE_BUY_LIMIT,  PendingNear_Percent); ObjectSetInteger(0, "MTP_BL_N",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_SL_N")  { OpenPending(ORDER_TYPE_SELL_LIMIT, PendingNear_Percent); ObjectSetInteger(0, "MTP_SL_N",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_BL_F")  { OpenPending(ORDER_TYPE_BUY_LIMIT,  PendingFar_Percent);  ObjectSetInteger(0, "MTP_BL_F",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_SL_F")  { OpenPending(ORDER_TYPE_SELL_LIMIT, PendingFar_Percent);  ObjectSetInteger(0, "MTP_SL_F",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_BS_N")  { OpenPending(ORDER_TYPE_BUY_STOP,   PendingNear_Percent); ObjectSetInteger(0, "MTP_BS_N",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_SS_N")  { OpenPending(ORDER_TYPE_SELL_STOP,  PendingNear_Percent); ObjectSetInteger(0, "MTP_SS_N",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_BS_F")  { OpenPending(ORDER_TYPE_BUY_STOP,   PendingFar_Percent);  ObjectSetInteger(0, "MTP_BS_F",  OBJPROP_STATE, false); }
      else if(sparam == "MTP_SS_F")  { OpenPending(ORDER_TYPE_SELL_STOP,  PendingFar_Percent);  ObjectSetInteger(0, "MTP_SS_F",  OBJPROP_STATE, false); }

      ChartRedraw(0);
   }

   if(id == CHARTEVENT_OBJECT_ENDEDIT && sparam == "MTP_LotEdit")
   {
      string lotStr = ObjectGetString(0, "MTP_LotEdit", OBJPROP_TEXT);
      double newLot = StringToDouble(lotStr);
      if(newLot >= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) && newLot <= g_myMaxLot)
      {
         g_currentLotSize = NormalizeLot(newLot);
         SaveLotSize(g_currentLotSize);
      }
      UpdateLotDisplay();
   }
}

int GetLotDigits()
{
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(lotStep >= 1.0) return 0;
   if(lotStep >= 0.1) return 1;
   if(lotStep >= 0.01) return 2;
   return 2;
}

double LoadLotSize()
{
   string filename = "MTP_LotSize_" + _Symbol + ".txt";
   double lotSize = Default_LotSize;

   if(FileIsExist(filename))
   {
      int handle = FileOpen(filename, FILE_READ|FILE_TXT|FILE_ANSI);
      if(handle != INVALID_HANDLE)
      {
         string content = FileReadString(handle);
         FileClose(handle);
         double savedLot = StringToDouble(content);
         if(savedLot >= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) && savedLot <= g_myMaxLot)
            lotSize = savedLot;
      }
   }
   return NormalizeLot(lotSize);
}

void SaveLotSize(double lotSize)
{
   string filename = "MTP_LotSize_" + _Symbol + ".txt";
   int handle = FileOpen(filename, FILE_WRITE|FILE_TXT|FILE_ANSI);
   if(handle != INVALID_HANDLE)
   {
      FileWriteString(handle, DoubleToString(lotSize, GetLotDigits()));
      FileClose(handle);
   }
}

double NormalizeLot(double lot)
{
   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot  = g_myMaxLot;
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(lotStep <= 0) lotStep = 0.01;
   if(lot < minLot) lot = minLot;
   if(lot > maxLot) lot = maxLot;
   return NormalizeDouble(MathRound(lot / lotStep) * lotStep, GetLotDigits());
}

void IncreaseLot()
{
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(lotStep <= 0) lotStep = 0.01;
   g_currentLotSize = NormalizeLot(g_currentLotSize + lotStep);
   SaveLotSize(g_currentLotSize);
   UpdateLotDisplay();
}

void DecreaseLot()
{
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(lotStep <= 0) lotStep = 0.01;
   g_currentLotSize = NormalizeLot(g_currentLotSize - lotStep);
   SaveLotSize(g_currentLotSize);
   UpdateLotDisplay();
}

void UpdateLotDisplay()
{
   ObjectSetString(0, "MTP_LotEdit", OBJPROP_TEXT, DoubleToString(g_currentLotSize, GetLotDigits()));
   ChartRedraw(0);
}

void CreateButton(string name, int x, int y, int width, int height,
                  string text, int fontSize, color textColor, color bgColor,
                  ENUM_BASE_CORNER corner, string font = "Arial")
{
   ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetString(0, name, OBJPROP_FONT, font);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, name, OBJPROP_COLOR, textColor);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bgColor);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_ZORDER, BUTTON_ZORDER);
}

void CreateButtons()
{
   int btnY = Button_Y;
   ENUM_BASE_CORNER corner = Button_TopCorner ? CORNER_LEFT_UPPER : CORNER_LEFT_LOWER;

   // Lot Size label
   ObjectCreate(0, "MTP_LotLabel", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_CORNER, corner);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_XDISTANCE, Button_X);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_YDISTANCE, Button_TopCorner ? btnY : btnY + 230);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_ANCHOR, Button_TopCorner ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER);
   ObjectSetString(0, "MTP_LotLabel", OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_COLOR, clrWhite);
   ObjectSetString(0, "MTP_LotLabel", OBJPROP_TEXT, "Lot Size:");
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, "MTP_LotLabel", OBJPROP_ZORDER, BUTTON_ZORDER);

   // Lot - button
   CreateButton("MTP_LotMinus", Button_X, Button_TopCorner ? btnY + 20 : btnY + 210,
                25, 25, "-", 12, clrWhite, clrDimGray, corner);

   // Lot edit field
   ObjectCreate(0, "MTP_LotEdit", OBJ_EDIT, 0, 0, 0);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_CORNER, corner);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_XDISTANCE, Button_X + 30);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_YDISTANCE, Button_TopCorner ? btnY + 20 : btnY + 210);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_XSIZE, 70);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_YSIZE, 25);
   ObjectSetString(0, "MTP_LotEdit", OBJPROP_TEXT, DoubleToString(g_currentLotSize, GetLotDigits()));
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_FONTSIZE, 11);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_ALIGN, ALIGN_CENTER);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_BGCOLOR, clrDarkSlateGray);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_BORDER_COLOR, clrGray);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, "MTP_LotEdit", OBJPROP_ZORDER, BUTTON_ZORDER);

   // Lot + button
   CreateButton("MTP_LotPlus", Button_X + 105, Button_TopCorner ? btnY + 20 : btnY + 210,
                25, 25, "+", 12, clrWhite, clrDimGray, corner);

   int mainBtnY  = btnY + 50;
   int wideBtnW  = Button_Width * 2 + 10;

   // BUY button
   CreateButton("MTP_BuyBtn", Button_X, mainBtnY, Button_Width, Button_Height,
                "BUY", 12, clrWhite, Color_BuyButton, corner, "Arial Bold");

   // SELL button
   CreateButton("MTP_SellBtn", Button_X + Button_Width + 10, mainBtnY, Button_Width, Button_Height,
                "SELL", 12, clrWhite, Color_SellButton, corner, "Arial Bold");

   int quickBtnY = mainBtnY + Button_Height + 10;

   // Break-Even button
   CreateButton("MTP_BE", Button_X, quickBtnY, wideBtnW, 25,
                "Break-Even (open price)", 9, clrWhite, clrCornflowerBlue, corner);

   int actionY = quickBtnY + 30;

   if(ShowHotkeyButtons)
   {
      CreateButton("MTP_RevBtn", Button_X, actionY, wideBtnW, 25,
                   "INSTANT REVERSE (1)", 9, clrWhite, clrGoldenrod, corner);
      actionY += 30;

      CreateButton("MTP_ClsChBtn", Button_X, actionY, wideBtnW, 25,
                   "CLOSE CHART (2)", 9, clrWhite, clrSlateGray, corner);
      actionY += 30;

      CreateButton("MTP_ClsAllBtn", Button_X, actionY, wideBtnW, 25,
                   "CLOSE ALL (3)", 9, clrWhite, clrMaroon, corner);
      actionY += 30;
   }

   // Pending order buttons
   int pendingBtnY = actionY;
   int pbX    = Button_X;
   int pBtnW  = 40;
   int pBtnGap = 4;

   string limitLabels[] = {"BL N", "SL N", "BL F", "SL F"};
   string limitNames[]  = {"MTP_BL_N", "MTP_SL_N", "MTP_BL_F", "MTP_SL_F"};

   for(int i = 0; i < 4; i++)
      CreateButton(limitNames[i], pbX + i * (pBtnW + pBtnGap), pendingBtnY,
                   pBtnW, 20, limitLabels[i], 8, clrWhite, clrTeal, corner);

   int stopBtnY = pendingBtnY + 25;
   string stopLabels[] = {"BS N", "SS N", "BS F", "SS F"};
   string stopNames[]  = {"MTP_BS_N", "MTP_SS_N", "MTP_BS_F", "MTP_SS_F"};

   for(int i = 0; i < 4; i++)
      CreateButton(stopNames[i], pbX + i * (pBtnW + pBtnGap), stopBtnY,
                   pBtnW, 20, stopLabels[i], 8, clrWhite, clrPurple, corner);

   ChartRedraw(0);
}

void OpenPending(ENUM_ORDER_TYPE orderType, double distancePercent)
{
   double currentPrice = (orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP) ? g_lastAsk : g_lastBid;
   double percentDist  = currentPrice * distancePercent / 100.0;
   long   stopLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double stopLevel    = stopLevelPoints * SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   if(percentDist < stopLevel) percentDist = stopLevel + SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10;

   double price = 0;
   if(orderType == ORDER_TYPE_BUY_LIMIT)  price = NormalizeDouble(g_lastAsk - percentDist, _Digits);
   else if(orderType == ORDER_TYPE_SELL_LIMIT) price = NormalizeDouble(g_lastBid + percentDist, _Digits);
   else if(orderType == ORDER_TYPE_BUY_STOP)  price = NormalizeDouble(g_lastAsk + percentDist, _Digits);
   else if(orderType == ORDER_TYPE_SELL_STOP) price = NormalizeDouble(g_lastBid - percentDist, _Digits);

   double sl = CalculateSL(orderType, price);
   double tp = CalculateTP(orderType, price);

   if(!g_trade.OrderOpen(_Symbol, orderType, g_currentLotSize, 0, price, sl, tp, ORDER_TIME_GTC, 0, "MTP Pending"))
      Print("OrderOpen Pending failed. Error: ", GetLastError());
   else
      Print("Pending order opened: Type: ", orderType, " @ ", price);
}

void CreateDisplay()
{
   int y = Display_Y;

   ObjectCreate(0, "MTP_Title", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_Title", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "MTP_Title", OBJPROP_XDISTANCE, Display_X);
   ObjectSetInteger(0, "MTP_Title", OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MTP_Title", OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   ObjectSetString(0, "MTP_Title", OBJPROP_FONT, "Arial Bold");
   ObjectSetInteger(0, "MTP_Title", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_Title", OBJPROP_COLOR, Color_Title);
   ObjectSetString(0, "MTP_Title", OBJPROP_TEXT, "ManualTrader Pro v5.5 MT5");
   ObjectSetInteger(0, "MTP_Title", OBJPROP_SELECTABLE, false);
   y += LineSpacing;

   ObjectCreate(0, "MTP_Trailing", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_XDISTANCE, Display_X);
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   ObjectSetString(0, "MTP_Trailing", OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_Trailing", OBJPROP_SELECTABLE, false);
   y += LineSpacing;

   ObjectCreate(0, "MTP_AutoBE", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_XDISTANCE, Display_X);
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   ObjectSetString(0, "MTP_AutoBE", OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_SELECTABLE, false);
   y += LineSpacing;

   ObjectCreate(0, "MTP_Spread", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_XDISTANCE, Display_X);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   ObjectSetString(0, "MTP_Spread", OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_COLOR, Color_Spread);
   ObjectSetInteger(0, "MTP_Spread", OBJPROP_SELECTABLE, false);
   y += LineSpacing;

   ObjectCreate(0, "MTP_Commission", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_XDISTANCE, Display_X);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
   ObjectSetString(0, "MTP_Commission", OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_COLOR, Color_Commission);
   ObjectSetInteger(0, "MTP_Commission", OBJPROP_SELECTABLE, false);
}

void UpdateDisplay()
{
   if(g_trailingEnabled)
   {
      ObjectSetString(0, "MTP_Trailing", OBJPROP_TEXT,
                      "Trailing: ON (" + DoubleToString(Trailing_Percent, 2) + "%)");
      ObjectSetInteger(0, "MTP_Trailing", OBJPROP_COLOR, Color_TrailON);
   }
   else
   {
      ObjectSetString(0, "MTP_Trailing", OBJPROP_TEXT, "Trailing: OFF");
      ObjectSetInteger(0, "MTP_Trailing", OBJPROP_COLOR, Color_TrailOFF);
   }

   if(g_autoBEEnabled)
   {
      ObjectSetString(0, "MTP_AutoBE", OBJPROP_TEXT,
                      "Auto BE: ON (" + DoubleToString(AutoBE_Trigger_Percent, 2) + "%)");
      ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_COLOR, Color_TrailON);
   }
   else
   {
      ObjectSetString(0, "MTP_AutoBE", OBJPROP_TEXT, "Auto BE: OFF");
      ObjectSetInteger(0, "MTP_AutoBE", OBJPROP_COLOR, Color_TrailOFF);
   }

   ChartRedraw(0);
}

void UpdateSpreadCommission()
{
   long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   ObjectSetString(0, "MTP_Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pts");

   if(g_commissionCalculated && g_commissionPerLot > 0)
   {
      double totalComm = g_commissionPerLot * g_currentLotSize * 2;
      ObjectSetString(0, "MTP_Commission", OBJPROP_TEXT,
                      "Est. Comm: $" + DoubleToString(totalComm, 2) + " (RT)");
      ObjectSetInteger(0, "MTP_Commission", OBJPROP_COLOR, Color_Commission);
   }
   else
   {
      ObjectSetString(0, "MTP_Commission", OBJPROP_TEXT, "Commission: N/A");
      ObjectSetInteger(0, "MTP_Commission", OBJPROP_COLOR, clrGray);
   }

   ChartRedraw(0);
}

void CalculateCommission()
{
   g_commissionPerLot = 0;
   g_commissionCalculated = false;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_position.Symbol() == _Symbol)
         {
            if(HistorySelectByPosition(g_position.Identifier()))
            {
               for(int j = HistoryDealsTotal() - 1; j >= 0; j--)
               {
                  ulong dealTicket = HistoryDealGetTicket(j);
                  if(dealTicket > 0)
                  {
                     double comm = HistoryDealGetDouble(dealTicket, DEAL_COMMISSION);
                     double lots = HistoryDealGetDouble(dealTicket, DEAL_VOLUME);
                     if(lots > 0 && comm != 0)
                     {
                        g_commissionPerLot = MathAbs(comm / lots);
                        g_commissionCalculated = true;
                        return;
                     }
                  }
               }
            }
         }
      }
   }

   if(!HistorySelect(TimeCurrent() - 86400 * 30, TimeCurrent())) return;

   for(int i = HistoryDealsTotal() - 1; i >= 0 && i >= HistoryDealsTotal() - 100; i--)
   {
      ulong ticket = HistoryDealGetTicket(i);
      if(ticket == 0) continue;
      if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue;

      double comm = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
      double lots = HistoryDealGetDouble(ticket, DEAL_VOLUME);

      if(lots > 0 && comm != 0)
      {
         g_commissionPerLot = MathAbs(comm / lots);
         g_commissionCalculated = true;
         return;
      }
   }
}

double CalculateSL(ENUM_ORDER_TYPE orderType, double openPrice)
{
   if(SL_Percent <= 0) return 0;

   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP)
      return NormalizeDouble(openPrice * (1.0 - SL_Percent / 100.0), _Digits);

   return NormalizeDouble(openPrice * (1.0 + SL_Percent / 100.0), _Digits);
}

double CalculateTP(ENUM_ORDER_TYPE orderType, double openPrice)
{
   if(TP_Percent <= 0) return 0;

   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP)
      return NormalizeDouble(openPrice * (1.0 + TP_Percent / 100.0), _Digits);

   return NormalizeDouble(openPrice * (1.0 - TP_Percent / 100.0), _Digits);
}

// Break-Even: SL-t pontosan az open price-ra teszi
void ApplyBreakEven()
{
   int modified = 0;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_position.Symbol() != _Symbol) continue;

         double openPrice = g_position.PriceOpen();
         double currentSL = g_position.StopLoss();
         double newSL     = NormalizeDouble(openPrice, _Digits);

         if(g_position.PositionType() == POSITION_TYPE_BUY)
         {
            // Csak ha a pozíció profitban van és az SL még nem BE-n van
            if(g_lastBid > openPrice && currentSL < newSL)
            {
               if(g_trade.PositionModify(g_position.Ticket(), newSL, g_position.TakeProfit()))
                  modified++;
               else
                  Print("BE Modify failed for #", g_position.Ticket(), " Error: ", GetLastError());
            }
         }
         else if(g_position.PositionType() == POSITION_TYPE_SELL)
         {
            // Csak ha a pozíció profitban van és az SL még nem BE-n van
            if(g_lastAsk < openPrice && (currentSL > newSL || currentSL == 0))
            {
               if(g_trade.PositionModify(g_position.Ticket(), newSL, g_position.TakeProfit()))
                  modified++;
               else
                  Print("BE Modify failed for #", g_position.Ticket(), " Error: ", GetLastError());
            }
         }
      }
   }

   if(modified > 0)
      Print("Break-Even applied to ", modified, " positions");
}

// Auto BE: SL-t pontosan az open price-ra teszi, ha a trigger teljesül
void ApplyAutoBreakEven()
{
   if(AutoBE_Trigger_Percent <= 0) return;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_position.Symbol() != _Symbol) continue;

         double openPrice   = g_position.PriceOpen();
         double currentSL   = g_position.StopLoss();
         double triggerDist = openPrice * AutoBE_Trigger_Percent / 100.0;
         double newSL       = NormalizeDouble(openPrice, _Digits);

         if(g_position.PositionType() == POSITION_TYPE_BUY && g_lastBid >= openPrice + triggerDist)
         {
            if(currentSL < newSL)
            {
               if(!g_trade.PositionModify(g_position.Ticket(), newSL, g_position.TakeProfit()))
                  Print("Auto BE failed for #", g_position.Ticket(), " Error: ", GetLastError());
            }
         }
         else if(g_position.PositionType() == POSITION_TYPE_SELL && g_lastAsk <= openPrice - triggerDist)
         {
            if(currentSL > newSL || currentSL == 0)
            {
               if(!g_trade.PositionModify(g_position.Ticket(), newSL, g_position.TakeProfit()))
                  Print("Auto BE failed for #", g_position.Ticket(), " Error: ", GetLastError());
            }
         }
      }
   }
}

void ManageTrailingStop()
{
   if(Trailing_Percent <= 0) return;

   CleanupHiddenSLs();
   long   stopLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double minDist         = stopLevelPoints * SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_position.Symbol() != _Symbol) continue;

         ulong  ticket    = g_position.Ticket();
         double openPrice = g_position.PriceOpen();
         double currentSL = g_position.StopLoss();
         double currentTP = g_position.TakeProfit();

         if(g_position.PositionType() == POSITION_TYPE_BUY)
         {
            double trailDist = g_lastBid * Trailing_Percent / 100.0;
            double stepDist  = g_lastBid * Trailing_Step_Percent / 100.0;
            double newSL     = NormalizeDouble(g_lastBid - trailDist, _Digits);

            if(g_lastBid > openPrice && newSL > openPrice)
            {
               if(UseHiddenTrailing)
               {
                  double hiddenSL = GetHiddenSL(ticket);
                  if(hiddenSL == 0) SetHiddenSL(ticket, newSL);
                  else if(newSL > hiddenSL + stepDist) SetHiddenSL(ticket, newSL);

                  hiddenSL = GetHiddenSL(ticket);
                  if(hiddenSL > 0 && g_lastBid <= hiddenSL)
                  {
                     if(!g_trade.PositionClose(ticket))
                        Print("Hidden Trail Close failed for #", ticket, " Error: ", GetLastError());
                     else
                        RemoveHiddenSL(ticket);
                  }
               }
               else if(currentSL == 0 || newSL > currentSL + stepDist)
               {
                  if(g_lastBid - newSL >= minDist)
                  {
                     if(!g_trade.PositionModify(ticket, newSL, currentTP))
                        Print("Trailing Modify failed for #", ticket, " Error: ", GetLastError());
                  }
               }
            }
         }
         else if(g_position.PositionType() == POSITION_TYPE_SELL)
         {
            double trailDist = g_lastAsk * Trailing_Percent / 100.0;
            double stepDist  = g_lastAsk * Trailing_Step_Percent / 100.0;
            double newSL     = NormalizeDouble(g_lastAsk + trailDist, _Digits);

            if(g_lastAsk < openPrice && newSL < openPrice)
            {
               if(UseHiddenTrailing)
               {
                  double hiddenSL = GetHiddenSL(ticket);
                  if(hiddenSL == 0) SetHiddenSL(ticket, newSL);
                  else if(newSL < hiddenSL - stepDist) SetHiddenSL(ticket, newSL);

                  hiddenSL = GetHiddenSL(ticket);
                  if(hiddenSL > 0 && g_lastAsk >= hiddenSL)
                  {
                     if(!g_trade.PositionClose(ticket))
                        Print("Hidden Trail Close failed for #", ticket, " Error: ", GetLastError());
                     else
                        RemoveHiddenSL(ticket);
                  }
               }
               else if(currentSL == 0 || newSL < currentSL - stepDist)
               {
                  if(newSL - g_lastAsk >= minDist)
                  {
                     if(!g_trade.PositionModify(ticket, newSL, currentTP))
                        Print("Trailing Modify failed for #", ticket, " Error: ", GetLastError());
                  }
               }
            }
         }
      }
   }
}

double GetHiddenSL(ulong ticket)
{
   for(int i = 0; i < g_hiddenCount; i++)
      if(g_hiddenTickets[i] == ticket) return g_hiddenSL[i];
   return 0;
}

void SetHiddenSL(ulong ticket, double sl)
{
   for(int i = 0; i < g_hiddenCount; i++)
   {
      if(g_hiddenTickets[i] == ticket) { g_hiddenSL[i] = sl; return; }
   }
   if(g_hiddenCount < MAX_HIDDEN_POSITIONS)
   {
      g_hiddenTickets[g_hiddenCount] = ticket;
      g_hiddenSL[g_hiddenCount]      = sl;
      g_hiddenCount++;
   }
}

void RemoveHiddenSL(ulong ticket)
{
   for(int i = 0; i < g_hiddenCount; i++)
   {
      if(g_hiddenTickets[i] == ticket)
      {
         g_hiddenTickets[i] = g_hiddenTickets[g_hiddenCount - 1];
         g_hiddenSL[i]      = g_hiddenSL[g_hiddenCount - 1];
         g_hiddenTickets[g_hiddenCount - 1] = 0;
         g_hiddenSL[g_hiddenCount - 1]      = 0;
         g_hiddenCount--;
         return;
      }
   }
}

void CleanupHiddenSLs()
{
   for(int i = g_hiddenCount - 1; i >= 0; i--)
   {
      bool found = false;
      for(int j = PositionsTotal() - 1; j >= 0; j--)
      {
         if(g_position.SelectByIndex(j))
            if(g_position.Ticket() == g_hiddenTickets[i]) { found = true; break; }
      }
      if(!found) RemoveHiddenSL(g_hiddenTickets[i]);
   }
}

void OpenBuy()
{
   double sl = CalculateSL(ORDER_TYPE_BUY, g_lastAsk);
   double tp = CalculateTP(ORDER_TYPE_BUY, g_lastAsk);

   if(!g_trade.Buy(g_currentLotSize, _Symbol, 0, sl, tp, "MTP Buy"))
      Print("Buy failed. Error: ", GetLastError());
   else
      Print("BUY opened: Lots: ", g_currentLotSize, " @ ", g_lastAsk, " SL: ", sl, " TP: ", tp);
}

void OpenSell()
{
   double sl = CalculateSL(ORDER_TYPE_SELL, g_lastBid);
   double tp = CalculateTP(ORDER_TYPE_SELL, g_lastBid);

   if(!g_trade.Sell(g_currentLotSize, _Symbol, 0, sl, tp, "MTP Sell"))
      Print("Sell failed. Error: ", GetLastError());
   else
      Print("SELL opened: Lots: ", g_currentLotSize, " @ ", g_lastBid, " SL: ", sl, " TP: ", tp);
}

void InstantReverse()
{
   ulong  buyTickets[], sellTickets[];
   double totalBuyLots = 0, totalSellLots = 0;
   int    buyCount = 0, sellCount = 0;

   ArrayResize(buyTickets, 0);
   ArrayResize(sellTickets, 0);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_position.Symbol() != _Symbol) continue;

         if(g_position.PositionType() == POSITION_TYPE_BUY)
         {
            ArrayResize(buyTickets, buyCount + 1);
            buyTickets[buyCount] = g_position.Ticket();
            totalBuyLots += g_position.Volume();
            buyCount++;
         }
         else if(g_position.PositionType() == POSITION_TYPE_SELL)
         {
            ArrayResize(sellTickets, sellCount + 1);
            sellTickets[sellCount] = g_position.Ticket();
            totalSellLots += g_position.Volume();
            sellCount++;
         }
      }
   }

   if(buyCount == 0 && sellCount == 0) { Print("No positions to reverse"); return; }

   if(buyCount > 0)
   {
      for(int i = 0; i < buyCount; i++)
         if(!g_trade.PositionClose(buyTickets[i]))
            Print("PositionClose failed for #", buyTickets[i], " Error: ", GetLastError());

      double sl = CalculateSL(ORDER_TYPE_SELL, g_lastBid);
      double tp = CalculateTP(ORDER_TYPE_SELL, g_lastBid);
      if(!g_trade.Sell(totalBuyLots, _Symbol, 0, sl, tp, "Reverse"))
         Print("Reverse SELL failed. Error: ", GetLastError());
      else
         Print("Reversed ", buyCount, " BUY to SELL. Lots: ", totalBuyLots);
   }

   if(sellCount > 0)
   {
      for(int i = 0; i < sellCount; i++)
         if(!g_trade.PositionClose(sellTickets[i]))
            Print("PositionClose failed for #", sellTickets[i], " Error: ", GetLastError());

      double sl = CalculateSL(ORDER_TYPE_BUY, g_lastAsk);
      double tp = CalculateTP(ORDER_TYPE_BUY, g_lastAsk);
      if(!g_trade.Buy(totalSellLots, _Symbol, 0, sl, tp, "Reverse"))
         Print("Reverse BUY failed. Error: ", GetLastError());
      else
         Print("Reversed ", sellCount, " SELL to BUY. Lots: ", totalSellLots);
   }
}

void CloseAllOnChart()
{
   int closed = 0, deleted = 0;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
         if(g_position.Symbol() == _Symbol)
         {
            if(g_trade.PositionClose(g_position.Ticket())) closed++;
            else Print("PositionClose failed for #", g_position.Ticket(), " Error: ", GetLastError());
         }
   }

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(g_order.SelectByIndex(i))
         if(g_order.Symbol() == _Symbol)
         {
            if(g_trade.OrderDelete(g_order.Ticket())) deleted++;
            else Print("OrderDelete failed for #", g_order.Ticket(), " Error: ", GetLastError());
         }
   }

   Print("Closed ", closed, " positions, deleted ", deleted, " pending orders on ", _Symbol);
}

void CloseAllPositions()
{
   int closed = 0, deleted = 0;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(g_position.SelectByIndex(i))
      {
         if(g_trade.PositionClose(g_position.Ticket())) closed++;
         else Print("PositionClose failed for #", g_position.Ticket(), " Error: ", GetLastError());
      }
   }

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(g_order.SelectByIndex(i))
      {
         if(g_trade.OrderDelete(g_order.Ticket())) deleted++;
         else Print("OrderDelete failed for #", g_order.Ticket(), " Error: ", GetLastError());
      }
   }

   Print("Closed ", closed, " positions, deleted ", deleted, " pending orders (ALL SYMBOLS)");
}
