//+------------------------------------------------------------------+
//|                                   supertrend-trend-ea.mq5          |
//|  A trend-following EA: enters when the SuperTrend line flips       |
//|  sides, using the flipped line value itself as the Stop Loss (not |
//|  a separate ATR multiple -- SuperTrend already gives a             |
//|  volatility-adaptive stop by design), with risk-based position    |
//|  sizing. One position at a time.                                   |
//|  EDUCATIONAL — test on a demo account first. Past performance     |
//|  does not guarantee future results. This is not financial advice.|
//|  Source: web-forex (educational, free to use and modify)          |
//+------------------------------------------------------------------+
#property copyright "web-forex"
#include <Trade\Trade.mqh>

CTrade trade;

input string       ___Strategy___       = "--- SuperTrend Strategy ---";
input int          ATRPeriod            = 10;
input double       Multiplier           = 3.0;

input string       ___RiskManagement___ = "--- Risk Management ---";
input double       RiskRewardRatio      = 2.0;
input bool         UseFixedLot          = false;
input double       FixedLotSize         = 0.01;
input double       RiskPercent          = 1.0;

input string       ___Filters___        = "--- Filters ---";
input int          MaxSpreadPoints      = 30;

input string       ___General___        = "--- General ---";
input ulong        MagicNumber          = 20260727;
input bool         EnableTrading        = true;

#define SUPERTREND_LOOKBACK 300

int atrHandle = INVALID_HANDLE;
datetime lastBarTime = 0;

int OnInit()
{
   trade.SetExpertMagicNumber(MagicNumber);
   atrHandle = iATR(_Symbol, _Period, ATRPeriod);
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   IndicatorRelease(atrHandle);
}

void OnTick()
{
   if (!EnableTrading)
      return;

   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   if (currentBarTime == lastBarTime)
      return; // only evaluate once per new bar
   lastBarTime = currentBarTime;

   if (CountOpenPositions() > 0)
      return; // one position at a time

   long spreadPoints = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   if (spreadPoints > MaxSpreadPoints)
      return; // spread too wide right now, skip this bar

   double trendLast, trendPrev, lineLast;
   if (!CalculateSuperTrend(trendLast, trendPrev, lineLast))
      return;

   bool bullishFlip = (trendPrev == -1 && trendLast == 1);
   bool bearishFlip = (trendPrev == 1 && trendLast == -1);

   if (bullishFlip)
      OpenTrade(ORDER_TYPE_BUY, lineLast);
   else if (bearishFlip)
      OpenTrade(ORDER_TYPE_SELL, lineLast);
}

bool CalculateSuperTrend(double &trendLast, double &trendPrev, double &lineLast)
{
   int bars = MathMin(iBars(_Symbol, _Period) - 1, SUPERTREND_LOOKBACK);
   if (bars < ATRPeriod + 5)
      return(false);

   if (BarsCalculated(atrHandle) < bars + 1)
      return(false);

   double h[], l[], c[], atr[];
   ArraySetAsSeries(h, true);
   ArraySetAsSeries(l, true);
   ArraySetAsSeries(c, true);
   ArraySetAsSeries(atr, true);
   if (CopyHigh(_Symbol, _Period, 0, bars + 1, h) < bars + 1) return(false);
   if (CopyLow(_Symbol, _Period, 0, bars + 1, l) < bars + 1) return(false);
   if (CopyClose(_Symbol, _Period, 0, bars + 1, c) < bars + 1) return(false);
   if (CopyBuffer(atrHandle, 0, 0, bars + 1, atr) < bars + 1) return(false);

   double finalUpperArr[], finalLowerArr[], trendArr[], lineArr[];
   ArrayResize(finalUpperArr, bars + 1);
   ArrayResize(finalLowerArr, bars + 1);
   ArrayResize(trendArr, bars + 1);
   ArrayResize(lineArr, bars + 1);

   for (int i = bars; i >= 1; i--)
   {
      double mid = (h[i] + l[i]) / 2.0;
      double basicUpper = mid + Multiplier * atr[i];
      double basicLower = mid - Multiplier * atr[i];

      double finalUpper, finalLower;
      if (i == bars)
      {
         finalUpper = basicUpper;
         finalLower = basicLower;
      }
      else
      {
         double prevFinalUpper = finalUpperArr[i + 1];
         double prevFinalLower = finalLowerArr[i + 1];
         double prevClose      = c[i + 1];

         finalUpper = (basicUpper < prevFinalUpper || prevClose > prevFinalUpper) ? basicUpper : prevFinalUpper;
         finalLower = (basicLower > prevFinalLower || prevClose < prevFinalLower) ? basicLower : prevFinalLower;
      }

      finalUpperArr[i] = finalUpper;
      finalLowerArr[i] = finalLower;

      double prevTrend = (i == bars) ? 1 : trendArr[i + 1];
      double trend;
      if (prevTrend == 1)
         trend = (c[i] < finalLower) ? -1 : 1;
      else
         trend = (c[i] > finalUpper) ? 1 : -1;

      trendArr[i] = trend;
      lineArr[i]  = (trend == 1) ? finalLower : finalUpper;
   }

   trendLast = trendArr[1];
   trendPrev = trendArr[2];
   lineLast  = lineArr[1];
   return(true);
}

int CountOpenPositions()
{
   int count = 0;
   for (int i = 0; i < PositionsTotal(); i++)
   {
      ulong ticket = PositionGetTicket(i);
      if (ticket > 0 && PositionSelectByTicket(ticket))
      {
         if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber)
            count++;
      }
   }
   return(count);
}

void OpenTrade(ENUM_ORDER_TYPE type, double lineValue)
{
   double price, sl, tp, slDistance;

   if (type == ORDER_TYPE_BUY)
   {
      price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      sl = lineValue; // the flipped SuperTrend line is already positioned below price
      slDistance = price - sl;
      tp = price + slDistance * RiskRewardRatio;
   }
   else
   {
      price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      sl = lineValue; // the flipped SuperTrend line is already positioned above price
      slDistance = sl - price;
      tp = price - slDistance * RiskRewardRatio;
   }

   if (slDistance <= 0)
      return; // guard against a degenerate stop distance

   double lots = CalculateLotSize(slDistance);

   if (type == ORDER_TYPE_BUY)
      trade.Buy(lots, _Symbol, price, sl, tp, "SuperTrend Trend EA");
   else
      trade.Sell(lots, _Symbol, price, sl, tp, "SuperTrend Trend EA");
}

double CalculateLotSize(double slDistance)
{
   if (UseFixedLot || slDistance <= 0)
      return(NormalizeLotSize(FixedLotSize));

   double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * (RiskPercent / 100.0);
   double tickValue   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if (tickSize <= 0 || tickValue <= 0)
      return(NormalizeLotSize(FixedLotSize));

   double slTicks = slDistance / tickSize;
   double lots    = riskAmount / (slTicks * tickValue);

   return(NormalizeLotSize(lots));
}

double NormalizeLotSize(double lots)
{
   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if (lotStep <= 0)
      return(minLot);

   lots = MathFloor(lots / lotStep) * lotStep;
   if (lots < minLot)
      lots = minLot;
   if (lots > maxLot)
      lots = maxLot;
   return(lots);
}
