#include <Trade\Trade.mqh>

//--- input parameters
input int SMAPeriod = 200;                // Period for SMA
input int WMAPeriod = 100;                // Period for WMA
input int HMAPeriod = 100;                // Period for HMA
input int ROCPeriod = 40;                 // Period for ROC
input int BBPeriod = 118;                 // Period for Bollinger Bands
input double BBDeviation = 1.6;           // Deviation for Bollinger Bands
input int RSIPeriod = 22;                 // Period for RSI
input int MACDFastPeriod = 8;             // Fast period for MACD
input int MACDSlowPeriod = 26;            // Slow period for MACD
input int MACDSignalPeriod = 6;           // Signal period for MACD
input double LotSize = 0.02;              // Lot size for trades
input double ATRMultiplier = 2.0;         // Multiplier for ATR-based stop loss
input int ATRPeriod = 124;                // Period for ATR
input double RiskToRewardRatio = 2.6;     // Risk to Reward ratio
input double TrailingStopPips = 20;       // Trailing stop loss in pips
input double EarlyProfitPips = 30;        // Early profit taking in pips
input int MagicNumber = 124356;           // Magic number for orders

// Migliorie aggiunte
input int StartHour = 8;                   // Ora di inizio trading (UTC)
input int EndHour = 17;                    // Ora di fine trading (UTC)
input int ADXThreshold = 20;               // Soglia ADX
input double MaxDrawdown = 5.0;            // Limite di drawdown (%) del capitale

// Variabili globali
double initialBalance;
CTrade trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    initialBalance = AccountInfoDouble(ACCOUNT_BALANCE);
    Print("EA initialized. Initial Balance: ", initialBalance);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    Print("EA deinitialized. Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Controllo orario di trading
    if (!IsWithinTradingHours())
    {
        return;
    }

    // Controllo eventi economici imminenti (Placeholder)
    if (IsEconomicEventUpcoming())
    {
        return;
    }

    // Controllo drawdown massimo
    if (IsDrawdownExceeded())
    {
        Print("Drawdown massimo raggiunto. Pausa trading.");
        return;
    }

    // Esegui la strategia principale su un simbolo specifico
    StrategyOne("XAUUSD", PERIOD_H1);

    // Gestione delle operazioni esistenti
    ManageTrades();
}

//+------------------------------------------------------------------+
//| Strategy One: Trend Following                                    |
//+------------------------------------------------------------------+
void StrategyOne(string symbol, ENUM_TIMEFRAMES timeframe)
{
    // Calcolo ADX
    double adx = GetADX(symbol, timeframe, 14);
    if (adx < ADXThreshold)
    {
        Print(symbol, ": Trend debole (ADX = ", adx, "). Nessuna operazione.");
        return;
    }

    // Calcolo SMA per conferma trend
    double sma = GetMAValue(symbol, timeframe, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
    double price = iClose(symbol, timeframe, 0);

    // Definizione del trend
    bool isUptrend = (price > sma);

    // Calcolo degli altri indicatori
    double wma = CalculateWMA(symbol, timeframe, WMAPeriod, 0);
    double hma = CalculateHMA(symbol, timeframe, HMAPeriod, 0);
    double roc = CalculateROC(symbol, timeframe, ROCPeriod, 0);
    double vwap = CalculateVWAP(symbol);
    double atr = CalculateATR(symbol, PERIOD_H4, ATRPeriod, 0);
    double bbUpper, bbLower, bbMiddle;
    CalculateBollingerBands(symbol, timeframe, BBPeriod, BBDeviation, 0, bbUpper, bbLower, bbMiddle);
    double rsi = CalculateRSI(symbol, timeframe, RSIPeriod, 0);
    double macdMain, macdSignal;
    CalculateMACD(symbol, timeframe, MACDFastPeriod, MACDSlowPeriod, MACDSignalPeriod, 0, macdMain, macdSignal);

    Print(symbol, " - Indicators: SMA=", sma, ", WMA=", wma, ", HMA=", hma, ", ROC=", roc, ", VWAP=", vwap, ", ATR=", atr, ", BBUpper=", bbUpper, ", BBLower=", bbLower, ", RSI=", rsi, ", MACDMain=", macdMain, ", MACDSignal=", macdSignal);

    // Definisci le condizioni di acquisto e vendita
    bool buyConditionsMet = isUptrend &&
                            (price > wma) &&
                            (roc > 0) &&
                            (rsi < 70) &&
                            (macdMain > macdSignal) &&
                            (price > bbLower);

    bool sellConditionsMet = !isUptrend &&
                             (price < wma) &&
                             (roc < 0) &&
                             (rsi > 30) &&
                             (macdMain < macdSignal) &&
                             (price < bbUpper);

    if (isUptrend)
    {
        Print(symbol, ": Uptrend detected.");
        if (buyConditionsMet)
        {
            Print(symbol, ": Buy conditions met.");
            PlaceOrder(symbol, true);
        }
        else
        {
            Print(symbol, ": Buy conditions not met.");
        }
    }
    else
    {
        Print(symbol, ": Downtrend detected.");
        if (sellConditionsMet)
        {
            Print(symbol, ": Sell conditions met.");
            PlaceOrder(symbol, false);
        }
        else
        {
            Print(symbol, ": Sell conditions not met.");
        }
    }
}

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+
double GetMAValue(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price)
{
    int handle = iMA(symbol, timeframe, period, 0, method, price);
    double value[];
    if (CopyBuffer(handle, 0, shift, 1, value) > 0)
    {
        return value[0];
    }
    else
    {
        Print("Failed to copy MA buffer for ", symbol);
        return 0;
    }
}

double CalculateWMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
    double sumWeights = 0;
    double sumPriceWeight = 0;
    for (int i = 0; i < period; i++)
    {
        double price = iClose(symbol, timeframe, i + shift);
        double weight = period - i;
        sumPriceWeight += price * weight;
        sumWeights += weight;
    }
    return sumPriceWeight / sumWeights;
}

double CalculateHMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
    if (period < 2)
        return 0;

    int halfPeriod = period / 2;
    int sqrtPeriod = MathSqrt(period);

    double wma1 = CalculateWMA(symbol, timeframe, halfPeriod, shift);
    double wma2 = CalculateWMA(symbol, timeframe, period, shift);
    double diff = 2 * wma1 - wma2;

    return CalculateWMA(symbol, timeframe, sqrtPeriod, shift - (int)MathFloor(sqrtPeriod / 2)) + diff;
}

double CalculateROC(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
    double currentPrice = iClose(symbol, timeframe, shift);
    double pastPrice = iClose(symbol, timeframe, shift + period);
    if (pastPrice == 0) return 0;
    return ((currentPrice - pastPrice) / pastPrice) * 100;
}

double CalculateVWAP(string symbol)
{
    double sumPriceVolume = 0;
    double sumVolume = 0;
    for (int i = 0; i < 288; i++)
    {
        double price = iClose(symbol, PERIOD_M5, i);
        double volume = iVolume(symbol, PERIOD_M5, i);
        sumPriceVolume += price * volume;
        sumVolume += volume;
    }
    if (sumVolume != 0)
        return sumPriceVolume / sumVolume;
    else
        return 0;
}

double CalculateATR(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
    int handle = iATR(symbol, timeframe, period);
    double atrValues[];
    if (CopyBuffer(handle, 0, shift, 1, atrValues) > 0)
        return atrValues[0];
    else
    {
        Print("Failed to copy ATR buffer for ", symbol);
        return 0;
    }
}

void CalculateBollingerBands(string symbol, ENUM_TIMEFRAMES timeframe, int period, double deviation, int shift, double &upperBand, double &lowerBand, double &middleBand)
{
    int handle = iBands(symbol, timeframe, period, 0, deviation, PRICE_CLOSE);
    double upper[], lower[], middle[];
    if (CopyBuffer(handle, 1, shift, 1, upper) > 0 && CopyBuffer(handle, 2, shift, 1, lower) > 0 && CopyBuffer(handle, 0, shift, 1, middle) > 0)
    {
        upperBand = upper[0];
        lowerBand = lower[0];
        middleBand = middle[0];
    }
    else
    {
        Print("Failed to copy Bollinger Bands buffer for ", symbol);
        upperBand = 0;
        lowerBand = 0;
        middleBand = 0;
    }
}

double CalculateRSI(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
    int handle = iRSI(symbol, timeframe, period, PRICE_CLOSE);
    double rsiValues[];
    if (CopyBuffer(handle, 0, shift, 1, rsiValues) > 0)
        return rsiValues[0];
    else
    {
        Print("Failed to copy RSI buffer for ", symbol);
        return 0;
    }
}

void CalculateMACD(string symbol, ENUM_TIMEFRAMES timeframe, int fastPeriod, int slowPeriod, int signalPeriod, int shift, double &mainLine, double &signalLine)
{
    int handle = iMACD(symbol, timeframe, fastPeriod, slowPeriod, signalPeriod, PRICE_CLOSE);
    double macdMain[], macdSignal[];
    if (CopyBuffer(handle, 0, shift, 1, macdMain) > 0 && CopyBuffer(handle, 1, shift, 1, macdSignal) > 0)
    {
        mainLine = macdMain[0];
        signalLine = macdSignal[0];
    }
    else
    {
        Print("Failed to copy MACD buffer for ", symbol);
        mainLine = 0;
        signalLine = 0;
    }
}

double GetADX(string symbol, ENUM_TIMEFRAMES timeframe, int period)
{
    int handle = iADX(symbol, timeframe, period);
    double adxValues[];
    if (CopyBuffer(handle, 0, 0, 1, adxValues) > 0)
        return adxValues[0];
    else
    {
        Print("Failed to copy ADX buffer for ", symbol);
        return 0;
    }
}

bool IsWithinTradingHours()
{
    datetime currentTime = TimeCurrent();
    MqlDateTime timeStruct;
    TimeToStruct(currentTime, timeStruct);
    int currentHour = timeStruct.hour;

    if (StartHour < EndHour)
    {
        if (currentHour >= StartHour && currentHour <= EndHour)
            return true;
    }
    else
    {
        if (currentHour >= StartHour || currentHour <= EndHour)
            return true;
    }
    return false;
}

bool IsEconomicEventUpcoming()
{
    return false;
}

bool IsDrawdownExceeded()
{
    double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
    double drawdown = 100.0 * (initialBalance - currentBalance) / initialBalance;
    if (drawdown >= MaxDrawdown)
        return true;
    return false;
}

void PlaceOrder(string symbol, bool buy)
{
    MqlTradeRequest request;
    MqlTradeResult result;
    ZeroMemory(request);
    ZeroMemory(result);

    double atr = CalculateATR(symbol, PERIOD_H4, ATRPeriod, 0);
    double stopLossPips = ATRMultiplier * atr / _Point;
    double takeProfitPips = stopLossPips * RiskToRewardRatio;

    request.action = TRADE_ACTION_DEAL;
    request.symbol = symbol;
    request.volume = LotSize;
    request.deviation = 5;
    request.magic = MagicNumber;

    if (buy)
    {
        request.type = ORDER_TYPE_BUY;
        request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
        request.sl = request.price - stopLossPips * _Point;
        request.tp = request.price + takeProfitPips * _Point;
        Print("Placing BUY order on ", symbol, " Price: ", request.price, ", SL: ", request.sl, ", TP: ", request.tp);
    }
    else
    {
        request.type = ORDER_TYPE_SELL;
        request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
        request.sl = request.price + stopLossPips * _Point;
        request.tp = request.price - takeProfitPips * _Point;
        Print("Placing SELL order on ", symbol, " Price: ", request.price, ", SL: ", request.sl, ", TP: ", request.tp);
    }

    if (request.sl == request.price || request.tp == request.price)
    {
        if (buy)
        {
            request.sl = request.price - stopLossPips * _Point;
            request.tp = request.price + takeProfitPips * _Point;
        }
        else
        {
            request.sl = request.price + stopLossPips * _Point;
            request.tp = request.price - takeProfitPips * _Point;
        }
    }

    ENUM_ORDER_TYPE_FILLING filling_modes[] = {ORDER_FILLING_FOK, ORDER_FILLING_IOC};
    for (int i = 0; i < ArraySize(filling_modes); i++)
    {
        request.type_filling = filling_modes[i];
        if (OrderSend(request, result))
        {
            Print("Order placed successfully on ", symbol, ". Ticket: ", result.order);
            return;
        }
        else
        {
            Print("OrderSend failed with error #", GetLastError(), " for filling mode: ", EnumToString(filling_modes[i]), " on ", symbol);
        }
    }

    Print("Failed to place order on ", symbol, " with any supported filling mode.");
}

void ManageTrades()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            string symbol = PositionGetString(POSITION_SYMBOL);
            ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            double currentPrice = (posType == POSITION_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_BID) : SymbolInfoDouble(symbol, SYMBOL_ASK);
            double sl = PositionGetDouble(POSITION_SL);
            double tp = PositionGetDouble(POSITION_TP);
            double trailStop = TrailingStopPips * _Point;

            if (currentProfit >= EarlyProfitPips * _Point)
            {
                Print("Closing position early for profit - Ticket: ", ticket, ", Profit: ", currentProfit);
                trade.PositionClose(ticket);
            }

            if (posType == POSITION_TYPE_BUY)
            {
                if ((currentPrice - openPrice) > trailStop && (currentPrice - trailStop) > sl)
                {
                    double newSL = currentPrice - trailStop;
                    if (trade.PositionModify(ticket, newSL, tp))
                        Print("Trailing stop loss adjusted for BUY - New SL: ", newSL);
                    else
                        Print("Failed to modify BUY position - Ticket: ", ticket, ", Error: ", GetLastError());
                }
            }
            else if (posType == POSITION_TYPE_SELL)
            {
                if ((openPrice - currentPrice) > trailStop && (currentPrice + trailStop) < sl)
                {
                    double newSL = currentPrice + trailStop;
                    if (trade.PositionModify(ticket, newSL, tp))
                        Print("Trailing stop loss adjusted for SELL - New SL: ", newSL);
                    else
                        Print("Failed to modify SELL position - Ticket: ", ticket, ", Error: ", GetLastError());
                }
            }
        }
    }
}