//+------------------------------------------------------------------+
//|                                             ScoringTrade.mq5       | 
//|                        Generated by ChatGPT                    | 
//|                                                                  | 
//+------------------------------------------------------------------+ 
#property strict 
#include <Trade\Trade.mqh> 

// Input parameters
input int MagicNumber = 42;              // Unique identifier for this EA's trades
input int scoreThreshold = 15000;        // Minimum score required to enter a trade (increased for BTC's larger price movements)
input int slopeThreshold = 3500;         // Minimum EMA slope value to consider trend significant (increased for BTC's steeper trends)
input double maxScore = 25000;           // Maximum allowed score before clamping (prevents excessive trade signals)
input int cooldownMinutes = 18;          // Minimum time between crossover signals (prevents over-trading)
input int tradeCooldownMinutes = 44;     // Minimum time between consecutive trades (trade debounce period)
input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1;  // Timeframe for EMA calculation (1-hour candles)
input double delayClampAbsolute = 5000;  // Score threshold for applying decay (prevents score from growing too large)
input int emaPeriod = 139;                // Number of periods for EMA calculation (longer period for smoother trend)
input double crossOverStep = 2500;       // Score adjustment when price crosses EMA (increased for BTC's larger moves)
input double slopeThresholdStep = 2000;  // Score adjustment for significant slope changes (increased for BTC)
input double emaDistanceStep = 500;      // Score adjustment for price distance from EMA (increased for BTC)
input double emaDecayStep = 0;           // Score decay rate when no significant signals (0 means no decay)
input double decayMultiplier = 0.08;     // Multiplier applied to score when above delayClampAbsolute
input double distanceThreshold = 7100;     // Minimum price-EMA distance to trigger score adjustment (increased for BTC)
input double atrMultiplier = 3.9;        // Multiplier for dynamic stop loss calculation based on ATR
input double TrailingStop = 10;           // Distance in points for trailing stop loss
input bool ApplyTrailingStop = true;     // Enable/disable trailing stop functionality
input int maxCrossoverTrades = 14;        // Maximum number of trades allowed per crossover signal
input double max_drawdown = 0.1;         // Maximum allowed drawdown as percentage of account balance
input bool resetCrossoverTradeOnDistance = false;  // Reset trade count when price moves beyond distance threshold
input int resetCrossoverNumber = 0;      // Number of trades to reset to when resetCrossoverTradeOnDistance is true
input double minimumLotSize = 0.01;      // Minimum trade size allowed
input int maxTimeInPosition = 1;         // Maximum time in hours to hold a position
input int tradeLengthThreshold = 31;     // Time in minutes before considering a reverse trade
input int reverseTP = 707;              // Take profit level for reverse trades (increased for BTC)
input int reverseLotSizeMultiplier = 4; // Multiplier for lot size in reverse trades
input int secondaryPositionHoldTime = 75; // Maximum time in minutes to hold a secondary position
// Global variables
int emaHandle;                       // EMA handle
double prevScore = 0;                // Previous score 
double currentScore = 0;             // Current score 
double emaPrevValue = 0;             // Previous EMA value 
double emaCurrentValue = 0;          // Current EMA value 
double emaSlope = 0;                 // EMA slope value 
CTrade trade;                        // Trading object

datetime lastCrossoverTime = 0;      // Time of last crossover
datetime lastTradeTime = 0;          // Time of last trade
int crossoverTradeCount = 0;         // Count of trades after each crossover


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
    // Create EMA handle (e.g., 14-period EMA on the closing price)
    emaHandle = iMA(Symbol(), emaTimeFrame, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
    
    if (emaHandle == INVALID_HANDLE) {
        Print("Failed to create EMA handle");
        return INIT_FAILED;
    }

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    // Release the EMA handle
    if (emaHandle != INVALID_HANDLE) {
        ExpertRemove();
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
    // Buffer to hold the EMA values
    double emaBuffer[]; 
    
    // Get dynamic lot size based on current balance and max drawdown
    double lotSize = CalculateLotSize();
    
    if(lotSize < minimumLotSize) {
        lotSize = minimumLotSize;
    }
    
    // Get the current Ask and Bid prices
    double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

    // Copy the last 2 EMA values (current and previous)
    int copied = CopyBuffer(emaHandle, 0, 0, 2, emaBuffer);
    if (copied < 2) {
        return;
    }

    // Get the current and previous EMA values
    emaPrevValue = emaBuffer[1];  // Previous EMA value (index 1)
    emaCurrentValue = emaBuffer[0];  // Current EMA value (index 0)

    // Calculate the EMA slope (change in EMA values)
    emaSlope = - (emaCurrentValue - emaPrevValue) * 100;
    Print("EMA Slope: ", emaSlope);

    // Check for price action crossover with EMA
    double closePrev = iClose(Symbol(), Period(), 1);  // Close of previous bar
    double closeCurr = iClose(Symbol(), Period(), 0);  // Close of current bar

    // Check if enough time has passed for the cooldown (cooldownMinutes)
    if (TimeCurrent() - lastCrossoverTime >= cooldownMinutes * 60) {
        if (closePrev < emaPrevValue && closeCurr > emaCurrentValue) { // Bullish crossover
            currentScore += crossOverStep;
            crossoverTradeCount = 0;  // Reset trade count after new crossover
            lastCrossoverTime = TimeCurrent();  // Update the last crossover time
        } 
        else if (closePrev > emaPrevValue && closeCurr < emaCurrentValue) { // Bearish crossover
            currentScore -= crossOverStep;
            crossoverTradeCount = 0;  // Reset trade count after new crossover
            lastCrossoverTime = TimeCurrent();  // Update the last crossover time
        }
    }

    // Check EMA slope
    if (emaSlope > slopeThreshold) { // Positive slope
        currentScore += slopeThresholdStep;
    } 
    else if (emaSlope < -slopeThreshold) { // Negative slope
        currentScore -= slopeThresholdStep;
    } 
    else {
        if (MathAbs(currentScore) > delayClampAbsolute) {
            currentScore *= decayMultiplier;
        }
    }

    if(ApplyTrailingStop) {
      ApplyTrailingStop();
    }

    // Calculate distance to EMA and adjust score
    double priceToEmaDistance = closeCurr - emaCurrentValue;  // Distance between the current price and the EMA
    Print("Price to EMA Distance: ", priceToEmaDistance);

    if (MathAbs(priceToEmaDistance) > distanceThreshold) {
        if (priceToEmaDistance > 0) { // Bullish (price above EMA)
            currentScore += emaDistanceStep;
        }
        else if (priceToEmaDistance < 0) { // Bearish (price below EMA)
            currentScore -= emaDistanceStep;
        }
    } 
    else {
        if (currentScore > 0) {
            currentScore -= emaDecayStep;
        }
        else {
            currentScore += emaDecayStep;
        }
    }

    // Close all positions if score crosses zero
    if ((prevScore > 0 && currentScore <= 0) || (prevScore < 0 && currentScore >= 0)) {
        Close_Position_MN(MagicNumber);
    }

    // Update the previous score
    prevScore = currentScore;

    if (crossoverTradeCount > maxCrossoverTrades) {
        return;
    }

    // Debounce check: Ensure enough time has passed since the last trade
    if (TimeCurrent() - lastTradeTime >= tradeCooldownMinutes * 60) {
        // Calculate ATR (Average True Range) for stop loss calculation
        double atrArray[];
        int atrPeriod = 14;  // ATR period (can be adjusted)
        int copied = CopyBuffer(iATR(Symbol(), Period(), atrPeriod), 0, 0, 1, atrArray);
        if (copied < 1) {
            return;
        }

        // Get the current price (using Bid price)
        double currentPrice = Bid;
        // Get ATR value
        double atrValue = atrArray[0];  // Latest ATR value

        // Get the minimum stop level and freeze level for the symbol
        int stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
        int freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);

        // Calculate the minimum stop loss in price units (converted from pips)
        double minStopLoss = stopLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
        double minFreezeLevel = freezeLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);

        // Dynamic Stop Loss and Take Profit calculation based on ATR
        double dynamicSL = atrValue * atrMultiplier;
        double dynamicTP = atrValue * atrMultiplier;

        // Adjust SL and TP if they are smaller than the minimum stop level
        dynamicSL = MathMax(dynamicSL, minStopLoss);
        dynamicTP = MathMax(dynamicTP, dynamicSL);  // Ensure TP is at least the same as SL

        // Trade logic based on the score
        if (currentScore > scoreThreshold) { // Buy signal
            if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) {
                // Open buy position with dynamic SL and TP
                trade.SetExpertMagicNumber(MagicNumber);
                if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) { 
                    crossoverTradeCount++;  // Increment trade count
                    lastTradeTime = TimeCurrent();  // Update the last trade time
                }
            }
        }
        else if (currentScore < -scoreThreshold) { // Sell signal
            if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) {
                // Open sell position with dynamic SL and TP
                trade.SetExpertMagicNumber(MagicNumber);
                if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) { 
                    crossoverTradeCount++;  // Increment trade count
                    lastTradeTime = TimeCurrent();  // Update the last trade time
                }
            }
        }
    }

    // Check existing positions for profit and place reverse trade if needed
    CheckPositions();
}

//+------------------------------------------------------------------+
//| Check existing positions for profit and place reverse trade if needed |
//+------------------------------------------------------------------+
void CheckPositions() {
    // Check if there are any open positions
    if (PositionsTotal() > 0) {
        // Check if there are exactly 2 open positions
        if (PositionsTotal() == 2) {
            for (int i = 0; i < PositionsTotal(); i++) {
                ulong ticket = PositionGetTicket(i);
                if (PositionSelectByTicket(ticket)) {
                    datetime openTime = PositionGetInteger(POSITION_TIME);
                    int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds

                    // Check if the trade has been open for more than the secondaryPositionHoldTime
                    if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds
                        // Close all positions
                        CloseAllPositions();
                        return; // Exit the function after closing all positions
                    }
                }
            }
        } else if (PositionsTotal() < 2) {
            for (int i = 0; i < PositionsTotal(); i++) {
                ulong ticket = PositionGetTicket(i);
                if (PositionSelectByTicket(ticket)) {
                    double profit = PositionGetDouble(POSITION_PROFIT);
                    datetime openTime = PositionGetInteger(POSITION_TIME);
                    int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds

                    // Check if the trade has been open for more than the tradeLengthThreshold
                    if (tradeLength > tradeLengthThreshold * 60) { // Convert threshold to seconds
                        double lotSize = PositionGetDouble(POSITION_VOLUME);
                        double newLotSize = lotSize * reverseLotSizeMultiplier; // 10 times the original lot size

                        crossoverTradeCount = maxCrossoverTrades + 1;

                        // Place a reverse trade
                        if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
                            trade.SetExpertMagicNumber(MagicNumber);
                            if (trade.Sell(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_BID))) {
                            } else {
                                Print("Failed to execute reversal sell order");
                            }
                        } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
                            trade.SetExpertMagicNumber(MagicNumber);
                            if (trade.Buy(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_ASK))) {
                            } else {
                                Print("Failed to execute reversal buy order");
                            }
                        }
                    }

                    // Close the trade if profit meets the take profit level
                    if (profit >= reverseTP) {
                        Close_Position_MN(MagicNumber);
                        CloseAllPositions();
                    }

                    // Check if there is only one position and its volume is lotSize * reverseLotSizeMultiplier
                    if (PositionsTotal() == 1 && PositionGetDouble(POSITION_VOLUME) == minimumLotSize * reverseLotSizeMultiplier) {
                        trade.PositionClose(ticket);
                    }

                   // Get the current Ask and Bid prices
                   double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
                   double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

                    // Check if the double down trade is exited by stop loss
                    if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetDouble(POSITION_SL) > 0 && Bid <= PositionGetDouble(POSITION_SL)) {
                        // Close the original trade
                        CloseOriginalTrade();
                    } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetDouble(POSITION_SL) > 0 && Ask >= PositionGetDouble(POSITION_SL)) {
                        // Close the original trade
                        CloseOriginalTrade();
                    }
                }
            }
        }
    }
}

// Function to close the original trade
void CloseOriginalTrade() {
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
                trade.PositionClose(ticket);
            } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
                trade.PositionClose(ticket);
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Function to close all positions                                   |
//+------------------------------------------------------------------+
void CloseAllPositions() {
    // Loop through all positions and close them
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
                trade.PositionClose(ticket);
            } 
            else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
                trade.PositionClose(ticket);
            }
        }
    }
}

void ApplyTrailingStop()
{
    for(int i=PositionsTotal()-1; i>=0; i--)
    {
        string symbol = PositionGetSymbol(i);
        ulong PositionTicket = PositionGetTicket(i);   
        long trade_type = PositionGetInteger(POSITION_TYPE);
        
        if(!PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
            return;
        }
        
        double POINT  =       SymbolInfoDouble(  symbol, SYMBOL_POINT  );
        int    DIGIT  =       (int) SymbolInfoInteger( symbol, SYMBOL_DIGITS );
   
        if(trade_type == 0)
        {    
            double Bid = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_BID),DIGIT);
                                  
            if(Bid-PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * TrailingStop,DIGIT))
            {
                if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop,DIGIT))
                {
                    trade.PositionModify(PositionTicket,NormalizeDouble(Bid - POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
                }
            }
        }
        
        if(trade_type == 1)
        {  
            double Ask = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_ASK),DIGIT);
               
            if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble( POINT * TrailingStop,DIGIT))
            {
                if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop,DIGIT)) || (PositionGetDouble(POSITION_SL)==0))
                {
                    trade.PositionModify(PositionTicket,NormalizeDouble(Ask + POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
                }
            }
        }
    }  
}

void Close_Position_MN(ulong magicNumber)
{  
    int total = PositionsTotal();
    for(int i = total - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);

        // Use PositionSelect by symbol instead of ticket
        string symbol = PositionGetSymbol(i);
        if(PositionSelect(symbol))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetInteger(POSITION_TICKET) == ticket)
            {
                if(symbol == _Symbol) // Verify the symbol
                {
                    trade.PositionClose(ticket);
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Calculate the dynamic lot size based on max drawdown             |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
    double balance = AccountInfoDouble(ACCOUNT_BALANCE);   // Get account balance
    double allowedDrawdown = balance * max_drawdown;       // Calculate allowed drawdown in account currency
    double baseDrawdownPerLot = 150;                       // Assumed drawdown per 0.01 lots as per backtest
    
    // Calculate lot size based on maximum drawdown
    double lotSize = (allowedDrawdown / baseDrawdownPerLot) * 0.01;
    return NormalizeDouble(lotSize, 2);                   // Normalize lot size to 2 decimal places
}
