//+------------------------------------------------------------------+ //| PullbackTradingEA.mq5 | //| Copyright 2023, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict //--- Include necessary libraries #include //--- EA Input Parameters input bool EnableEA = true; // Enable/Disable the EA //--- Timeframe validation input bool AllowOnlyM15 = true; // Restrict to M15 timeframe only //--- Trend Indicators input int EMA50_Period = 50; // EMA50 Period input int EMA200_Period = 200; // EMA200 Period input double Min_EMA_Distance_Pct = 0.03; // Minimum EMA distance percentage (reduced for more signals) input bool Allow_Counter_Trend_With_Confirmation = false; // Allow counter-trend trades with strong confirmation input bool Allow_Trading_In_Sideways_Market = true; // Allow trading even in sideways market //--- UT Bot Parameters input int UT_ATR_Period = 14; // ATR period for UT Bot input double UT_ATR_Multiplier = 0.8; // ATR multiplier for UT Bot (reduced for more signals) input int UT_Sensitivity = 1; // UT Bot sensitivity input bool DebugMode = true; // Enable detailed logging for debugging //--- MACD Parameters input int MACD_Fast = 12; // MACD Fast period input int MACD_Slow = 26; // MACD Slow period input int MACD_Signal = 9; // MACD Signal period //--- Volume Parameters input int Volume_MA_Period = 20; // Volume SMA period input double Volume_Min_Ratio = 0.9; // Minimum volume ratio (reduced for more signals) //--- ATR Parameters input int ATR_Period = 14; // ATR period input double ATR_Min = 3.0; // Minimum ATR value (reduced for more signals) //--- News Filter Parameters input bool UseNewsFilter = true; // Enable/Disable news filter input string NewsTimes = ""; // News times in format "yyyy.MM.dd HH:MI;..." input int MinutesBefore = 30; // Minutes before news to block trading input int MinutesAfter = 60; // Minutes after news to block trading //--- Pullback Parameters input bool Enable_EMA50_Pullback = true; // Enable EMA50 pullback input bool Enable_Midpoint_Pullback = true; // Enable midpoint pullback input bool Enable_MACD_Pullback = true; // Enable MACD pullback input double EMA50_Pullback_Tolerance = 3.0; // EMA50 pullback tolerance (USD) - increased for more signals input double Midpoint_Pullback_Tolerance = 3.0; // Midpoint pullback tolerance (USD) - increased for more signals //--- Risk Management input double SL_Multiplier = 1.5; // SL multiplier for ATR input double RiskRewardRatio = 2.0; // Risk/Reward ratio for TP input double Risk_Per_Trade_Percent = 1.0; // Maximum risk per trade (%) input int SL_Type = 0; // SL Type (0=Fixed ATR, 1=Dynamic ATR, 2=Swing Low/High) input int TP_Type = 0; // TP Type (0=Fixed RR, 1=Multi-Target, 2=Support/Resistance) input bool Enable_Partial_TP = true; // Enable partial take profit input double TP1_Close_Percent = 40.0; // Percent to close at first target input double TP2_Close_Percent = 30.0; // Percent to close at second target input double TP3_Close_Percent = 30.0; // Percent to close at third target input double TP1_RR_Ratio = 1.2; // Risk-Reward ratio for TP1 input double TP2_RR_Ratio = 2.0; // Risk-Reward ratio for TP2 input double TP3_RR_Ratio = 3.0; // Risk-Reward ratio for TP3 input bool Enable_Trend_Exit = true; // Enable trend reversal exit //--- Signal Strength & Risk Adjustment input double Strong_Signal_Threshold = 0.8; // Signal strength threshold for increasing risk input double Weak_Signal_Threshold = 0.5; // Signal strength threshold for decreasing risk input double Strong_Signal_Risk_Boost = 0.1; // Additional risk percentage for strong signals (0.1 = 10% more) input double Weak_Signal_Risk_Reduction = 0.2; // Risk reduction percentage for weak signals (0.2 = 20% less) input double High_Volatility_Threshold = 1.8; // ATR ratio threshold for high volatility risk reduction input double Extreme_Volatility_Threshold = 2.5; // ATR ratio threshold for extreme volatility risk reduction input double High_Volatility_Risk_Multiplier = 0.7; // Risk multiplier during high volatility input double Extreme_Volatility_Risk_Multiplier = 0.5; // Risk multiplier during extreme volatility input double Drawdown_Protection_Threshold = 0.7; // Drawdown percentage (of max drawdown) to trigger protection input double Drawdown_Protection_Multiplier = 0.7; // Risk multiplier during drawdown protection input double Leverage_Warning_Threshold = 0.75; // Leverage usage percentage to trigger risk reduction input double Min_Lot_Size = 0.01; // Minimum lot size input double Max_Lot_Size = 10.0; // Maximum lot size input double Max_Daily_Risk_Percent = 3.0; // Maximum risk per day (%) input double Max_Drawdown_Percent = 10.0; // Maximum allowed drawdown (%) //--- Position Management input bool Enable_Position_Age_Limit = true; // Enable position age limit input int Max_Losing_Position_Minutes = 60; // Maximum time to hold losing position (minutes) input int Max_Winning_Position_Minutes = 240; // Maximum time to hold winning position (minutes) input int Min_Hold_Minutes = 15; // Minimum hold time to prevent excessive trading (minutes) input int Max_Open_Trades = 2; // Maximum number of open trades allowed input double ATR_Profit_Multiplier = 2.5; // ATR multiplier for dynamic profit target input bool Enable_Reversal_Exit = true; // Enable exit on reversal signals input double Max_Allowed_Loss_Percent = 2.0; // Maximum allowed loss percentage per trade input bool Close_On_High_Volatility = true; // Close positions on high volatility input double Volatility_Threshold_Multiplier = 2.5; // ATR multiplier for high volatility threshold //--- Trailing Stop input bool UseTrailingStop = true; // Enable trailing stop input int TrailingStopType = 2; // 0=Fixed pips, 1=ATR based, 2=Dynamic (推荐) input double TrailingStopPips = 10.0; // Trailing stop distance (pips) input double TrailingATRMultiplier = 2.0; // ATR multiplier for trailing stop input double TrailingDynamicFactor = 0.2; // Dynamic factor (0.1-1.0) for volatility adjustment input bool UsePartialTakeProfit = true; // Enable partial take profit (using multi-target strategy) input bool UseAdaptiveTrailingTightening = true; // Enable adaptive tightening of trailing stop when profit is high input bool UseGlobalProfitTargets = true; // Use global profit targets for partial take profit //--- Function Prototypes (Forward Declarations) enum TrendDirection {TREND_NONE, TREND_UP, TREND_DOWN}; TrendDirection GetTrendDirection(); bool IsSidewaysMarket(); bool IsMACDConfirmed(const bool is_buy_signal); bool IsVolumeSufficient(); bool IsATRVolatilitySufficient(); bool IsNewsEventNearby(); bool IsPullbackConditionMet(const bool is_buy_signal); bool IsEMA50Pullback(const bool is_buy_signal); bool IsMidpointPullback(const bool is_buy_signal); bool IsMACDPullback(const bool is_buy_signal); bool ApplyPreEntryFilters(const bool is_buy_signal); double GetEMA50(const int shift = 0); double GetEMA200(const int shift = 0); bool GetMACDValues(double &main[], double &signal[], double &histogram[], const int shift = 0); double GetVolumeMA(const int shift = 0); double GetATR(const int shift = 0); long GetCurrentVolume(const int shift = 0); //--- Global Variables CTrade trade; // Trade object //--- Trailing stop variables double last_trailing_level_buy = 0.0; // Last trailing level for buy positions double last_trailing_level_sell = 0.0; // Last trailing level for sell positions //--- Multi-target TP tracking variables double global_tp1_price = 0.0; double global_tp2_price = 0.0; double global_tp3_price = 0.0; //--- Indicator Handles int handle_ema50; // EMA50 handle int handle_ema200; // EMA200 handle int handle_macd; // MACD handle int handle_volume_ma; // Volume SMA handle int handle_atr; // ATR handle //--- UT Bot variables double prev_ut_buy_signal = 0; double prev_ut_sell_signal = 0; //--- Pullback tracking double ut_signal_candle_high = 0; double ut_signal_candle_low = 0; double ut_signal_candle_midpoint = 0; bool waiting_for_pullback = false; //--- Risk management variables double account_balance = 0.0; double account_equity = 0.0; double daily_risk_used = 0.0; int last_reset_day = 0; int last_reset_day_year = 0; //--- Signal tracking variables bool is_ut_bot_signal = false; int signal_direction = 0; // 1=Buy, -1=Sell, 0=None datetime signal_candle_time = 0; double signal_candle_open = 0.0; double signal_candle_high = 0.0; double signal_candle_low = 0.0; double signal_candle_close = 0.0; //--- Position management variables int active_trades_buy = 0; // Number of active BUY trades int active_trades_sell = 0; // Number of active SELL trades int total_open_trades = 0; // Total number of open trades double position_profit = 0.0; // Current position profit int position_age_minutes = 0; // Age of position in minutes double atr_value = 0.0; // Current ATR value double avg_atr_20 = 0.0; // 20-period average ATR (for volatility comparison) bool trend_reversal_detected = false; // Flag for trend reversal detection double volatility_threshold = 0.0; // Calculated volatility threshold for position closure bool position_close_requested = false; // Flag to request position closure //--- Pullback strategy performance tracking variables double g_ema50_strategy_performance = 0.5; // Performance score (0-1) for EMA50 strategy double g_midpoint_strategy_performance = 0.5; // Performance score (0-1) for Midpoint strategy double g_macd_strategy_performance = 0.5; // Performance score (0-1) for MACD strategy int g_ema50_trades_count = 0; // Number of trades taken with EMA50 confirmation int g_midpoint_trades_count = 0; // Number of trades taken with Midpoint confirmation int g_macd_trades_count = 0; // Number of trades taken with MACD confirmation int g_ema50_winning_trades = 0; // Number of winning trades for EMA50 strategy int g_midpoint_winning_trades = 0; // Number of winning trades for Midpoint strategy int g_macd_winning_trades = 0; // Number of winning trades for MACD strategy //--- Trade tracking variables bool g_last_ema50_pullback = false; // Last EMA50 pullback confirmation bool g_last_midpoint_pullback = false; // Last Midpoint pullback confirmation bool g_last_macd_pullback = false; // Last MACD pullback confirmation double g_last_entry_price = 0.0; // Last trade entry price int g_last_trade_type = 0; // Last trade type (0=none, 1=buy, 2=sell) //--- Constants const int EA_Magic_Number = 20231114; // EA Magic Number const int SignalTimeout = 24; // Signal timeout in hours enum PullbackDirection {NONE, BUY, SELL}; PullbackDirection current_pullback_direction = NONE; //--- Variables already declared above //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize daily risk tracking datetime now = TimeCurrent(); last_reset_day = TimeDayOfYear(now); last_reset_day_year = TimeYear(now); daily_risk_used = 0.0; } //+------------------------------------------------------------------+ //| Calculate signal strength based on multiple indicators | //+------------------------------------------------------------------+ double CalculateSignalStrength(bool is_buy_signal) { double strength = 0.5; // Start with neutral strength // Get trend information TrendDirection trend = GetTrendDirection(); double trend_strength = GetTrendStrength(); // Get MACD values MACDValues macd = GetMACDValues(); // Get volume information double current_volume = GetCurrentVolume(); double volume_ma = GetVolumeMA(); // Get price action information double ema50 = GetEMA50(); double ema200 = GetEMA200(); // Factor 1: Trend alignment (+0.2 points) if((trend == TREND_UP && is_buy_signal) || (trend == TREND_DOWN && !is_buy_signal)) strength += 0.2; else if((trend == TREND_UP && !is_buy_signal) || (trend == TREND_DOWN && is_buy_signal)) strength -= 0.1; // Slight penalty for counter-trend trades // Factor 2: Trend strength (+0 to 0.2 points) strength += trend_strength * 0.2; // Factor 3: MACD confirmation (+0 to 0.2 points) if((is_buy_signal && macd.histogram > 0) || (!is_buy_signal && macd.histogram < 0)) { // Scale histogram contribution based on its absolute value double histo_strength = MathMin(MathAbs(macd.histogram) * 10000, 1.0); strength += histo_strength * 0.2; // Additional bonus for MACD line crossover if((is_buy_signal && macd.macd > macd.signal) || (!is_buy_signal && macd.macd < macd.signal)) strength += 0.1; } // Factor 4: Volume confirmation (+0 to 0.15 points) if(volume_ma > 0 && current_volume > volume_ma) { double volume_ratio = MathMin(current_volume / volume_ma, 2.0); strength += (volume_ratio - 1.0) * 0.15; } // Factor 5: EMA position confirmation (+0 to 0.15 points) if(is_buy_signal && ema50 > ema200) // Golden cross alignment strength += 0.15; else if(!is_buy_signal && ema50 < ema200) // Death cross alignment strength += 0.15; // Clamp strength to 0-1 range strength = MathMax(0.0, MathMin(1.0, strength)); return strength; } //+------------------------------------------------------------------+ //| // Initialize max equity tracking GetMaxAccountEquity(); // This will set the initial max equity value //--- Check if the timeframe is M15 when restriction is enabled if(AllowOnlyM15 && Period() != PERIOD_M15) { Alert("Error: EA must run on M15 timeframe only!"); return(INIT_FAILED); } //--- Initialize indicators handle_ema50 = iMA(_Symbol, PERIOD_M15, EMA50_Period, 0, MODE_EMA, PRICE_CLOSE); handle_ema200 = iMA(_Symbol, PERIOD_M15, EMA200_Period, 0, MODE_EMA, PRICE_CLOSE); handle_macd = iMACD(_Symbol, PERIOD_M15, MACD_Fast, MACD_Slow, MACD_Signal, PRICE_CLOSE); handle_volume_ma = iMA(_Symbol, PERIOD_M15, Volume_MA_Period, 0, MODE_SMA, VOLUME_TICK); handle_atr = iATR(_Symbol, PERIOD_M15, ATR_Period); //--- Check if indicators were created successfully if(handle_ema50 == INVALID_HANDLE || handle_ema200 == INVALID_HANDLE || handle_macd == INVALID_HANDLE || handle_volume_ma == INVALID_HANDLE || handle_atr == INVALID_HANDLE) { Alert("Error: Failed to initialize indicators!"); return(INIT_FAILED); } //--- Set trade parameters trade.SetExpertMagicNumber(20231114); trade.SetTypeFilling(ORDER_FILLING_FOK); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Destroy indicator handles if(handle_ema50 != INVALID_HANDLE) IndicatorRelease(handle_ema50); if(handle_ema200 != INVALID_HANDLE) IndicatorRelease(handle_ema200); if(handle_macd != INVALID_HANDLE) IndicatorRelease(handle_macd); if(handle_volume_ma != INVALID_HANDLE) IndicatorRelease(handle_volume_ma); if(handle_atr != INVALID_HANDLE) IndicatorRelease(handle_atr); } //+------------------------------------------------------------------+ //| Check trend direction using EMA50 and EMA200 | //+------------------------------------------------------------------+ TrendDirection GetTrendDirection() { // Use current bar's EMAs instead of previous bar double ema50 = GetEMA50(0); double ema200 = GetEMA200(0); if(ema50 == 0 || ema200 == 0) { Print("Warning: Invalid EMA values (ema50=", ema50, ", ema200=", ema200, ")"); return(TREND_NONE); } // Always determine trend direction regardless of distance if(ema50 > ema200) return(TREND_UP); else return(TREND_DOWN); } //+------------------------------------------------------------------+ //| Check if market is sideways based on EMA distance | //+------------------------------------------------------------------+ bool IsSidewaysMarket() { double ema50 = GetEMA50(0); double ema200 = GetEMA200(0); if(ema50 == 0 || ema200 == 0) return(false); double ema_distance = MathAbs(ema50 - ema200); double price_base = (ema50 + ema200) / 2.0; // Use average of EMAs as price base double ema_distance_pct = (ema_distance / price_base) * 100.0; // Convert to percentage Print("EMA distance: ", ema_distance_pct, "%, Min threshold: ", Min_EMA_Distance_Pct, "%"); return(ema_distance_pct < Min_EMA_Distance_Pct); } //+------------------------------------------------------------------+ //| Check MACD momentum confirmation | //+------------------------------------------------------------------+ bool IsMACDConfirmed(const bool is_buy_signal) { double main[1], signal[1], histogram[1]; double main_prev[1], signal_prev[1], histogram_prev[1]; if(!GetMACDValues(main, signal, histogram, 0) || !GetMACDValues(main_prev, signal_prev, histogram_prev, 1)) { Print("Failed to get MACD values"); return(false); } // Log MACD values for debugging Print("MACD values - Main: ", main[0], ", Signal: ", signal[0], ", Histogram: ", histogram[0]); if(is_buy_signal) { // For Buy signal confirmation - simplified conditions bool main_above_signal = main[0] > signal[0]; bool positive_histogram = histogram[0] >= 0; bool improving = histogram[0] >= histogram_prev[0]; Print("MACD buy confirmation - Main above signal: ", main_above_signal, ", Positive histogram: ", positive_histogram, ", Improving: ", improving); return(main_above_signal && (positive_histogram || improving)); } else { // For Sell signal confirmation - simplified conditions bool main_below_signal = main[0] < signal[0]; bool negative_histogram = histogram[0] <= 0; bool improving = histogram[0] <= histogram_prev[0]; Print("MACD sell confirmation - Main below signal: ", main_below_signal, ", Negative histogram: ", negative_histogram, ", Improving: ", improving); return(main_below_signal && (negative_histogram || improving)); } } //+------------------------------------------------------------------+ //| Check volume filter | //+------------------------------------------------------------------+ bool IsVolumeSufficient() { long current_volume = GetCurrentVolume(0); // Use current bar instead of previous double volume_ma = GetVolumeMA(0); // Log volume information Print("Volume check - Current: ", current_volume, ", MA: ", volume_ma, ", Min ratio: ", Volume_Min_Ratio); if(current_volume == 0 || volume_ma == 0) { Print("Invalid volume values"); return(false); } bool sufficient = current_volume >= (volume_ma * Volume_Min_Ratio); Print("Volume sufficient: ", sufficient); return(sufficient); } //+------------------------------------------------------------------+ //| Check ATR volatility filter | //+------------------------------------------------------------------+ bool IsATRVolatilitySufficient() { double atr_value = GetATR(0); // Use current bar instead of previous // Log ATR information Print("ATR volatility check - Value: ", atr_value, ", Min required: ", ATR_Min); if(atr_value == 0) { Print("Invalid ATR value"); return(false); } bool sufficient = atr_value >= ATR_Min; Print("ATR volatility sufficient: ", sufficient); return(sufficient); } //+------------------------------------------------------------------+ //| Check if current time is during news event | //+------------------------------------------------------------------+ bool IsDuringNewsEvent() { if(!UseNewsFilter || NewsTimes == "") return(false); datetime current_time = TimeCurrent(); string separator = ";"; int pos = 0; string news_time_str; // Parse semicolon-separated news times string news_times_copy = NewsTimes; // Create a copy to modify while((pos = StringFind(news_times_copy, separator, pos)) >= 0) { news_time_str = StringSubstr(news_times_copy, 0, pos); news_times_copy = StringSubstr(news_times_copy, pos + 1); pos = 0; // Convert string to datetime datetime news_time = StringToTime(news_time_str); if(news_time != 0) { // Check if current time is within the news window datetime start_time = news_time - (MinutesBefore * 60); datetime end_time = news_time + (MinutesAfter * 60); if(current_time >= start_time && current_time <= end_time) return(true); } } // Check the last news time if(NewsTimes != "") { datetime news_time = StringToTime(NewsTimes); if(news_time != 0) { datetime start_time = news_time - (MinutesBefore * 60); datetime end_time = news_time + (MinutesAfter * 60); if(current_time >= start_time && current_time <= end_time) return(true); } } return(false); } //+------------------------------------------------------------------+ //| Check for EMA50 pullback | //+------------------------------------------------------------------+ bool IsEMA50Pullback(const bool is_buy_signal) { if(!Enable_EMA50_Pullback) return(false); // Get essential values double ema50 = GetEMA50(0); if(ema50 == 0) return(false); double ema50_prev = GetEMA50(1); double current_close = iClose(_Symbol, PERIOD_M15, 0); double prev_close = iClose(_Symbol, PERIOD_M15, 1); double current_high = iHigh(_Symbol, PERIOD_M15, 0); double current_low = iLow(_Symbol, PERIOD_M15, 0); double prev_high = iHigh(_Symbol, PERIOD_M15, 1); double prev_low = iLow(_Symbol, PERIOD_M15, 1); // Get volatility metrics double atr = GetATR(0); double atr_avg_20 = iATR(_Symbol, PERIOD_M15, 20, 0); double volatility_ratio = atr > 0 ? atr / atr_avg_20 : 1.0; // Dynamic tolerance based on volatility and trend double base_tolerance = MathMax(atr * 0.7, EMA50_Pullback_Tolerance); double dynamic_tolerance = base_tolerance * (1.0 + (volatility_ratio - 1.0) * 0.5); // Adjust by 50% of volatility deviation // Get trend strength double trend_strength = GetTrendStrength(); // Adjust tolerance based on trend strength if(trend_strength > 0.7) dynamic_tolerance *= 0.8; // Tighter tolerance in strong trends else if(trend_strength < 0.4) dynamic_tolerance *= 1.2; // Wider tolerance in weak trends if(is_buy_signal) { // Check for pullback to EMA50 in uptrend bool price_near_ema50 = current_close >= ema50 - dynamic_tolerance * 1.5 && current_close <= ema50 + dynamic_tolerance * 0.5; bool low_tested_ema50 = current_low <= ema50 + dynamic_tolerance * 0.5; bool prev_low_tested_ema50 = prev_low <= ema50 + dynamic_tolerance * 0.5; // Price action confirmation bool bullish_candlestick = current_close > current_open; bool bullish_close = current_close > prev_close; bool price_rejection = low_tested_ema50 && current_close > current_low + (current_high - current_low) * 0.6; // Trend alignment bool ema50_sloping_up = ema50 > ema50_prev; // Combine conditions with weighted importance bool touched_ema50_zone = price_near_ema50 || low_tested_ema50 || prev_low_tested_ema50; bool bullish_confirmation = bullish_candlestick || bullish_close || price_rejection; // Final decision with trend consideration bool result = touched_ema50_zone && (bullish_confirmation || (trend_strength > 0.6 && ema50_sloping_up)); // Enhanced logging if(DebugMode) { Print("EMA50 Buy Check - Price near EMA50: ", price_near_ema50, ", Low tested: ", low_tested_ema50, ", Prev low tested: ", prev_low_tested_ema50); Print("EMA50 Buy Check - Bullish candle: ", bullish_candlestick, ", Bullish close: ", bullish_close, ", Price rejection: ", price_rejection); Print("EMA50 Buy Check - EMA50 sloping up: ", ema50_sloping_up, ", Trend strength: ", trend_strength); Print("EMA50 Buy Check - Result: ", result, ", Dynamic tolerance: ", dynamic_tolerance, ", Volatility ratio: ", volatility_ratio); } return(result); } else { // Check for pullback to EMA50 in downtrend bool price_near_ema50 = current_close >= ema50 - dynamic_tolerance * 0.5 && current_close <= ema50 + dynamic_tolerance * 1.5; bool high_tested_ema50 = current_high >= ema50 - dynamic_tolerance * 0.5; bool prev_high_tested_ema50 = prev_high >= ema50 - dynamic_tolerance * 0.5; // Price action confirmation bool bearish_candlestick = current_close < current_open; bool bearish_close = current_close < prev_close; bool price_rejection = high_tested_ema50 && current_close < current_high - (current_high - current_low) * 0.6; // Trend alignment bool ema50_sloping_down = ema50 < ema50_prev; // Combine conditions with weighted importance bool touched_ema50_zone = price_near_ema50 || high_tested_ema50 || prev_high_tested_ema50; bool bearish_confirmation = bearish_candlestick || bearish_close || price_rejection; // Final decision with trend consideration bool result = touched_ema50_zone && (bearish_confirmation || (trend_strength > 0.6 && ema50_sloping_down)); // Enhanced logging if(DebugMode) { Print("EMA50 Sell Check - Price near EMA50: ", price_near_ema50, ", High tested: ", high_tested_ema50, ", Prev high tested: ", prev_high_tested_ema50); Print("EMA50 Sell Check - Bearish candle: ", bearish_candlestick, ", Bearish close: ", bearish_close, ", Price rejection: ", price_rejection); Print("EMA50 Sell Check - EMA50 sloping down: ", ema50_sloping_down, ", Trend strength: ", trend_strength); Print("EMA50 Sell Check - Result: ", result, ", Dynamic tolerance: ", dynamic_tolerance, ", Volatility ratio: ", volatility_ratio); } return(result); } } //+------------------------------------------------------------------+ //| Check for midpoint pullback | //+------------------------------------------------------------------+ bool IsMidpointPullback(const bool is_buy_signal) { if(!Enable_Midpoint_Pullback || ut_signal_candle_midpoint == 0) return(false); // Get essential price data double current_price = iClose(_Symbol, PERIOD_M15, 0); double prev_price = iClose(_Symbol, PERIOD_M15, 1); double current_high = iHigh(_Symbol, PERIOD_M15, 0); double current_low = iLow(_Symbol, PERIOD_M15, 0); double current_open = iOpen(_Symbol, PERIOD_M15, 0); // Get volatility metrics double atr = GetATR(0); double atr_avg_20 = iATR(_Symbol, PERIOD_M15, 20, 0); double volatility_ratio = atr > 0 ? atr / atr_avg_20 : 1.0; // Get trend strength double trend_strength = GetTrendStrength(); // Dynamic tolerance based on volatility and market conditions double base_tolerance = MathMax(atr * 0.4, Midpoint_Pullback_Tolerance); double dynamic_tolerance = base_tolerance * (1.0 + (volatility_ratio - 1.0) * 0.5); // Adjust tolerance based on trend strength if(trend_strength > 0.7) dynamic_tolerance *= 0.8; // Tighter in strong trends else if(trend_strength < 0.4) dynamic_tolerance *= 1.2; // Wider in weak trends // Calculate price distances to midpoint double distance_current = MathAbs(current_price - ut_signal_candle_midpoint); double distance_prev = MathAbs(prev_price - ut_signal_candle_midpoint); // Multi-level midpoint proximity checks bool very_close_to_midpoint = distance_current <= dynamic_tolerance * 0.6; bool close_to_midpoint = distance_current <= dynamic_tolerance * 0.8; bool near_midpoint = distance_current <= dynamic_tolerance * 1.2; bool prev_near_midpoint = distance_prev <= dynamic_tolerance * 1.2; // Price movement toward midpoint with acceleration check bool moving_toward_midpoint = distance_current < distance_prev * 0.95; bool strongly_moving_toward = distance_current < distance_prev * 0.85; // Price action confirmation bool candlestick_direction = (is_buy_signal ? current_close > current_open : current_close < current_open); bool price_reversal = is_buy_signal ? (current_low <= ut_signal_candle_midpoint - atr * 0.3 && current_close > current_low + (current_high - current_low) * 0.6) : (current_high >= ut_signal_candle_midpoint + atr * 0.3 && current_close < current_high - (current_high - current_low) * 0.6); // Primary midpoint conditions with weighted importance bool midpoint_condition = very_close_to_midpoint || (near_midpoint && candlestick_direction) || (strongly_moving_toward && close_to_midpoint) || (price_reversal && near_midpoint); // Trend-based override conditions if(trend_strength > 0.75) midpoint_condition = near_midpoint || (moving_toward_midpoint && prev_near_midpoint); // Volatility-based adjustments if(volatility_ratio > 1.8) midpoint_condition = midpoint_condition || (moving_toward_midpoint && prev_near_midpoint); // Enhanced logging with dynamic values if(DebugMode) { Print("Midpoint Check - Midpoint: ", ut_signal_candle_midpoint, ", Current Price: ", current_price); Print("Midpoint Check - Dynamic tolerance: ", dynamic_tolerance, ", Volatility ratio: ", volatility_ratio); Print("Midpoint Check - Near midpoint: ", near_midpoint, ", Moving toward: ", moving_toward_midpoint); Print("Midpoint Check - Price reversal: ", price_reversal, ", Candlestick direction: ", candlestick_direction); Print("Midpoint Check - Trend strength: ", trend_strength, ", Result: ", midpoint_condition); } return(midpoint_condition); } //+------------------------------------------------------------------+ //| Check for MACD pullback and re-expansion with volatility and trend adaptation | //+------------------------------------------------------------------+ bool IsMACDPullback(const bool is_buy_signal) { if(!Enable_MACD_Pullback) return(false); // Get volatility metrics double atr = iATR(Symbol(), 0, ATR_Period, 0); double atr_avg_20 = CalculateATR_SMA(20); double volatility_ratio = (atr_avg_20 > 0) ? atr / atr_avg_20 : 1.0; // Get trend strength and direction double trend_strength = CalculateTrendStrength(); int trend_direction = GetTrendDirection(); double main[1], signal[1], histogram[1]; double main_prev[1], signal_prev[1], histogram_prev[1]; double main_prev_2[1], signal_prev_2[1], histogram_prev_2[1]; // Get more bars of MACD data for better trend analysis if(!GetMACDValues(main, signal, histogram, 0) || !GetMACDValues(main_prev, signal_prev, histogram_prev, 1) || !GetMACDValues(main_prev_2, signal_prev_2, histogram_prev_2, 2)) return(false); // Get price data for confirmation double current_price = Close[0]; double prev_price = Close[1]; // Calculate histogram slope and divergence double histogram_slope = histogram[0] - histogram_prev[0]; bool histogram_divergence = false; if(is_buy_signal) histogram_divergence = histogram[0] > 0 && prev_price < current_price; else histogram_divergence = histogram[0] < 0 && prev_price > current_price; // Dynamic threshold based on volatility and trend int required_conditions = 3; // Default required conditions if(volatility_ratio > 1.8) // Higher volatility requires more confirmation required_conditions = 4; if(trend_strength > 0.7) // Strong trend can have slightly lower requirements required_conditions = MathMax(2, required_conditions - 1); if(is_buy_signal) { // Enhanced buy conditions with trend and volatility adaptation bool main_above_signal = main[0] > signal[0]; bool histogram_positive = histogram[0] > 0; bool improving_histogram = histogram[0] > histogram_prev[0]; bool main_line_improving = main[0] > main_prev[0]; bool histogram_building = histogram[0] > histogram_prev[0] && histogram_prev[0] > histogram_prev_2[0]; // Price action confirmation - ensure MACD signals align with price movements bool price_confirmation = prev_price < current_price && current_price > Open[0]; // Trend alignment - ensure MACD signals align with overall trend bool trend_alignment = (trend_direction >= 0) ? true : (histogram_building && main_above_signal); // Debug logging with dynamic values if(DebugMode) { Print("MACD Buy Check - Main: ", main[0], ", Signal: ", signal[0], ", Histogram: ", histogram[0]); Print("MACD Buy Check - Volatility ratio: ", volatility_ratio, ", Trend strength: ", trend_strength); Print("MACD Buy Check - Main above signal: ", main_above_signal, ", Positive histogram: ", histogram_positive); Print("MACD Buy Check - Improving histogram: ", improving_histogram, ", Histogram slope: ", histogram_slope); Print("MACD Buy Check - Price confirmation: ", price_confirmation, ", Trend alignment: ", trend_alignment); } // Return true if required conditions are met int condition_count = 0; if(main_above_signal) condition_count += 2; // Weight main signal line position higher if(histogram_positive) condition_count++; if(improving_histogram) condition_count++; if(main_line_improving) condition_count++; if(histogram_building) condition_count++; if(price_confirmation) condition_count++; if(histogram_divergence) condition_count++; // Main above signal is a critical condition that must be met in most cases bool critical_condition = main_above_signal || (histogram_building && trend_strength > 0.6); // Adjust requirements based on trend strength if(trend_strength > 0.8 && main_above_signal && histogram_building) { // Strong trend with clear MACD confirmation if(DebugMode) Print("MACD Buy Check - Strong trend override activated"); return(true); } // Calculate weighted score instead of simple count double weighted_score = (main_above_signal ? 0.4 : 0) + (histogram_building ? 0.3 : 0) + (price_confirmation ? 0.15 : 0) + (histogram_positive ? 0.15 : 0); bool result = false; if(trend_strength > 0.5) result = (critical_condition && condition_count >= MathMax(2, required_conditions - 1)) || weighted_score >= 0.65; else result = (critical_condition && condition_count >= required_conditions) || weighted_score >= 0.75; if(DebugMode) { Print("MACD Buy Pullback - Condition count: ", condition_count, ", Weighted score: ", weighted_score); Print("MACD Buy Pullback - Required conditions: ", required_conditions, ", Result: ", result); } return(result); } else { // Enhanced sell conditions with trend and volatility adaptation bool main_below_signal = main[0] < signal[0]; bool histogram_negative = histogram[0] < 0; bool improving_histogram = histogram[0] < histogram_prev[0]; bool main_line_improving = main[0] < main_prev[0]; bool histogram_building = histogram[0] < histogram_prev[0] && histogram_prev[0] < histogram_prev_2[0]; // Price action confirmation - ensure MACD signals align with price movements bool price_confirmation = prev_price > current_price && current_price < Open[0]; // Trend alignment - ensure MACD signals align with overall trend bool trend_alignment = (trend_direction <= 0) ? true : (histogram_building && main_below_signal); // Debug logging with dynamic values if(DebugMode) { Print("MACD Sell Check - Main: ", main[0], ", Signal: ", signal[0], ", Histogram: ", histogram[0]); Print("MACD Sell Check - Volatility ratio: ", volatility_ratio, ", Trend strength: ", trend_strength); Print("MACD Sell Check - Main below signal: ", main_below_signal, ", Negative histogram: ", histogram_negative); Print("MACD Sell Check - Improving histogram: ", improving_histogram, ", Histogram slope: ", histogram_slope); Print("MACD Sell Check - Price confirmation: ", price_confirmation, ", Trend alignment: ", trend_alignment); } // Return true if required conditions are met int condition_count = 0; if(main_below_signal) condition_count += 2; // Weight main signal line position higher if(histogram_negative) condition_count++; if(improving_histogram) condition_count++; if(main_line_improving) condition_count++; if(histogram_building) condition_count++; if(price_confirmation) condition_count++; if(histogram_divergence) condition_count++; // Main below signal is a critical condition that must be met in most cases bool critical_condition = main_below_signal || (histogram_building && trend_strength > 0.6); // Adjust requirements based on trend strength if(trend_strength > 0.8 && main_below_signal && histogram_building) { // Strong trend with clear MACD confirmation if(DebugMode) Print("MACD Sell Check - Strong trend override activated"); return(true); } // Calculate weighted score instead of simple count double weighted_score = (main_below_signal ? 0.4 : 0) + (histogram_building ? 0.3 : 0) + (price_confirmation ? 0.15 : 0) + (histogram_negative ? 0.15 : 0); bool result = false; if(trend_strength > 0.5) result = (critical_condition && condition_count >= MathMax(2, required_conditions - 1)) || weighted_score >= 0.65; else result = (critical_condition && condition_count >= required_conditions) || weighted_score >= 0.75; if(DebugMode) { Print("MACD Sell Pullback - Condition count: ", condition_count, ", Weighted score: ", weighted_score); Print("MACD Sell Pullback - Required conditions: ", required_conditions, ", Result: ", result); } return(result); } } //+------------------------------------------------------------------+ //| Global variables for pullback strategy performance tracking | //+------------------------------------------------------------------+ double g_ema50_strategy_performance = 0.5; // Performance score (0-1) for EMA50 strategy double g_midpoint_strategy_performance = 0.5; // Performance score (0-1) for Midpoint strategy double g_macd_strategy_performance = 0.5; // Performance score (0-1) for MACD strategy int g_ema50_trades_count = 0; // Number of trades taken with EMA50 confirmation int g_midpoint_trades_count = 0; // Number of trades taken with Midpoint confirmation int g_macd_trades_count = 0; // Number of trades taken with MACD confirmation int g_ema50_winning_trades = 0; // Number of winning trades for EMA50 strategy int g_midpoint_winning_trades = 0; // Number of winning trades for Midpoint strategy int g_macd_winning_trades = 0; // Number of winning trades for MACD strategy //+------------------------------------------------------------------+ //| Update pullback strategy performance metrics | //+------------------------------------------------------------------+ void UpdatePullbackStrategyPerformance(const bool ema50_confirmed, const bool midpoint_confirmed, const bool macd_confirmed, const bool is_winning_trade) { // Update counters and performance for each strategy that confirmed the trade if(ema50_confirmed) { g_ema50_trades_count++; if(is_winning_trade) g_ema50_winning_trades++; g_ema50_strategy_performance = (double)g_ema50_winning_trades / (double)g_ema50_trades_count; Print("EMA50 Strategy Performance Updated: ", g_ema50_strategy_performance, " (Wins: ", g_ema50_winning_trades, ", Total: ", g_ema50_trades_count, ")"); } if(midpoint_confirmed) { g_midpoint_trades_count++; if(is_winning_trade) g_midpoint_winning_trades++; g_midpoint_strategy_performance = (double)g_midpoint_winning_trades / (double)g_midpoint_trades_count; Print("Midpoint Strategy Performance Updated: ", g_midpoint_strategy_performance, " (Wins: ", g_midpoint_winning_trades, ", Total: ", g_midpoint_trades_count, ")"); } if(macd_confirmed) { g_macd_trades_count++; if(is_winning_trade) g_macd_winning_trades++; g_macd_strategy_performance = (double)g_macd_winning_trades / (double)g_macd_trades_count; Print("MACD Strategy Performance Updated: ", g_macd_strategy_performance, " (Wins: ", g_macd_winning_trades, ", Total: ", g_macd_trades_count, ")"); } } //+------------------------------------------------------------------+ //| Update pullback strategy performance metrics | //+------------------------------------------------------------------+ void UpdatePullbackStrategyPerformance(const bool ema50_confirmed, const bool midpoint_confirmed, const bool macd_confirmed, const bool is_winning_trade) { // Update counters and performance for each strategy that confirmed the trade if(ema50_confirmed) { g_ema50_trades_count++; if(is_winning_trade) g_ema50_winning_trades++; g_ema50_strategy_performance = (double)g_ema50_winning_trades / (double)g_ema50_trades_count; Print("EMA50 Strategy Performance Updated: ", g_ema50_strategy_performance, " (Wins: ", g_ema50_winning_trades, ", Total: ", g_ema50_trades_count, ")"); } if(midpoint_confirmed) { g_midpoint_trades_count++; if(is_winning_trade) g_midpoint_winning_trades++; g_midpoint_strategy_performance = (double)g_midpoint_winning_trades / (double)g_midpoint_trades_count; Print("Midpoint Strategy Performance Updated: ", g_midpoint_strategy_performance, " (Wins: ", g_midpoint_winning_trades, ", Total: ", g_midpoint_trades_count, ")"); } if(macd_confirmed) { g_macd_trades_count++; if(is_winning_trade) g_macd_winning_trades++; g_macd_strategy_performance = (double)g_macd_winning_trades / (double)g_macd_trades_count; Print("MACD Strategy Performance Updated: ", g_macd_strategy_performance, " (Wins: ", g_macd_winning_trades, ", Total: ", g_macd_trades_count, ")"); } } //+------------------------------------------------------------------+ //| Track trade results and update strategy performance | //+------------------------------------------------------------------+ void TrackTradeResult(const int trade_type, const double entry_price, const double exit_price) { // Determine if the trade was profitable bool is_winning_trade = false; if(trade_type == 1) // Buy trade { is_winning_trade = exit_price > entry_price; } else if(trade_type == 2) // Sell trade { is_winning_trade = exit_price < entry_price; } // Log trade result Print("Trade result - Type: ", (trade_type == 1 ? "Buy" : "Sell"), ", Entry: ", entry_price, ", Exit: ", exit_price, ", Result: ", (is_winning_trade ? "Win" : "Loss")); // Update pullback strategy performance based on which strategies confirmed this trade UpdatePullbackStrategyPerformance(g_last_ema50_pullback, g_last_midpoint_pullback, g_last_macd_pullback, is_winning_trade); // Reset tracking variables g_last_ema50_pullback = false; g_last_midpoint_pullback = false; g_last_macd_pullback = false; g_last_entry_price = 0.0; g_last_trade_type = 0; } //+------------------------------------------------------------------+ //| Check if any pullback condition is met | //+------------------------------------------------------------------+ bool IsPullbackConditionMet(const bool is_buy_signal) { // At least one pullback method must be enabled if(!Enable_EMA50_Pullback && !Enable_Midpoint_Pullback && !Enable_MACD_Pullback) { Print("Warning: All pullback methods are disabled. No entries will be made."); return(false); } // Log which pullback methods are enabled and their current performance Print("Pullback methods enabled - EMA50: ", Enable_EMA50_Pullback, "(Perf: ", StringFormat("%.2f", g_ema50_strategy_performance), "), Midpoint: ", Enable_Midpoint_Pullback, "(Perf: ", StringFormat("%.2f", g_midpoint_strategy_performance), "), MACD: ", Enable_MACD_Pullback, "(Perf: ", StringFormat("%.2f", g_macd_strategy_performance), ")"); // Check each enabled pullback method bool ema50_pullback = IsEMA50Pullback(is_buy_signal); bool midpoint_pullback = IsMidpointPullback(is_buy_signal); bool macd_pullback = IsMACDPullback(is_buy_signal); // Count how many pullback methods are satisfied int satisfied_pullback_methods = 0; if(Enable_EMA50_Pullback && ema50_pullback) satisfied_pullback_methods++; if(Enable_Midpoint_Pullback && midpoint_pullback) satisfied_pullback_methods++; if(Enable_MACD_Pullback && macd_pullback) satisfied_pullback_methods++; // Get trend direction and strength to apply dynamic conditions TrendDirection trend = GetTrendDirection(); double trend_strength = GetTrendStrength(); // Enhanced logging if(ema50_pullback) Print("EMA50 pullback condition met"); if(midpoint_pullback) Print("Midpoint pullback condition met"); if(macd_pullback) Print("MACD pullback condition met"); Print("Current trend: ", trend, ", Trend strength: ", StringFormat("%.2f", trend_strength)); // Calculate weighted score based on strategy performance double weighted_score = 0.0; double total_weight = 0.0; // Assign weights based on performance and minimum confidence (0.3) double ema50_weight = MathMax(0.3, g_ema50_strategy_performance); double midpoint_weight = MathMax(0.3, g_midpoint_strategy_performance); double macd_weight = MathMax(0.3, g_macd_strategy_performance); // Calculate weighted score if(Enable_EMA50_Pullback && ema50_pullback) { weighted_score += ema50_weight; total_weight += ema50_weight; } if(Enable_Midpoint_Pullback && midpoint_pullback) { weighted_score += midpoint_weight; total_weight += midpoint_weight; } if(Enable_MACD_Pullback && macd_pullback) { weighted_score += macd_weight; total_weight += macd_weight; } // Calculate final confidence score (0-1) double confidence_score = total_weight > 0 ? weighted_score / total_weight : 0; Print("Pullback confidence score: ", StringFormat("%.2f", confidence_score)); // Apply dynamic conditions based on market environment and strategy performance bool result = false; if(IsSidewaysMarket() || trend == TREND_NONE) { // In sideways or no trend environments, require higher confidence Print("Sideways/No trend environment detected"); // Option 1: Multiple confirmations bool multiple_confirmations = satisfied_pullback_methods >= 2; // Option 2: High confidence from a single strategy bool high_confidence_single = confidence_score >= 0.7 && satisfied_pullback_methods >= 1; result = multiple_confirmations || high_confidence_single; Print("Sideways market result - Multiple confirmations: ", multiple_confirmations, ", High confidence: ", high_confidence_single); } else if(trend == TREND_UP || trend == TREND_DOWN) { // For clear trends, adjust requirements based on trend strength Print("Trending market detected with strength: ", StringFormat("%.2f", trend_strength)); if(trend_strength > 0.7) // Strong trend { // In strong trends, we can be more flexible with pullback requirements // But still need at least one pullback method or high confidence result = satisfied_pullback_methods >= 1 || confidence_score >= 0.6; Print("Strong trend - Using relaxed pullback requirements"); } else if(trend_strength > 0.4) // Moderate trend { // Need at least one pullback confirmation result = satisfied_pullback_methods >= 1; Print("Moderate trend - Requiring at least one pullback confirmation"); } else // Weak trend { // For weak trends, require multiple confirmations result = satisfied_pullback_methods >= 2; Print("Weak trend - Requiring multiple pullback confirmations"); } } // Additional check: If we have only one confirmation but it's from a high-performing strategy if(!result && satisfied_pullback_methods == 1) { if((Enable_EMA50_Pullback && ema50_pullback && g_ema50_strategy_performance > 0.7) || (Enable_Midpoint_Pullback && midpoint_pullback && g_midpoint_strategy_performance > 0.7) || (Enable_MACD_Pullback && macd_pullback && g_macd_strategy_performance > 0.7)) { result = true; Print("Overriding result: Single high-performing strategy confirmation"); } } // Additional check: If all enabled strategies confirm, always allow entry bool all_strategies_confirm = true; if(Enable_EMA50_Pullback && !ema50_pullback) all_strategies_confirm = false; if(Enable_Midpoint_Pullback && !midpoint_pullback) all_strategies_confirm = false; if(Enable_MACD_Pullback && !macd_pullback) all_strategies_confirm = false; if(all_strategies_confirm) { result = true; Print("All enabled pullback strategies confirm - Strong signal"); } Print("Pullback conditions result: ", result, " (", satisfied_pullback_methods, " methods satisfied, ", StringFormat("%.2f", confidence_score), " confidence score)"); // Store the current pullback confirmations for later performance tracking // These will be used in the trade result handler to update strategy performance g_last_ema50_pullback = ema50_pullback; g_last_midpoint_pullback = midpoint_pullback; g_last_macd_pullback = macd_pullback; return(result); } //+------------------------------------------------------------------+ //| Apply all 6 pre-entry filters | //+------------------------------------------------------------------+ bool ApplyPreEntryFilters(const bool is_buy_signal) { // Initialize filter pass count int filter_pass_count = 0; const int total_filters = 6; // Step 1: Trend filter - strict trend alignment with enhanced conditions TrendDirection trend = GetTrendDirection(); bool trend_filter_passed = false; // Check if market is sideways - now enforced as a filter bool is_sideways = IsSidewaysMarket(); Print("Filter 1/6 - Trend: ", trend == TREND_UP ? "UP" : (trend == TREND_DOWN ? "DOWN" : "NONE"), ", Sideways: ", is_sideways ? "YES" : "NO"); if(is_sideways && !Allow_Trading_In_Sideways_Market) { Print("Filter 1/6 FAILED: Market is sideways and trading in sideways market is disabled"); return(false); } // Strict trend direction check bool trend_match = (is_buy_signal && trend == TREND_UP) || (!is_buy_signal && trend == TREND_DOWN); if(!trend_match) { if(Allow_Counter_Trend_With_Confirmation) { // Require strong confirmation for counter-trend trades Print("Filter 1/6 - Checking counter-trend confirmation"); // For counter-trend trades, require stronger MACD confirmation double main[1], signal[1], histogram[1]; if(!GetMACDValues(main, signal, histogram, 0)) { Print("Filter 1/6 FAILED: Could not get MACD values for counter-trend confirmation"); return(false); } // Stronger counter-trend confirmation requirements bool strong_macd_confirmation = false; if(is_buy_signal) // Counter-trend buy in downtrend strong_macd_confirmation = histogram[0] > histogram[1] * 1.5 && main[0] > signal[0]; else // Counter-trend sell in uptrend strong_macd_confirmation = histogram[0] < histogram[1] * 1.5 && main[0] < signal[0]; if(!strong_macd_confirmation) { Print("Filter 1/6 FAILED: Counter-trend signal lacks strong confirmation"); return(false); } else { Print("Filter 1/6 PASSED: Strong counter-trend confirmation detected"); trend_filter_passed = true; filter_pass_count++; } } else { Print("Filter 1/6 FAILED: Signal direction doesn't match trend and counter-trend trading is disabled"); return(false); } } else { Print("Filter 1/6 PASSED: Signal direction matches trend direction"); trend_filter_passed = true; filter_pass_count++; } // Step 2: UT Bot signal confirmation - ensure signal strength is sufficient Print("Filter 2/6 - UT Bot signal validation"); // Since this is called after signal detection, ensure the signal strength is sufficient // Get ATR value for signal strength validation double atr = GetATR(0); if(atr <= 0) { Print("Filter 2/6 FAILED: Invalid ATR value for signal strength validation"); return(false); } // Calculate minimum required price movement based on ATR double min_movement_required = atr * UT_ATR_Multiplier * 0.8; // 80% of regular requirement as a confirmation // Ensure the signal has meaningful price movement if(signal_candle_high - signal_candle_low < min_movement_required) { Print("Filter 2/6 FAILED: UT Bot signal has insufficient price movement"); return(false); } Print("Filter 2/6 PASSED: UT Bot signal has sufficient strength"); filter_pass_count++; // Step 3: MACD momentum confirmation - now strictly enforced with additional checks Print("Filter 3/6 - MACD confirmation"); if(!IsMACDConfirmed(is_buy_signal)) { Print("Filter 3/6 FAILED: MACD confirmation not met"); return(false); } // Additional MACD strength check double macd_main[1], macd_signal[1], macd_hist[1]; if(GetMACDValues(macd_main, macd_signal, macd_hist, 0)) { // Check MACD histogram strength double hist_strength = MathAbs(macd_hist[0]); Print("MACD histogram strength: ", hist_strength); // If trend is strong, require less MACD strength; if trend is weak, require more MACD strength double min_hist_strength = 0.00002; // Default minimum strength if(is_sideways || trend == TREND_NONE) min_hist_strength *= 1.5; // Require stronger MACD in sideways markets if(hist_strength < min_hist_strength && is_sideways) { Print("Filter 3/6 FAILED: MACD histogram too weak for sideways market"); return(false); } } Print("Filter 3/6 PASSED: MACD confirmation and strength check passed"); filter_pass_count++; // Step 4: Volume filter - now strictly enforced with volatility consideration Print("Filter 4/6 - Volume confirmation"); if(!IsVolumeSufficient()) { Print("Filter 4/6 FAILED: Volume is insufficient"); return(false); } // Additional volume check against current volatility double volume_ma = GetVolumeMA(0); long current_volume = GetCurrentVolume(0); if(current_volume > 0 && volume_ma > 0) { double volume_ratio = (double)current_volume / volume_ma; Print("Current volume ratio: ", volume_ratio); // Adjust volume requirements based on market conditions if(is_sideways && volume_ratio < 1.2) // Need higher volume confirmation in sideways { Print("Filter 4/6 FAILED: Volume insufficient for sideways market"); return(false); } } Print("Filter 4/6 PASSED: Volume confirmation passed"); filter_pass_count++; // Step 5: ATR volatility filter - now strictly enforced with enhanced checks Print("Filter 5/6 - ATR volatility confirmation"); if(!IsATRVolatilitySufficient()) { Print("Filter 5/6 FAILED: ATR volatility is too low"); return(false); } // Additional volatility check against recent average double current_atr = GetATR(0); // Calculate average ATR over 20 periods for comparison double sum_atr = 0; int valid_atr_count = 0; for(int i = 0; i < 20; i++) { double atr_value = GetATR(i); if(atr_value > 0) { sum_atr += atr_value; valid_atr_count++; } } if(valid_atr_count > 0) { avg_atr_20 = sum_atr / valid_atr_count; double atr_ratio = current_atr / avg_atr_20; Print("Current ATR/20MA ATR ratio: ", atr_ratio); // Ensure volatility is not excessively high if(atr_ratio > Volatility_Threshold_Multiplier) { Print("Filter 5/6 FAILED: ATR volatility excessively high (potential market stress)"); return(false); } } Print("Filter 5/6 PASSED: ATR volatility confirmation passed"); filter_pass_count++; // Step 6: News filter - use the correct function name Print("Filter 6/6 - News event check"); if(IsNewsEventNearby()) { Print("Filter 6/6 FAILED: News event is nearby"); return(false); } Print("Filter 6/6 PASSED: No news events nearby"); filter_pass_count++; // Final validation: Ensure all filters passed if(filter_pass_count != total_filters) { Print("ERROR: Only ", filter_pass_count, " of ", total_filters, " filters passed"); return(false); } Print("SUCCESS: All 6 pre-entry filters passed - Strong signal confirmed"); return(true); } //+------------------------------------------------------------------+ //| Update account information and daily risk reset | //+------------------------------------------------------------------+ void UpdateAccountInfo() { // Update account balance and equity account_balance = AccountInfoDouble(ACCOUNT_BALANCE); account_equity = AccountInfoDouble(ACCOUNT_EQUITY); // Check if we need to reset daily risk (new day) MqlDateTime time_struct; TimeCurrent(time_struct); int current_day = time_struct.day; if(current_day != last_reset_day) { daily_risk_used = 0.0; last_reset_day = current_day; Print("Daily risk reset:", DoubleToString(daily_risk_used, 2), "% of account"); } } //+------------------------------------------------------------------+ //| Check if account drawdown is acceptable | //+------------------------------------------------------------------+ bool CheckAccountDrawdown() { // Calculate drawdown percentage double drawdown = 0.0; if(account_balance > 0) drawdown = ((account_balance - account_equity) / account_balance) * 100.0; // Check if drawdown exceeds maximum allowed if(drawdown >= Max_Drawdown_Percent) { Print("WARNING: Account drawdown of ", DoubleToString(drawdown, 2), "% exceeds maximum allowed of ", DoubleToString(Max_Drawdown_Percent, 2) + "%. Trading suspended."); return(false); } return(true); } //+------------------------------------------------------------------+ //| Check if we can take another trade based on daily risk limit | //+------------------------------------------------------------------+ bool CanTakeNewTrade(double risk_percent) { // Check if adding this trade would exceed daily risk limit if((daily_risk_used + risk_percent) > Max_Daily_Risk_Percent) { Print("WARNING: Cannot open new trade. Daily risk limit of ", DoubleToString(Max_Daily_Risk_Percent, 2), "% would be exceeded (current: ", DoubleToString(daily_risk_used, 2), "%, additional: ", DoubleToString(risk_percent, 2), "%)"); return(false); } return(true); } //+------------------------------------------------------------------+ //| Calculate position size based on risk and account size | //+------------------------------------------------------------------+ double CalculatePositionSize(double sl_pips, bool is_buy_signal = false) { // Ensure SL is positive if(sl_pips <= 0) { Print("Error: SL pips must be positive"); return(0.0); } // Get account information account_balance = AccountInfoDouble(ACCOUNT_BALANCE); account_equity = AccountInfoDouble(ACCOUNT_EQUITY); double account_free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN); if(account_balance <= 0 || account_equity <= 0) { Print("Error: Invalid account balance or equity"); return(0.0); } // Calculate current drawdown double max_equity = GetMaxAccountEquity(); double drawdown_percent = (max_equity > 0) ? ((max_equity - account_equity) / max_equity) * 100.0 : 0.0; // Adjust risk based on drawdown - reduce risk when drawdown is high double adjusted_risk_percent = Risk_Per_Trade_Percent; if(drawdown_percent > 0.5 * Max_Drawdown_Percent) { // Reduce risk when drawdown reaches half of maximum allowed double reduction_factor = 1.0 - ((drawdown_percent - 0.5 * Max_Drawdown_Percent) / (0.5 * Max_Drawdown_Percent)) * 0.5; adjusted_risk_percent *= reduction_factor; adjusted_risk_percent = MathMax(adjusted_risk_percent, Risk_Per_Trade_Percent * 0.3); // Minimum 30% of original risk } // Enhanced drawdown protection - additional reduction at higher drawdowns if(drawdown_percent > 0.7 * Max_Drawdown_Percent) { adjusted_risk_percent *= 0.7; // Further reduce risk at 70% of max drawdown Print("WARNING: High drawdown detected (" + DoubleToString(drawdown_percent, 2) + "%), risk further reduced"); } // Get current market volatility double current_atr = GetATR(0); double avg_atr_20 = GetAverageATR(20); double volatility_ratio = (avg_atr_20 > 0) ? current_atr / avg_atr_20 : 1.0; // Enhanced volatility adjustment if(volatility_ratio > 1.8) { double volatility_factor = MathMin(1.0 / volatility_ratio, 0.8); // Cap reduction at 20% adjusted_risk_percent *= volatility_factor; // Extreme volatility protection if(volatility_ratio > 2.5) { volatility_factor = MathMin(1.0 / volatility_ratio, 0.5); // Cap reduction at 50% for extreme volatility adjusted_risk_percent *= volatility_factor; Print("WARNING: Extreme market volatility detected, risk significantly reduced"); } } // Add signal strength based adjustment double signal_strength = CalculateSignalStrength(is_buy_signal); if(signal_strength > 0.8) // Strong signals can use slightly more risk { adjusted_risk_percent *= 1.1; // Increase risk by up to 10% for strong signals Print("Strong signal detected (" + DoubleToString(signal_strength, 2) + "), risk increased by 10%"); } else if(signal_strength < 0.5) // Reduce risk for weak signals { adjusted_risk_percent *= 0.8; // Decrease risk by 20% for weak signals Print("Weak signal detected (" + DoubleToString(signal_strength, 2) + "), risk decreased by 20%"); } // Check daily risk limit before calculating position size UpdateDailyRiskTracking(); // Update daily risk tracking if(!CanTakeNewTrade(adjusted_risk_percent, sl_pips)) { Print("Cannot take new trade: Daily risk limit reached or exceeded"); return(0.0); } // Calculate risk in currency using equity (more accurate than balance) double risk_in_currency = account_equity * (adjusted_risk_percent / 100.0); // Calculate position size: risk / (sl_pips * pip_value) double pip_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) / SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); if(pip_value <= 0) { Print("Error: Invalid pip value"); return(0.0); } double lot_size = risk_in_currency / (sl_pips * pip_value); // Apply margin check - ensure we have enough margin double required_margin = CalculateRequiredMargin(lot_size); if(required_margin > account_free_margin * 0.8) // Use max 80% of free margin { double safe_lot_size = (account_free_margin * 0.8) / required_margin * lot_size; lot_size = MathMin(lot_size, safe_lot_size); Print("Adjusted lot size due to margin constraints from ", DoubleToString(lot_size, 2), " to ", DoubleToString(safe_lot_size, 2)); } // Additional account leverage check double account_margin_used = AccountInfoDouble(ACCOUNT_MARGIN); double margin_usage_percent = (account_equity > 0) ? (account_margin_used / account_equity) * 100.0 : 0.0; if(margin_usage_percent > 75.0) // If already using more than 75% margin { lot_size *= 0.5; // Reduce lot size by 50% Print("Adjusted lot size due to high account leverage usage (" + DoubleToString(margin_usage_percent, 2) + "%), reduced by 50%"); } // Apply minimum and maximum lot size restrictions lot_size = MathMax(lot_size, Min_Lot_Size); lot_size = MathMin(lot_size, Max_Lot_Size); lot_size = MathMin(lot_size, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX)); // Respect symbol max volume // Round to symbol's lot step double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lot_size = NormalizeDouble(MathRound(lot_size / lot_step) * lot_step, 2); // Log position size calculation with detailed information if(DebugMode) { Print("Position size calculation details:"); Print("- Original risk: ", DoubleToString(Risk_Per_Trade_Percent, 2), "%"); Print("- Adjusted risk: ", DoubleToString(adjusted_risk_percent, 2), "%"); Print("- Account equity: ", DoubleToString(account_equity, 2)); Print("- Risk in currency: ", DoubleToString(risk_in_currency, 2)); Print("- SL pips: ", DoubleToString(sl_pips, 1)); Print("- Pip value: ", DoubleToString(pip_value, 4)); Print("- Volatility ratio: ", DoubleToString(volatility_ratio, 2)); Print("- Signal strength: ", DoubleToString(signal_strength, 2)); Print("- Drawdown: ", DoubleToString(drawdown_percent, 2), "%"); Print("- Margin usage: ", DoubleToString(margin_usage_percent, 2), "%"); Print("- Final lot size: ", DoubleToString(lot_size, 2), " lots"); } return(lot_size); } //+------------------------------------------------------------------+ //| Calculate required margin for a position | //+------------------------------------------------------------------+ double CalculateRequiredMargin(double lot_size) { double margin_rate = SymbolInfoDouble(_Symbol, SYMBOL_MARGIN_RATE); double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); double current_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Standard margin calculation: (lot_size * contract_size * price) * margin_rate double required_margin = (lot_size * contract_size * current_price) * margin_rate; return required_margin; } //+------------------------------------------------------------------+ //| Get maximum account equity (for drawdown calculation) | //+------------------------------------------------------------------+ double GetMaxAccountEquity() { static double max_equity = 0.0; double current_equity = AccountInfoDouble(ACCOUNT_EQUITY); if(current_equity > max_equity) { max_equity = current_equity; } return max_equity; } //+------------------------------------------------------------------+ //| Get average ATR over specified period | //+------------------------------------------------------------------+ double GetAverageATR(int period) { double sum_atr = 0.0; int valid_values = 0; for(int i = 0; i < period; i++) { double atr_value = GetATR(i); if(atr_value > 0) { sum_atr += atr_value; valid_values++; } } return (valid_values > 0) ? sum_atr / valid_values : GetATR(0); } //+------------------------------------------------------------------+ //| Update daily risk tracking | //+------------------------------------------------------------------+ void UpdateDailyRiskTracking() { datetime now = TimeCurrent(); int current_day = TimeDayOfYear(now); int current_year = TimeYear(now); // Reset daily risk if it's a new day if(current_day != last_reset_day || TimeYear(now) != last_reset_day_year) { daily_risk_used = 0.0; last_reset_day = current_day; last_reset_day_year = current_year; if(DebugMode) { Print("Daily risk tracking reset for new day"); } } // Update account information account_balance = AccountInfoDouble(ACCOUNT_BALANCE); account_equity = AccountInfoDouble(ACCOUNT_EQUITY); } //+------------------------------------------------------------------+ //| Check if new trade can be taken based on daily risk limits | //+------------------------------------------------------------------+ bool CanTakeNewTrade(double risk_percent, double sl_pips) { // Calculate potential risk for this trade double potential_risk = account_equity * (risk_percent / 100.0); double max_daily_risk = account_equity * (Max_Daily_Risk_Percent / 100.0); // Check if adding this trade would exceed daily risk limit if(daily_risk_used + potential_risk > max_daily_risk) { if(DebugMode) { Print("Daily risk limit check failed:"); Print("- Daily risk used: ", DoubleToString(daily_risk_used, 2)); Print("- Potential risk: ", DoubleToString(potential_risk, 2)); Print("- Max daily risk: ", DoubleToString(max_daily_risk, 2)); } return false; } // Check if account drawdown is too high double max_equity = GetMaxAccountEquity(); double drawdown_percent = (max_equity > 0) ? ((max_equity - account_equity) / max_equity) * 100.0 : 0.0; if(drawdown_percent >= Max_Drawdown_Percent) { if(DebugMode) { Print("Account drawdown too high: ", DoubleToString(drawdown_percent, 2), "% (Max allowed: ", DoubleToString(Max_Drawdown_Percent, 2), "%)"); } return false; } return true; } //+------------------------------------------------------------------+ //| Update daily risk when trade is closed | //+------------------------------------------------------------------+ void UpdateDailyRiskOnTradeClose(double trade_profit_loss) { // Update daily risk tracking UpdateDailyRiskTracking(); // Only add losses to daily risk tracking if(trade_profit_loss < 0) { daily_risk_used += MathAbs(trade_profit_loss); if(DebugMode) { Print("Updated daily risk used: ", DoubleToString(daily_risk_used, 2), " after trade loss of ", DoubleToString(trade_profit_loss, 2)); } } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Add diagnostic logging static int log_counter = 0; log_counter++; if(log_counter % 100 == 0) // Log every 100 ticks to avoid flooding { Print("EA Status: EnableEA=", EnableEA, ", Timeframe=", EnumToString(Period()), ", Bars=", Bars(_Symbol, PERIOD_M15), ", waiting_for_pullback=", waiting_for_pullback, ", daily_risk_used=", daily_risk_used); } //--- Check if EA is enabled if(!EnableEA) { if(log_counter % 500 == 0) Print("EA is disabled (EnableEA=false)"); return; } //--- Check if the timeframe is M15 (runtime check) if(AllowOnlyM15 && Period() != PERIOD_M15) { if(log_counter % 500 == 0) Print("Wrong timeframe. EA requires M15 but running on ", EnumToString(Period())); return; } //--- Check if there are enough bars for indicators if(Bars(_Symbol, PERIOD_M15) < 200) { if(log_counter % 500 == 0) Print("Not enough bars. Need at least 200 bars, current: ", Bars(_Symbol, PERIOD_M15)); return; } //--- Update account information and check drawdown UpdateAccountInfo(); if(!CheckAccountDrawdown()) return; //--- Check for UT Bot signals CheckUTBotSignals(); //--- If we're waiting for pullback, check if any pullback condition is met if(waiting_for_pullback) { bool is_buy = (current_pullback_direction == BUY); Print("Applying pre-entry filters for ", is_buy ? "BUY" : "SELL"); if(ApplyPreEntryFilters(is_buy)) { Print("Checking pullback conditions"); if(IsPullbackConditionMet(is_buy)) { Print("All conditions met, executing trade"); ExecuteTrade(is_buy); // Reset pullback tracking waiting_for_pullback = false; current_pullback_direction = NONE; prev_ut_buy_signal = 0; prev_ut_sell_signal = 0; } else { Print("No pullback condition met"); } } else { Print("Pre-entry filters not met"); } } //--- Manage open positions (SL/TP/trailing) ManageOpenPositions(); // Apply trailing stops to open positions if(UseTrailingStop) ApplyTrailingStop(); } //+------------------------------------------------------------------+ //| 计算ATR移动平均值 - 用于衡量波动率水平 | //+------------------------------------------------------------------+ double CalculateATR_MovingAverage(const int periods) { double atr_sum = 0.0; int valid_count = 0; for(int i = 1; i <= periods; i++) // 从1开始,避免使用当前未完成的K线 { double atr_value = GetATR(i); if(atr_value > 0) { atr_sum += atr_value; valid_count++; } } return (valid_count > 0) ? (atr_sum / valid_count) : GetATR(1); } //+------------------------------------------------------------------+ //| 计算趋势强度 (0-1) - 用于调整止损策略 | //+------------------------------------------------------------------+ double CalculateTrendStrength() { double trend_strength = 0.5; // 默认中等强度 // 1. EMA50和EMA200的关系作为趋势方向指标 double ema50 = GetEMA50(1); double ema200 = GetEMA200(1); if(ema50 == 0 || ema200 == 0) return trend_strength; // 2. EMA50的斜率作为短期趋势强度指标 double ema50_prev = GetEMA50(3); // 使用3根K线前的值计算斜率 double ema50_slope = (ema50 - ema50_prev) / MathMax(ema50_prev, 1.0); // 百分比变化 // 3. 计算EMA50和EMA200之间的距离比例 double ema_distance_ratio = MathAbs(ema50 - ema200) / ema200; // 4. 使用ADX计算趋势强度(如果可用) double adx = 0.0; if(handle_adx > 0) { double adx_buffer[1]; if(CopyBuffer(handle_adx, 0, 1, 1, adx_buffer) > 0) adx = adx_buffer[0]; } // 综合计算趋势强度 (0-1) if(adx > 0) // 如果有ADX值,给予较高权重 { // ADX通常在0-100之间,30以上表示强趋势 double adx_strength = MathMin(1.0, adx / 50.0); trend_strength = adx_strength * 0.6 + MathMin(1.0, ema_distance_ratio * 100.0) * 0.3 + MathMin(1.0, MathAbs(ema50_slope) * 1000.0) * 0.1; } else // 使用EMA关系和斜率 { // 计算EMA距离强度 (0-1) double distance_strength = MathMin(1.0, ema_distance_ratio * 200.0); // 计算EMA斜率强度 (0-1) double slope_strength = MathMin(1.0, MathAbs(ema50_slope) * 2000.0); trend_strength = (distance_strength + slope_strength) / 2.0; } return MathMax(0.0, MathMin(1.0, trend_strength)); // 确保在0-1范围内 } //+------------------------------------------------------------------+ //| Calculate ATR-based stop loss price | //+------------------------------------------------------------------+ double CalculateStopLossPrice(const bool is_buy, const double entry_price) { double atr_value = GetATR(1); if(atr_value == 0) return(0); double sl_price = 0; double sl_size_usd = 0; // 获取交易平台最小止损距离要求 int stoplevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); double min_stop_distance = stoplevel * _Point; // 计算更全面的波动率指标 double atr_avg_5 = CalculateATR_MovingAverage(5); double atr_avg_20 = CalculateATR_MovingAverage(20); double atr_avg_50 = CalculateATR_MovingAverage(50); // 多级波动率比率 double volatility_ratio_short = atr_value / atr_avg_5; // 短期波动率变化 double volatility_ratio_medium = atr_value / atr_avg_20; // 中期波动率变化 double volatility_ratio_long = atr_avg_20 / atr_avg_50; // 长期波动率变化趋势 // 获取当前市场趋势强度 double trend_strength = CalculateTrendStrength(); // 基础ATR乘数,根据交易策略优化 double base_multiplier = SL_Multiplier; // 1. 基于趋势强度的自适应调整 // 强趋势市场中,价格回撤通常较小 if(trend_strength > 0.75) // 极强趋势 base_multiplier *= 0.85; else if(trend_strength > 0.6) // 强趋势 base_multiplier *= 0.9; // 弱趋势或震荡市场需要更大的止损空间 else if(trend_strength < 0.25) // 极弱趋势/震荡 base_multiplier *= 1.3; else if(trend_strength < 0.4) // 弱趋势 base_multiplier *= 1.15; // 2. 基于波动率分层的自适应调整 // 短期波动率突增时需要更大的止损空间 if(volatility_ratio_short > 1.8) base_multiplier *= 1.2; else if(volatility_ratio_short > 1.4) base_multiplier *= 1.1; else if(volatility_ratio_short < 0.6) base_multiplier *= 0.9; // 中期波动率环境调整 if(volatility_ratio_medium > 1.6) base_multiplier *= 1.15; else if(volatility_ratio_medium > 1.3) base_multiplier *= 1.05; else if(volatility_ratio_medium < 0.8) base_multiplier *= 0.95; switch(SL_Type) { case 0: // Fixed ATR with Enhanced Volatility Adaptation { // 使用多级波动率比率替代单一volatility_ratio if(volatility_ratio_medium > 1.5) // 高波动期 base_multiplier *= 1.1; else if(volatility_ratio_medium < 0.7) // 低波动期 base_multiplier *= 0.9; sl_size_usd = atr_value * base_multiplier; break; } case 1: // Dynamic ATR with Market Regime Detection { // 市场状态检测:趋势vs震荡 bool is_trending_market = trend_strength > 0.5; // 动态止损调整策略 double dynamic_factor = 1.0; if(is_trending_market) { // 趋势市场:基于趋势强度和波动率变化调整 dynamic_factor = 0.9 + (0.2 * trend_strength); if(volatility_ratio_short > 1.2) dynamic_factor *= 1.1; } else { // 震荡市场:需要更大的止损空间 dynamic_factor = 1.2 + (0.3 * (1 - trend_strength)); if(volatility_ratio_medium > 1.3) dynamic_factor *= 1.15; } sl_size_usd = base_multiplier * dynamic_factor * atr_value; // 应用最大止损限制 double max_sl_size = 4.0 * atr_avg_20; // 最大不超过20周期平均ATR的4倍 if(sl_size_usd > max_sl_size) sl_size_usd = max_sl_size; break; } case 2: // Enhanced Swing Low/High with Validation { // 改进的摆动点检测,使用更高低点确认 int swing_lookback = 20; // 回看20个K线 double swing_point = 0; if(is_buy) { // 为买入找到最近的显著低点,并确保有足够的确认 double lowest_low = iLow(_Symbol, PERIOD_M15, 0); int lowest_low_index = 0; for(int i = 1; i < swing_lookback; i++) { double current_low = iLow(_Symbol, PERIOD_M15, i); if(current_low < lowest_low) { lowest_low = current_low; lowest_low_index = i; } } // 验证低点是否有效(两边至少有1根K线确认) if(lowest_low_index > 0 && lowest_low_index < swing_lookback - 1) { bool valid_low = true; for(int i = 1; i <= 2 && valid_low; i++) { if(iLow(_Symbol, PERIOD_M15, lowest_low_index - i) < lowest_low || iLow(_Symbol, PERIOD_M15, lowest_low_index + i) < lowest_low) valid_low = false; } if(valid_low) swing_point = lowest_low; else swing_point = lowest_low - (atr_value * 0.2); // 如果没有有效低点,稍微扩大范围 } else swing_point = lowest_low; // 计算基于摆动低点的止损,添加ATR缓冲并考虑波动率 double buffer_multiplier = (volatility_ratio_medium > 1.3) ? 0.6 : 0.4; sl_size_usd = MathMax((entry_price - swing_point) + (atr_value * buffer_multiplier), atr_value * base_multiplier); } else { // 为卖出找到最近的显著高点,并确保有足够的确认 double highest_high = iHigh(_Symbol, PERIOD_M15, 0); int highest_high_index = 0; for(int i = 1; i < swing_lookback; i++) { double current_high = iHigh(_Symbol, PERIOD_M15, i); if(current_high > highest_high) { highest_high = current_high; highest_high_index = i; } } // 验证高点是否有效 if(highest_high_index > 0 && highest_high_index < swing_lookback - 1) { bool valid_high = true; for(int i = 1; i <= 2 && valid_high; i++) { if(iHigh(_Symbol, PERIOD_M15, highest_high_index - i) > highest_high || iHigh(_Symbol, PERIOD_M15, highest_high_index + i) > highest_high) valid_high = false; } if(valid_high) swing_point = highest_high; else swing_point = highest_high + (atr_value * 0.2); // 如果没有有效高点,稍微扩大范围 } else swing_point = highest_high; // 计算基于摆动高点的止损,添加ATR缓冲并考虑波动率 double buffer_multiplier = (volatility_ratio_medium > 1.3) ? 0.6 : 0.4; sl_size_usd = MathMax((swing_point - entry_price) + (atr_value * buffer_multiplier), atr_value * base_multiplier); } // 摆动交易止损额外验证:确保足够的风险回报比 double potential_tp = CalculateTakeProfitPrice(is_buy, entry_price); double potential_rr = (is_buy ? (potential_tp - entry_price) : (entry_price - potential_tp)) / sl_size_usd; // 如果风险回报比低于1.0,适当调整止损以确保最小风险回报比 if(potential_rr < 1.0) sl_size_usd *= 0.8; // 减小止损以提高风险回报比 break; } default: { sl_size_usd = atr_value * base_multiplier; break; } } // 确保止损满足交易平台的最小止损距离要求 if(sl_size_usd < min_stop_distance) sl_size_usd = min_stop_distance; // 确保止损至少为1个点 if(sl_size_usd < _Point) sl_size_usd = _Point; // 基于波动率设置最大止损限制 double max_stop_limit = atr_avg_20 * 3.0; // 不超过20周期平均ATR的3倍 if(sl_size_usd > max_stop_limit) sl_size_usd = max_stop_limit; // 计算最终止损价格 if(is_buy) sl_price = entry_price - sl_size_usd; else sl_price = entry_price + sl_size_usd; // 四舍五入到交易品种的位数 sl_price = NormalizeDouble(sl_price, _Digits); // Log the SL calculation string sl_type_desc = ""; switch(SL_Type) { case 0: sl_type_desc = "Fixed ATR"; break; case 1: sl_type_desc = "Dynamic ATR"; break; case 2: sl_type_desc = "Swing Low/High"; break; } Print("Stop Loss calculated using ", sl_type_desc, ": ", DoubleToString(sl_price, _Digits), " (", DoubleToString(sl_size_usd / _Point, 1), " pips)"); return(sl_price); } //+------------------------------------------------------------------+ //| Find significant support/resistance levels | //+------------------------------------------------------------------+ double FindNearestSupportResistance(const bool is_buy, const double current_price, const double trend_strength = 0.5) { // Adjust lookback period based on trend strength // Stronger trend = larger lookback period int base_lookback = 50; int lookback = (int)(base_lookback * (1.0 + trend_strength * 0.5)); // 50-75 bars // Track multiple levels for better selection double best_level = 0; double best_distance = 1000000.0; // Very large initial value double second_best_level = 0; double second_best_distance = 1000000.0; // ATR-based volatility adjustment double atr_value = GetATR(1); double volatility_factor = 1.0; // Check multiple levels (highs, lows, opens, closes) for better support/resistance double levels[4]; // Store high, low, open, close if(is_buy) { // Find nearest resistance above current price for(int i = 1; i < lookback; i++) { levels[0] = iHigh(_Symbol, PERIOD_M15, i); levels[1] = iLow(_Symbol, PERIOD_M15, i); levels[2] = iOpen(_Symbol, PERIOD_M15, i); levels[3] = iClose(_Symbol, PERIOD_M15, i); for(int j = 0; j < 4; j++) { double level = levels[j]; if(level > current_price && ValidateSupportResistanceLevel(level, i, is_buy, trend_strength)) { double distance = level - current_price; // Prioritize levels based on price action and trend strength double priority_factor = 1.0; if(j == 0 || j == 1) priority_factor = 1.2; // Prioritize highs/lows // Adjust for trend alignment if(trend_strength > 0.7) priority_factor *= 1.1; // Strengthened trend = more reliable levels double weighted_distance = distance * priority_factor; if(weighted_distance < best_distance * priority_factor) { second_best_level = best_level; second_best_distance = best_distance; best_distance = distance; best_level = level; } else if(weighted_distance < second_best_distance * priority_factor) { second_best_distance = distance; second_best_level = level; } } } } } else { // Find nearest support below current price for(int i = 1; i < lookback; i++) { levels[0] = iHigh(_Symbol, PERIOD_M15, i); levels[1] = iLow(_Symbol, PERIOD_M15, i); levels[2] = iOpen(_Symbol, PERIOD_M15, i); levels[3] = iClose(_Symbol, PERIOD_M15, i); for(int j = 0; j < 4; j++) { double level = levels[j]; if(level < current_price && ValidateSupportResistanceLevel(level, i, is_buy, trend_strength)) { double distance = current_price - level; // Prioritize levels based on price action and trend strength double priority_factor = 1.0; if(j == 0 || j == 1) priority_factor = 1.2; // Prioritize highs/lows // Adjust for trend alignment if(trend_strength > 0.7) priority_factor *= 1.1; // Strengthened trend = more reliable levels double weighted_distance = distance * priority_factor; if(weighted_distance < best_distance * priority_factor) { second_best_level = best_level; second_best_distance = best_distance; best_distance = distance; best_level = level; } else if(weighted_distance < second_best_distance * priority_factor) { second_best_distance = distance; second_best_level = level; } } } } } // Apply trend strength adjustment to the selected level if(best_level > 0) { // In strong trends, aim for slightly further targets double trend_adjustment = 0.0; if(trend_strength > 0.7) trend_adjustment = atr_value * 0.3 * trend_strength; // Additional 0-0.3 ATR in strong trends if(is_buy) best_level += trend_adjustment; else best_level -= trend_adjustment; } // If no level found, return dynamic target based on trend and volatility if(best_level == 0) { double atr_multiplier = 2.0 + (trend_strength * 1.0); // 2.0-3.0 based on trend if(is_buy) best_level = current_price + (atr_value * atr_multiplier); else best_level = current_price - (atr_value * atr_multiplier); } return(best_level); } //+------------------------------------------------------------------+ //| Validate a support/resistance level | //+------------------------------------------------------------------+ bool ValidateSupportResistanceLevel(const double level, const int bar_index, const bool is_buy, const double trend_strength) { // Minimum price touch requirement (based on trend strength) int min_touches = 1; if(trend_strength < 0.5) min_touches = 2; // More confirmation needed in weak trends // ATR-based tolerance for considering a touch double atr_value = GetATR(1); double tolerance = atr_value * 0.15; // 15% of ATR // Count touches in recent history (more significant if touched multiple times) int touches = 0; int confirmation_lookback = MathMin(100, bar_index + 50); // Look back up to 100 bars for(int i = 0; i < confirmation_lookback; i++) { // Check if price has touched this level in the past double high = iHigh(_Symbol, PERIOD_M15, i); double low = iLow(_Symbol, PERIOD_M15, i); if(is_buy && level <= high && level >= low) // Resistance level touched touches++; else if(!is_buy && level <= high && level >= low) // Support level touched touches++; // Early exit if we have enough confirmation if(touches >= min_touches + 1) break; } // Check for clear price rejection bool has_rejection = false; if(bar_index > 0) { double bar_high = iHigh(_Symbol, PERIOD_M15, bar_index); double bar_low = iLow(_Symbol, PERIOD_M15, bar_index); double bar_close = iClose(_Symbol, PERIOD_M15, bar_index); double bar_open = iOpen(_Symbol, PERIOD_M15, bar_index); if(is_buy && bar_high >= level && bar_high - bar_low > atr_value * 0.8) { // Bullish rejection from resistance (indicating stronger level) if(bar_close < bar_open && bar_close > level * 0.999) // Price closed below resistance has_rejection = true; } else if(!is_buy && bar_low <= level && bar_high - bar_low > atr_value * 0.8) { // Bearish rejection from support (indicating stronger level) if(bar_close > bar_open && bar_close < level * 1.001) // Price closed above support has_rejection = true; } } // Time-based validation - more recent levels are more relevant double recency_factor = 1.0 - MathMin(1.0, (double)bar_index / 100.0); // 100 bars = old enough // Volume validation - check if volume was higher at this level long level_volume = iVolume(_Symbol, PERIOD_M15, bar_index); double avg_volume = 0.0; int volume_lookback = MathMin(20, bar_index); for(int i = 0; i < volume_lookback; i++) avg_volume += iVolume(_Symbol, PERIOD_M15, bar_index + i); avg_volume /= volume_lookback; bool high_volume = (level_volume > avg_volume * 1.2); // 20% above average volume // Final validation logic bool valid = false; if(touches >= min_touches) valid = true; else if(has_rejection) // Strong rejection is sufficient even with fewer touches valid = true; else if(high_volume && bar_index < 20) // Recent high volume levels are significant valid = true; // In very strong trends, be more lenient with validation if(trend_strength > 0.85 && touches >= 1) valid = true; return valid; } //+------------------------------------------------------------------+ //| Check for trend reversal signals | //+------------------------------------------------------------------+ bool IsTrendReversal(const bool is_buy) { // Check for trend reversal based on EMAs double ema50 = GetEMA50(0); double ema200 = GetEMA200(0); double ema50_prev = GetEMA50(1); double ema200_prev = GetEMA200(1); if(ema50 == 0 || ema200 == 0 || ema50_prev == 0 || ema200_prev == 0) return(false); // Check for EMA crossover in opposite direction bool ema_cross_reversal = false; if(is_buy) { // For buy, check if EMA50 is crossing below EMA200 ema_cross_reversal = (ema50_prev > ema200_prev && ema50 <= ema200); } else { // For sell, check if EMA50 is crossing above EMA200 ema_cross_reversal = (ema50_prev < ema200_prev && ema50 >= ema200); } // Check MACD reversal double macd_main[1], macd_signal[1], macd_histogram[1]; double macd_main_prev[1], macd_signal_prev[1], macd_histogram_prev[1]; if(GetMACDValues(macd_main, macd_signal, macd_histogram, 0) && GetMACDValues(macd_main_prev, macd_signal_prev, macd_histogram_prev, 1)) { bool macd_reversal = false; if(is_buy) { // For buy, check if MACD histogram is decreasing after peak if(macd_histogram_prev[0] > 0 && macd_histogram[0] < macd_histogram_prev[0]) macd_reversal = true; } else { // For sell, check if MACD histogram is increasing after trough if(macd_histogram_prev[0] < 0 && macd_histogram[0] > macd_histogram_prev[0]) macd_reversal = true; } return(ema_cross_reversal || macd_reversal); } return(ema_cross_reversal); } //+------------------------------------------------------------------+ //| Enhanced Calculate take profit price with multiple strategies | //+------------------------------------------------------------------+ double CalculateTakeProfitPrice(const bool is_buy, const double entry_price, const double sl_size_usd) { double tp_price = 0; double atr_value = GetATR(0); double trend_strength = CalculateTrendStrength(); double volatility_ratio = 0.0; // Calculate volatility ratio for dynamic adjustment if(avg_atr_20 > 0) volatility_ratio = atr_value / avg_atr_20; else volatility_ratio = 1.0; // Advanced Dynamic risk reward ratio based on market conditions double dynamic_rr = RiskRewardRatio; // 1. Adjust RR based on trend strength with more granular levels if(trend_strength > 0.85) // Very strong trend dynamic_rr *= 1.4; // Significantly increase target else if(trend_strength > 0.7) // Strong trend dynamic_rr *= 1.2; // Increase target else if(trend_strength > 0.5) // Moderate trend dynamic_rr *= 1.0; // Neutral else if(trend_strength > 0.3) // Weak trend dynamic_rr *= 0.9; // Slightly decrease target else // Very weak trend dynamic_rr *= 0.7; // Significantly decrease target // 2. Adjust RR based on volatility with adaptive scaling if(volatility_ratio > 2.0) // Extremely high volatility dynamic_rr *= 0.7; // Reduce target more aggressively else if(volatility_ratio > 1.5) // High volatility dynamic_rr *= 0.85; // Reduce target else if(volatility_ratio < 0.5) // Very low volatility dynamic_rr *= 1.2; // Increase target more aggressively else if(volatility_ratio < 0.7) // Low volatility dynamic_rr *= 1.1; // Increase target // 3. Adjust RR based on account drawdown (risk management) double current_drawdown = CalculateDrawdown(); if(current_drawdown > Max_Drawdown_Percent * 0.8) // Near max drawdown dynamic_rr *= 0.9; // Be more conservative // 4. Adjust RR based on daily risk usage if(daily_risk_used > Max_Daily_Risk_Percent * 0.7) // Used significant daily risk dynamic_rr *= 0.95; // Slightly more conservative switch(TP_Type) { case 0: // Fixed Risk/Reward with enhanced dynamic adjustment { double tp_size_usd = sl_size_usd * dynamic_rr; if(is_buy) tp_price = entry_price + tp_size_usd; else tp_price = entry_price - tp_size_usd; if(DebugMode) Print("[DEBUG] Take Profit calculated (Enhanced Dynamic RR): ", DoubleToString(tp_price, _Digits), " (", DoubleToString(tp_size_usd / _Point, 1), " pips, Base R:R ", DoubleToString(RiskRewardRatio, 2), ":1, ", "Dynamic R:R ", DoubleToString(dynamic_rr, 2), ":1, Trend Strength ", DoubleToString(trend_strength, 2), ", Volatility Ratio ", DoubleToString(volatility_ratio, 2), ", Drawdown ", DoubleToString(current_drawdown, 2), "%"); break; } case 1: // Advanced Multi-Target with enhanced partial profit taking { // Calculate dynamic targets based on market conditions double tp1_rr = 1.0 + (trend_strength * 0.5); // 1.0-1.5:1 based on trend double tp2_rr = tp1_rr * TP2_RR_Multiplier; double tp3_rr = dynamic_rr; // TP1: 40% profit at adjusted 1.0-1.5:1 RR double tp1_size_usd = sl_size_usd * tp1_rr; double tp1_price = is_buy ? (entry_price + tp1_size_usd) : (entry_price - tp1_size_usd); // TP2: 30% profit at adjusted 1.5-2.25:1 RR double tp2_size_usd = sl_size_usd * tp2_rr; double tp2_price = is_buy ? (entry_price + tp2_size_usd) : (entry_price - tp2_size_usd); // TP3: 30% profit at fully dynamic RR double tp3_size_usd = sl_size_usd * tp3_rr; double tp3_price = is_buy ? (entry_price + tp3_size_usd) : (entry_price - tp3_size_usd); // Store targets in global variables for partial close tracking global_tp1_price = tp1_price; global_tp2_price = tp2_price; global_tp3_price = tp3_price; // Return TP1 as the initial take profit tp_price = tp1_price; if(DebugMode) { Print("[DEBUG] Advanced Multi-Target Strategy - TP1 (", DoubleToString(tp1_price, _Digits), ", ", DoubleToString(tp1_rr, 2), ":1 RR, 40% of position)"); Print("[DEBUG] Advanced Multi-Target Strategy - TP2 (", DoubleToString(tp2_price, _Digits), ", ", DoubleToString(tp2_rr, 2), ":1 RR, 30% of position)"); Print("[DEBUG] Advanced Multi-Target Strategy - TP3 (", DoubleToString(tp3_price, _Digits), ", ", DoubleToString(tp3_rr, 2), ":1 RR, 30% of position)"); } break; } case 2: // Enhanced Support/Resistance with trend alignment and validation { // Use enhanced support/resistance detection with trend alignment tp_price = FindNearestSupportResistance(is_buy, entry_price, trend_strength); // Validate support/resistance level with additional checks bool level_valid = ValidateSupportResistanceLevel(tp_price, is_buy, trend_strength); // Check if the found level is valid, too close or too far double distance = MathAbs(tp_price - entry_price); double min_distance = sl_size_usd * 0.8; // At least 80% of SL distance double max_distance = sl_size_usd * 6.0; // Maximum 6x SL distance if(!level_valid || distance < min_distance || distance > max_distance) { if(DebugMode) Print("[DEBUG] Support/Resistance level invalid or extreme, using Dynamic ATR-based fallback"); // Advanced fallback to volatility-adapted ATR target double atr_multiplier = ATR_Profit_Multiplier * (0.8 + trend_strength * 0.4); double atr_based_tp = is_buy ? (entry_price + atr_value * atr_multiplier) : (entry_price - atr_value * atr_multiplier); tp_price = atr_based_tp; } // Calculate distance in pips and implied RR distance = is_buy ? (tp_price - entry_price) : (entry_price - tp_price); double implied_rr = distance / sl_size_usd; // Ensure implied RR is within reasonable bounds if(implied_rr < 1.0 && TP_Type == 2) { if(DebugMode) Print("[DEBUG] Implied RR too low, adjusting to minimum 1.0:1"); double min_tp_size_usd = sl_size_usd * 1.0; tp_price = is_buy ? (entry_price + min_tp_size_usd) : (entry_price - min_tp_size_usd); implied_rr = 1.0; } if(DebugMode) Print("[DEBUG] Take Profit calculated (Enhanced S/R): ", DoubleToString(tp_price, _Digits), " (", DoubleToString(distance / _Point, 1), " pips, Implied R:R ", DoubleToString(implied_rr, 2), ":1)"); break; } case 3: // Advanced ATR-based with market regime adaptation { // Calculate market regime factor (combines trend and volatility) double market_regime_factor = CalculateMarketRegimeFactor(trend_strength, volatility_ratio); // Base ATR multiplier with comprehensive adaptation double atr_multiplier = ATR_Profit_Multiplier; // Adjust multiplier based on market regime atr_multiplier *= market_regime_factor; // Fine-tune based on current volatility if(volatility_ratio > 1.8) // Very high volatility atr_multiplier *= 0.8; else if(volatility_ratio < 0.6) // Very low volatility atr_multiplier *= 1.25; // Ensure multiplier stays within reasonable bounds atr_multiplier = MathMax(0.5, MathMin(4.0, atr_multiplier)); double tp_distance = atr_value * atr_multiplier; tp_price = is_buy ? (entry_price + tp_distance) : (entry_price - tp_distance); double implied_rr = tp_distance / sl_size_usd; if(DebugMode) Print("[DEBUG] Take Profit calculated (Market Regime-Adapted ATR): ", DoubleToString(tp_price, _Digits), " (", DoubleToString(tp_distance / _Point, 1), " pips, ATR Multiplier ", DoubleToString(atr_multiplier, 2), ", ", "Market Regime Factor ", DoubleToString(market_regime_factor, 2), ", Implied R:R ", DoubleToString(implied_rr, 2), ":1)"); break; } case 4: // Dynamic Volatility-Adjusted with Profit Maximization { // Base target is standard RR double base_tp_size_usd = sl_size_usd * dynamic_rr; // Calculate profit maximization factor based on trend persistence double trend_persistence = CalculateTrendPersistence(); double profit_max_factor = 1.0 + (trend_persistence * 0.5); // Final target with profit maximization double final_tp_size_usd = base_tp_size_usd * profit_max_factor; tp_price = is_buy ? (entry_price + final_tp_size_usd) : (entry_price - final_tp_size_usd); double implied_rr = final_tp_size_usd / sl_size_usd; if(DebugMode) Print("[DEBUG] Take Profit calculated (Profit Maximization): ", DoubleToString(tp_price, _Digits), " (", DoubleToString(final_tp_size_usd / _Point, 1), " pips, Base R:R ", DoubleToString(dynamic_rr, 2), ":1, ", "Profit Max Factor ", DoubleToString(profit_max_factor, 2), ", Final R:R ", DoubleToString(implied_rr, 2), ":1)"); break; } default: // Default to Enhanced Fixed RR { double tp_size_usd = sl_size_usd * dynamic_rr; if(is_buy) tp_price = entry_price + tp_size_usd; else tp_price = entry_price - tp_size_usd; } } // Ensure TP is at least minimum distance from entry (enforce minimum RR) double min_tp_distance = sl_size_usd * 0.8; // Increased to 0.8:1 minimum RR double calculated_distance = MathAbs(tp_price - entry_price); if(calculated_distance < min_tp_distance) { if(DebugMode) Print("[DEBUG] Adjusting TP: Too close to entry, using minimum RR of 0.8:1"); if(is_buy) tp_price = entry_price + min_tp_distance; else tp_price = entry_price - min_tp_distance; } // Round TP price to symbol's digits tp_price = NormalizeDouble(tp_price, _Digits); return(tp_price); } //+------------------------------------------------------------------+ //| 计算市场状态因子 - 用于止盈策略调整 | //+------------------------------------------------------------------+ double CalculateMarketRegimeFactor(const double trend_strength, const double volatility_ratio) { // Base factor starts at 1.0 double factor = 1.0; // Adjust based on trend-volatility combination (market regime) if(trend_strength > 0.7 && volatility_ratio < 1.2) // Trending with normal volatility factor = 1.3; // Strong trend environment - extend targets else if(trend_strength > 0.7 && volatility_ratio > 1.5) // Trending with high volatility factor = 1.1; // High volatility trend - moderately extend else if(trend_strength < 0.4 && volatility_ratio < 0.8) // Low trend, low volatility factor = 0.9; // Ranging market - reduce targets else if(trend_strength < 0.4 && volatility_ratio > 1.5) // Low trend, high volatility factor = 0.8; // Chaotic market - significantly reduce targets return factor; } //+------------------------------------------------------------------+ //| 计算趋势持续性 - 用于利润最大化策略 | //+------------------------------------------------------------------+ double CalculateTrendPersistence() { // Default persistence if indicators unavailable double persistence = 0.5; // Calculate based on EMA50 slope consistency double ema50_slope_current = (GetEMA50(1) - GetEMA50(3)) / MathMax(GetEMA50(3), 1.0); double ema50_slope_prev = (GetEMA50(3) - GetEMA50(6)) / MathMax(GetEMA50(6), 1.0); // Check if slopes are in same direction and consistent if(ema50_slope_current != 0 && ema50_slope_prev != 0) { if((ema50_slope_current > 0 && ema50_slope_prev > 0) || (ema50_slope_current < 0 && ema50_slope_prev < 0)) { // Calculate persistence based on slope magnitude and consistency double slope_ratio = MathMin(1.0, MathAbs(ema50_slope_current / ema50_slope_prev)); persistence = 0.5 + (slope_ratio * 0.5); } } return MathMax(0.1, MathMin(1.0, persistence)); } //+------------------------------------------------------------------+ //| 验证支撑/阻力位 - 确保其有效性和可靠性 | //+------------------------------------------------------------------+ bool ValidateSupportResistanceLevel(const double level, const bool is_buy, const double trend_strength) { // Basic validation - level must be significantly different from current price double current_price = is_buy ? Ask : Bid; double distance = MathAbs(level - current_price); double min_significant_distance = GetATR(0) * 0.5; // At least 0.5 ATR away if(distance < min_significant_distance) return false; // Check if level aligns with trend direction if(trend_strength > 0.6) // Strong trend { if(is_buy && level <= current_price) // Buy trade but resistance below current price return false; if(!is_buy && level >= current_price) // Sell trade but support above current price return false; } return true; } //+------------------------------------------------------------------+ //| Execute trade based on signals and filters | //+------------------------------------------------------------------+ void ExecuteTrade(const bool is_buy) { // Get current price double ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double entry_price = is_buy ? ask_price : bid_price; // Calculate SL double sl_price = CalculateStopLossPrice(is_buy, entry_price); if(sl_price == 0) { Print("Error: Could not calculate stop loss price"); return; } // Calculate SL size in USD and pips double sl_size_usd = is_buy ? (entry_price - sl_price) : (sl_price - entry_price); double sl_pips = sl_size_usd / _Point; // Check daily risk limit before opening new trade if(!CanTakeNewTrade(Risk_Per_Trade_Percent)) return; // Calculate position size with signal direction for dynamic risk adjustment double lot_size = CalculatePositionSize(sl_pips, is_buy); if(lot_size <= 0) { Print("Error: Invalid position size calculated"); return; } // Calculate TP double tp_price = CalculateTakeProfitPrice(is_buy, entry_price, sl_size_usd); // Execute trade string trade_type = is_buy ? "Buy" : "Sell"; Print("Executing ", trade_type, " trade at ", DoubleToString(entry_price, _Digits), ", SL: ", DoubleToString(sl_price, _Digits), ", TP: ", DoubleToString(tp_price, _Digits), ", Lot size: ", DoubleToString(lot_size, 2)); // Execute the trade MqlTradeResult result; if(is_buy) { if(!trade.Buy(lot_size, _Symbol, 0, sl_price, tp_price, trade_type + " Entry")) { Print("Error executing Buy trade: " + IntegerToString(GetLastError())); return; } } else { if(!trade.Sell(lot_size, _Symbol, 0, sl_price, tp_price, trade_type + " Entry")) { Print("Error executing Sell trade: " + IntegerToString(GetLastError())); return; } } // Update daily risk used daily_risk_used += Risk_Per_Trade_Percent; Print("Daily risk updated:", DoubleToString(daily_risk_used, 2), "% of account"); } //+------------------------------------------------------------------+ //| Get EMA50 value | //+------------------------------------------------------------------+ double GetEMA50(const int shift = 0) { double ema50_value[1]; if(CopyBuffer(handle_ema50, 0, shift, 1, ema50_value) <= 0) return(0.0); return(ema50_value[0]); } //+------------------------------------------------------------------+ //| Get EMA200 value | //+------------------------------------------------------------------+ double GetEMA200(const int shift = 0) { double ema200_value[1]; if(CopyBuffer(handle_ema200, 0, shift, 1, ema200_value) <= 0) return(0.0); return(ema200_value[0]); } //+------------------------------------------------------------------+ //| Get MACD values | //+------------------------------------------------------------------+ bool GetMACDValues(double &main[], double &signal[], double &histogram[], const int shift = 0) { if(CopyBuffer(handle_macd, 0, shift, 1, histogram) <= 0) return(false); if(CopyBuffer(handle_macd, 1, shift, 1, signal) <= 0) return(false); if(CopyBuffer(handle_macd, 2, shift, 1, main) <= 0) return(false); return(true); } //+------------------------------------------------------------------+ //| Get Volume SMA value | //+------------------------------------------------------------------+ double GetVolumeMA(const int shift = 0) { double volume_ma_value[1]; if(CopyBuffer(handle_volume_ma, 0, shift, 1, volume_ma_value) <= 0) return(0.0); return(volume_ma_value[0]); } //+------------------------------------------------------------------+ //| Get ATR value | //+------------------------------------------------------------------+ double GetATR(const int shift = 0) { double atr_value[1]; if(CopyBuffer(handle_atr, 0, shift, 1, atr_value) <= 0) return(0.0); return(atr_value[0]); } //+------------------------------------------------------------------+ //| Get current tick volume | //+------------------------------------------------------------------+ long GetCurrentVolume(const int shift = 0) { return(iVolume(_Symbol, PERIOD_M15, shift)); } //+------------------------------------------------------------------+ //| Check for UT Bot signals | //+------------------------------------------------------------------+ void CheckUTBotSignals() { //--- Add diagnostic logging for UT Bot signals static int bar_counter = 0; //--- Only check for signals on bar close static datetime prev_bar_time = 0; datetime current_bar_time = iTime(_Symbol, PERIOD_M15, 0); if(current_bar_time == prev_bar_time) return; // No new bar yet bar_counter++; if(bar_counter % 10 == 0) // Log every 10 bars Print("Checking UT Bot signals on new bar: ", TimeToString(current_bar_time)); prev_bar_time = current_bar_time; // Reset previous signals for the new bar prev_ut_buy_signal = 0; prev_ut_sell_signal = 0; //--- Get ATR value and filter based on volatility double atr_value = GetATR(1); // Previous bar's ATR if(atr_value == 0) { Print("Warning: ATR value is zero, skipping UT Bot signal check"); return; } // Get average ATR over longer period to filter out noise double avg_atr_20 = 0; for(int i=1; i<=20; i++) avg_atr_20 += GetATR(i); avg_atr_20 /= 20; // Skip signals during extremely low volatility periods if(atr_value < avg_atr_20 * 0.5) { Print("Low volatility environment detected, skipping UT Bot signals"); return; } //--- Calculate ATR-based thresholds with stricter multiplier double atr_threshold = atr_value * (UT_ATR_Multiplier > 0 ? UT_ATR_Multiplier : 1.2); // Ensure minimum multiplier //--- Get price data for signal calculation with additional bars double close_prev = iClose(_Symbol, PERIOD_M15, 1); double close_prev_2 = iClose(_Symbol, PERIOD_M15, 2); double close_prev_3 = iClose(_Symbol, PERIOD_M15, 3); double high_prev = iHigh(_Symbol, PERIOD_M15, 1); double low_prev = iLow(_Symbol, PERIOD_M15, 1); double open_prev = iOpen(_Symbol, PERIOD_M15, 1); // Get EMA values for trend alignment double ema50_prev = GetEMA50(1); double ema50_prev_2 = GetEMA50(2); // Get volume for volume confirmation long volume_prev = GetCurrentVolume(1); double volume_ma_prev = GetVolumeMA(1); //--- UT Bot Buy Signal logic: Enhanced with multiple confirmations bool ut_buy_signal = false; // Main price movement condition - stricter than before bool main_price_condition = (high_prev - close_prev_2) >= (atr_threshold * UT_Sensitivity) && close_prev > close_prev_2; // Trend alignment condition - price should be above EMA50 or EMA50 should be rising bool trend_alignment = (close_prev > ema50_prev) || (ema50_prev > ema50_prev_2); // Volume confirmation - volume should be higher than average bool volume_confirmation = volume_prev > volume_ma_prev * 1.2; // Price pattern confirmation - higher lows pattern bool higher_lows = (low_prev > iLow(_Symbol, PERIOD_M15, 2)) && (iLow(_Symbol, PERIOD_M15, 2) > iLow(_Symbol, PERIOD_M15, 3)); // Close above open confirmation bool close_above_open = close_prev > open_prev; // Signal strength calculation int buy_signal_strength = 0; if(main_price_condition) buy_signal_strength++; if(trend_alignment) buy_signal_strength++; if(volume_confirmation) buy_signal_strength++; if(higher_lows) buy_signal_strength++; if(close_above_open) buy_signal_strength++; // Main price condition is mandatory, and need at least 3 conditions total if(main_price_condition && buy_signal_strength >= 3) { ut_buy_signal = true; Print("UT Bot Buy Signal conditions: Price: ", main_price_condition, ", Trend: ", trend_alignment, ", Volume: ", volume_confirmation, ", Higher Lows: ", higher_lows, ", Close>Open: ", close_above_open, ", Strength: ", buy_signal_strength); } //--- UT Bot Sell Signal logic: Enhanced with multiple confirmations bool ut_sell_signal = false; // Main price movement condition - stricter than before bool sell_price_condition = (close_prev_2 - low_prev) >= (atr_threshold * UT_Sensitivity) && close_prev < close_prev_2; // Trend alignment condition - price should be below EMA50 or EMA50 should be falling bool sell_trend_alignment = (close_prev < ema50_prev) || (ema50_prev < ema50_prev_2); // Volume confirmation - volume should be higher than average bool sell_volume_confirmation = volume_prev > volume_ma_prev * 1.2; // Price pattern confirmation - lower highs pattern bool lower_highs = (high_prev < iHigh(_Symbol, PERIOD_M15, 2)) && (iHigh(_Symbol, PERIOD_M15, 2) < iHigh(_Symbol, PERIOD_M15, 3)); // Close below open confirmation bool close_below_open = close_prev < open_prev; // Signal strength calculation int sell_signal_strength = 0; if(sell_price_condition) sell_signal_strength++; if(sell_trend_alignment) sell_signal_strength++; if(sell_volume_confirmation) sell_signal_strength++; if(lower_highs) sell_signal_strength++; if(close_below_open) sell_signal_strength++; // Main price condition is mandatory, and need at least 3 conditions total if(sell_price_condition && sell_signal_strength >= 3) { ut_sell_signal = true; Print("UT Bot Sell Signal conditions: Price: ", sell_price_condition, ", Trend: ", sell_trend_alignment, ", Volume: ", sell_volume_confirmation, ", Lower Highs: ", lower_highs, ", Close sell_signal_strength + 1) { // Buy signal is significantly stronger prev_ut_buy_signal = buy_signal_strength; ut_signal_candle_high = high_prev; ut_signal_candle_low = low_prev; ut_signal_candle_midpoint = (high_prev + low_prev) / 2; current_pullback_direction = BUY; waiting_for_pullback = true; Print("UT Bot Signal Conflict: Buy signal (Strength: ", buy_signal_strength, ") stronger than Sell (Strength: ", sell_signal_strength, ")"); } else if(sell_signal_strength > buy_signal_strength + 1) { // Sell signal is significantly stronger prev_ut_sell_signal = sell_signal_strength; ut_signal_candle_high = high_prev; ut_signal_candle_low = low_prev; ut_signal_candle_midpoint = (high_prev + low_prev) / 2; current_pullback_direction = SELL; waiting_for_pullback = true; Print("UT Bot Signal Conflict: Sell signal (Strength: ", sell_signal_strength, ") stronger than Buy (Strength: ", buy_signal_strength, ")"); } else { // Signals are too close in strength, ignore both Print("UT Bot Signal Conflict: Both signals too close in strength (Buy: ", buy_signal_strength, ", Sell: ", sell_signal_strength, "). Ignoring both."); } } } //+------------------------------------------------------------------+ //| Manage open positions | //+------------------------------------------------------------------+ void ManageOpenPositions() { // Get current prices and market data double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); datetime current_time = TimeCurrent(); // Get indicator values for decision making double atr_value = GetATR(0); double ema50 = GetEMA50(0); double ema200 = GetEMA200(0); // Check all open positions for this EA for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == 20231114) { // Get position details ulong ticket = PositionGetInteger(POSITION_TICKET); int position_type = (int)PositionGetInteger(POSITION_TYPE); double open_price = PositionGetDouble(POSITION_PRICE_OPEN); double current_sl = PositionGetDouble(POSITION_SL); double current_tp = PositionGetDouble(POSITION_TP); double current_profit = PositionGetDouble(POSITION_PROFIT); double position_volume = PositionGetDouble(POSITION_VOLUME); datetime open_time = PositionGetInteger(POSITION_TIME); // Calculate position age in minutes int position_age_minutes = (int)((current_time - open_time) / 60); Print("Managing position #", ticket, ", Type: ", (position_type == POSITION_TYPE_BUY ? "BUY" : "SELL"), ", Age: ", position_age_minutes, " min, Profit: ", DoubleToString(current_profit, 2)); // Check if trend has reversed against the position bool trend_reversal = false; if(position_type == POSITION_TYPE_BUY) { // For buy positions, check if trend reversed to downtrend if(ema50 < ema200 && ema50 < iMA(_Symbol, PERIOD_M15, 50, 0, MODE_EMA, PRICE_CLOSE, 1)) trend_reversal = true; } else { // For sell positions, check if trend reversed to uptrend if(ema50 > ema200 && ema50 > iMA(_Symbol, PERIOD_M15, 50, 0, MODE_EMA, PRICE_CLOSE, 1)) trend_reversal = true; } // Check if we've hit trailing stop conditions bool trailing_stop_hit = false; if(position_type == POSITION_TYPE_BUY) { // For buy positions, check trailing stop if(current_sl > 0 && bid - trailing_stop_distance_buy <= current_sl) trailing_stop_hit = true; } else { // For sell positions, check trailing stop if(current_sl > 0 && ask + trailing_stop_distance_sell >= current_sl) trailing_stop_hit = true; } // Check if we've reached take profit target bool take_profit_hit = false; if(position_type == POSITION_TYPE_BUY) { if(current_tp > 0 && bid >= current_tp) take_profit_hit = true; } else { if(current_tp > 0 && ask <= current_tp) take_profit_hit = true; } // Check if we should exit based on profit target or stop loss bool should_close = false; string close_reason = ""; // 1. Close if trend reversed if(trend_reversal) { should_close = true; close_reason = "Trend reversal detected"; } // 2. Close if trailing stop hit else if(trailing_stop_hit) { should_close = true; close_reason = "Trailing stop hit"; } // 3. Close if take profit hit else if(take_profit_hit) { should_close = true; close_reason = "Take profit hit"; } // 4. Close if position is losing money and has been open too long else if(current_profit < 0 && position_age_minutes > MaxLosingPositionMinutes) { should_close = true; close_reason = "Maximum losing position time exceeded"; } // 5. Close if profit is significant and time-based exit criteria met else if(current_profit > 0 && position_age_minutes > MinProfitPositionMinutes) { // Calculate profit in pips double profit_pips = position_type == POSITION_TYPE_BUY ? (bid - open_price) / point : (open_price - ask) / point; // Close if profit reaches certain multiple of ATR double atr_profit_threshold = atr_value * ProfitATRMultiple; if(profit_pips * point >= atr_profit_threshold) { should_close = true; close_reason = "Profit target reached (" + (string)MathRound(profit_pips) + " pips)"; } } // Close the position if any closing condition is met if(should_close) { Print("Closing position #", ticket, ", Reason: ", close_reason, ", Profit: ", DoubleToString(current_profit, 2)); // Close the position CTrade trade; trade.SetTypeFillingBySymbol(_Symbol); trade.SetDeviation(5); if(position_type == POSITION_TYPE_BUY) { if(trade.PositionClose(ticket)) { Print("Successfully closed BUY position #", ticket); // Reset trailing stop distance trailing_stop_distance_buy = 0; // Update daily risk tracking with trade result UpdateDailyRiskOnTradeClose(current_profit); // Track trade result to update strategy performance TrackTradeResult(position_type, current_profit, open_price); } else Print("Failed to close BUY position #", ticket, ", Error: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription()); } else { if(trade.PositionClose(ticket)) { Print("Successfully closed SELL position #", ticket); // Reset trailing stop distance trailing_stop_distance_sell = 0; // Update daily risk tracking with trade result UpdateDailyRiskOnTradeClose(current_profit); // Track trade result to update strategy performance TrackTradeResult(position_type, current_profit, open_price); } else Print("Failed to close SELL position #", ticket, ", Error: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription()); } } // Update trailing stop if position is profitable else if(current_profit > 0) { // We'll use the ApplyTrailingStop function for this // No need to update it here separately } } } } //+------------------------------------------------------------------+ //| Apply trailing stop to open positions | //+------------------------------------------------------------------+ // Advanced trailing stop function with profit threshold, trend strength adjustment, // intelligent volatility adaptation, partial take profit based on price targets, // and enhanced risk management void ApplyTrailingStop() { // Get market data double atr_value = GetATR(0); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); // Calculate trend strength for adaptive trailing double trend_strength = CalculateTrendStrength(); // Get ATR moving average for volatility context double atr_ma = CalculateATR_MovingAverage(20); double atr_ratio = atr_value / atr_ma; // Define minimum profit threshold (in points) before trailing stop activates double min_profit_threshold = MathMax(atr_value * 0.5, TrailingStopPips * 0.75 * point); // Check all open positions for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == 20231114) { int position_type = (int)PositionGetInteger(POSITION_TYPE); double stop_loss = PositionGetDouble(POSITION_SL); ulong position_ticket = PositionGetInteger(POSITION_TICKET); double open_price = PositionGetDouble(POSITION_PRICE_OPEN); double current_profit = PositionGetDouble(POSITION_PROFIT); double position_volume = PositionGetDouble(POSITION_VOLUME); datetime open_time = PositionGetInteger(POSITION_TIME); // Calculate position age in hours double position_age_hours = (TimeCurrent() - open_time) / (double)PERIOD_H1; // Check if position has already been partially closed bool has_partial_closed_25 = false; bool has_partial_closed_50 = false; bool has_partial_closed_75 = false; // Get position comment to track partial closes string position_comment = PositionGetString(POSITION_COMMENT); if(StringFind(position_comment, "PARTIAL_25") >= 0) has_partial_closed_25 = true; if(StringFind(position_comment, "PARTIAL_50") >= 0) has_partial_closed_50 = true; if(StringFind(position_comment, "PARTIAL_75") >= 0) has_partial_closed_75 = true; // Calculate current position profit in points double profit_points; if(position_type == POSITION_TYPE_BUY) profit_points = (bid - open_price) / point; else profit_points = (open_price - ask) / point; // Only apply trailing stop if position has minimum profit if(profit_points * point < min_profit_threshold) continue; // Skip positions without enough profit // Check for partial take profit opportunities using multi-target strategy if(UsePartialTakeProfit) { // For BUY positions if(position_type == POSITION_TYPE_BUY) { // 40% partial close at first profit target if(bid >= global_tp1_price && !has_partial_closed_25) { double partial_volume = position_volume * 0.4; // 40% of position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_40_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 40% close executed for BUY position #", position_ticket, ", Volume: ", partial_volume, ", at TP1: ", global_tp1_price); } else if(DebugMode) Print("Failed to execute partial 40% close for position #", position_ticket, ", Error: ", GetLastError()); } // 30% partial close at second profit target else if(bid >= global_tp2_price && !has_partial_closed_50) { double partial_volume = position_volume * 0.3; // 30% of original position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_70_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 30% close executed for BUY position #", position_ticket, ", Volume: ", partial_volume, ", at TP2: ", global_tp2_price); } else if(DebugMode) Print("Failed to execute partial 30% close for position #", position_ticket, ", Error: ", GetLastError()); } // 30% partial close at third profit target else if(bid >= global_tp3_price && !has_partial_closed_75) { double partial_volume = position_volume * 0.3; // 30% of original position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_100_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 30% close executed for BUY position #", position_ticket, ", Volume: ", partial_volume, ", at TP3: ", global_tp3_price); } else if(DebugMode) Print("Failed to execute partial 30% close for position #", position_ticket, ", Error: ", GetLastError()); } } // For SELL positions else if(position_type == POSITION_TYPE_SELL) { // 40% partial close at first profit target if(ask <= global_tp1_price && !has_partial_closed_25) { double partial_volume = position_volume * 0.4; // 40% of position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_40_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 40% close executed for SELL position #", position_ticket, ", Volume: ", partial_volume, ", at TP1: ", global_tp1_price); } else if(DebugMode) Print("Failed to execute partial 40% close for position #", position_ticket, ", Error: ", GetLastError()); } // 30% partial close at second profit target else if(ask <= global_tp2_price && !has_partial_closed_50) { double partial_volume = position_volume * 0.3; // 30% of original position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_70_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 30% close executed for SELL position #", position_ticket, ", Volume: ", partial_volume, ", at TP2: ", global_tp2_price); } else if(DebugMode) Print("Failed to execute partial 30% close for position #", position_ticket, ", Error: ", GetLastError()); } // 30% partial close at third profit target else if(ask <= global_tp3_price && !has_partial_closed_75) { double partial_volume = position_volume * 0.3; // 30% of original position if(trade.PositionClosePartial(position_ticket, partial_volume)) { // Update position comment string new_comment = position_comment + (position_comment == "" ? "" : ",") + "PARTIAL_100_CLOSED"; trade.PositionComment(position_ticket, new_comment); if(DebugMode) Print("Partial 30% close executed for SELL position #", position_ticket, ", Volume: ", partial_volume, ", at TP3: ", global_tp3_price); } else if(DebugMode) Print("Failed to execute partial 30% close for position #", position_ticket, ", Error: ", GetLastError()); } } } // Calculate trailing stop distance with enhanced logic double trailing_stop_distance = 0.0; switch(TrailingStopType) { case 0: // Fixed pips trailing_stop_distance = TrailingStopPips * point; // Adjust based on trend strength, volatility, and position age trailing_stop_distance *= (0.8 + 0.4 * trend_strength) * MathMin(1.2, 1.0 + position_age_hours / 24.0 * 0.1); break; case 1: // ATR based trailing_stop_distance = TrailingATRMultiplier * atr_value; // Adjust based on trend strength and position age trailing_stop_distance *= (0.8 + 0.4 * trend_strength) * MathMin(1.2, 1.0 + position_age_hours / 24.0 * 0.1); break; case 2: // Advanced Dynamic { // Multi-factor adjustment based on volatility, trend strength, profit level, and position age double volatility_factor = MathMax(0.6, MathMin(1.5, 1.0 + (1.0 - atr_ratio) * 0.5)); double trend_factor = 0.8 + 0.4 * trend_strength; double profit_factor = MathMin(1.3, 1.0 + (profit_points * point / (atr_value * 2)) * 0.25); double age_factor = MathMin(1.2, 1.0 + position_age_hours / 24.0 * 0.1); trailing_stop_distance = TrailingATRMultiplier * atr_value * volatility_factor * trend_factor * profit_factor * age_factor; // Additional safety margin during high volatility if(atr_ratio > 1.5) trailing_stop_distance *= 1.1; // Add 10% safety margin break; } default: trailing_stop_distance = TrailingStopPips * point; break; } // For BUY positions if(position_type == POSITION_TYPE_BUY) { // Calculate new trailing level double new_trailing_level = bid - trailing_stop_distance; // Update trailing level if price has moved sufficiently if(new_trailing_level > last_trailing_level_buy) { last_trailing_level_buy = new_trailing_level; // Only update SL if new trailing level is above current SL and profit is sufficient if(new_trailing_level > stop_loss && profit_points * point >= min_profit_threshold) { // Round to appropriate digits new_trailing_level = NormalizeDouble(new_trailing_level, digits); // Ensure new SL is at least min_profit_threshold away from entry double min_sl_level = open_price + min_profit_threshold / 2; // Enforce minimum SL level if(new_trailing_level < min_sl_level) new_trailing_level = min_sl_level; // Risk-based adjustment: tighten SL more aggressively when profit is high double profit_ratio = (profit_points * point) / min_profit_threshold; if(profit_ratio > 3.0 && UseAdaptiveTrailingTightening) { // Tighten trailing stop by 5-15% based on profit ratio double tightening_factor = MathMax(0.85, 1.0 - (profit_ratio - 3.0) * 0.025); new_trailing_level = bid - (trailing_stop_distance * tightening_factor); new_trailing_level = NormalizeDouble(new_trailing_level, digits); } // Modify position if(!trade.PositionModify(position_ticket, new_trailing_level, 0.0)) { int error_code = GetLastError(); if(DebugMode) Print("Failed to modify trailing stop for BUY position #" + (string)position_ticket + ", Error: " + (string)error_code); } else { if(DebugMode) Print("Trailing stop updated for BUY position #", (string)position_ticket, ": New SL = ", DoubleToString(new_trailing_level, digits)); } } } } // For SELL positions else if(position_type == POSITION_TYPE_SELL) { // Calculate new trailing level double new_trailing_level = ask + trailing_stop_distance; // Update trailing level if price has moved sufficiently if(new_trailing_level < last_trailing_level_sell) { last_trailing_level_sell = new_trailing_level; // Only update SL if new trailing level is below current SL and profit is sufficient if(new_trailing_level < stop_loss && profit_points * point >= min_profit_threshold) { // Round to appropriate digits new_trailing_level = NormalizeDouble(new_trailing_level, digits); // Ensure new SL is at least min_profit_threshold away from entry double min_sl_level = open_price - min_profit_threshold / 2; // Enforce minimum SL level if(new_trailing_level > min_sl_level) new_trailing_level = min_sl_level; // Risk-based adjustment: tighten SL more aggressively when profit is high double profit_ratio = (profit_points * point) / min_profit_threshold; if(profit_ratio > 3.0 && UseAdaptiveTrailingTightening) { // Tighten trailing stop by 5-15% based on profit ratio double tightening_factor = MathMax(0.85, 1.0 - (profit_ratio - 3.0) * 0.025); new_trailing_level = ask + (trailing_stop_distance * tightening_factor); new_trailing_level = NormalizeDouble(new_trailing_level, digits); } // Modify position if(!trade.PositionModify(position_ticket, new_trailing_level, 0.0)) { int error_code = GetLastError(); if(DebugMode) Print("Failed to modify trailing stop for SELL position #" + (string)position_ticket + ", Error: " + (string)error_code); } else { if(DebugMode) Print("Trailing stop updated for SELL position #", (string)position_ticket, ": New SL = ", DoubleToString(new_trailing_level, digits)); } } } } }} } }