//+------------------------------------------------------------------+ //| ScoringTrade.mq5 | //| Generated by ChatGPT | //| | //+------------------------------------------------------------------+ #property strict #include // Input parameters input int MagicNumber = 42; input int scoreThreshold = 5200; // Score threshold for trade entry input int slopeThreshold = 93; // EMA slope threshold input double maxScore = 7900; // Max score value for clamping input int cooldownMinutes = 18; // Cooldown period in minutes (37 minutes) input int tradeCooldownMinutes = 24; // Trade debounce cooldown period (5 minutes) input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1; // EMA Timeframe input double delayClampAbsolute = 1690; input int emaPeriod = 64; // EMA period input double crossOverStep = 950; input double slopeThresholdStep = 635; input double emaDistanceStep = 150; input double emaDecayStep = 0; input double decayMultiplier = 0.08; // Decay multiplier input double distanceThreshold = 28.5; // Set your distance threshold (adjust as necessary) input double atrMultiplier = 7.6; // Multiplier for dynamic SL and TP calculation input double TrailingStop = 5; input bool UseTrailingStop = true; input int maxCrossoverTrades = 4; // Maximum number of trades per crossover input double max_drawdown = 0.1; // Maximum drawdown percentage input bool resetCrossoverTradeOnDistance = false; input int resetCrossoverNumber = 0; input double minimumLotSize = 0.01; input int maxTimeInPosition = 9; input int tradeLengthThreshold = 98; input int reverseTP = 32; input int reverseLotSizeMultiplier = 15; input int secondaryPositionHoldTime = 32; // 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) { if (emaHandle != INVALID_HANDLE) { IndicatorRelease(emaHandle); emaHandle = INVALID_HANDLE; } } //+------------------------------------------------------------------+ //| 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) { Print("Failed to copy EMA values. Error code: ", GetLastError()); 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; // 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 Print("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 Print("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(UseTrailingStop) { ApplyTrailingStop(); } // Calculate distance to EMA and adjust score double priceToEmaDistance = closeCurr - emaCurrentValue; // Distance between the current price and the EMA if (MathAbs(priceToEmaDistance) > distanceThreshold) { if (priceToEmaDistance > 0) { // Bullish (price above EMA) currentScore += emaDistanceStep; Print("Bullish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue); } else if (priceToEmaDistance < 0) { // Bearish (price below EMA) currentScore -= emaDistanceStep; Print("Bearish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue); } } 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) { Print("Failed to get ATR values. Error code: ", GetLastError()); 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 long stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL); long 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()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber) && crossoverTradeCount < maxCrossoverTrades) { Print("maxCrossover"); Print(crossoverTradeCount); // Open buy position with dynamic SL and TP trade.SetExpertMagicNumber(MagicNumber); if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) { Print("Buy order executed with score: ", currentScore); crossoverTradeCount++; // Increment trade count lastTradeTime = TimeCurrent(); // Update the last trade time } } } else if (currentScore < -scoreThreshold) { // Sell signal if ((!PositionSelect(Symbol()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber) && crossoverTradeCount < maxCrossoverTrades) { Print("maxCrossover"); Print(crossoverTradeCount); // Open sell position with dynamic SL and TP trade.SetExpertMagicNumber(MagicNumber); if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) { Print("Sell order executed with score: ", currentScore); crossoverTradeCount++; // Increment trade count lastTradeTime = TimeCurrent(); // Update the last trade time } } } } else { Print("Trade skipped due to debounce: ", currentScore); } // Check existing positions for profit and place reverse trade if needed CheckPositions(); } //+------------------------------------------------------------------+ //| Check existing positions for profit and place reverse trade if needed | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Check existing positions for duration 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 = (datetime)PositionGetInteger(POSITION_TIME); long tradeLength = (long)(TimeCurrent() - openTime); // Check if the trade has been open for more than the secondaryPositionHoldTime if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds // Close all positions CloseAllPositions(); Print("All positions closed due to exceeding secondaryPositionHoldTime"); 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 = (datetime)PositionGetInteger(POSITION_TIME); long tradeLength = (long)(TimeCurrent() - openTime); // 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))) { Print("Reversal sell order executed with increased lot size"); } 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))) { Print("Reversal buy order executed with increased lot size"); } 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); Print("Single position with volume equal to lotSize * reverseLotSizeMultiplier closed"); } // 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); Print("Original buy position closed due to double down stop loss."); } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { trade.PositionClose(ticket); Print("Original sell position closed due to double down stop loss."); } } } } //+------------------------------------------------------------------+ //| 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); Print("Buy position closed at score crossover."); } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { trade.PositionClose(ticket); Print("Sell position closed at score crossover."); } } } } 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) { continue; } 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 { Print("MN ", magicNumber); trade.PositionClose(ticket); } } } else { int errorCode = GetLastError(); Print("aaaa PositionSelect failed with error code: ", errorCode); } } } //+------------------------------------------------------------------+ //| 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 }