mql5/Experts/Examples/PullbackTradingEA.mq5

3496 lines
147 KiB
MQL5
Raw Permalink Normal View History

2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
//| 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 <Trade/Trade.mqh>
//--- 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)
2025-11-24 06:54:14 +08:00
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
2025-11-23 19:13:51 +08:00
input bool Enable_Trend_Exit = true; // Enable trend reversal exit
2025-11-24 06:54:14 +08:00
//--- 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
2025-11-23 19:13:51 +08:00
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 (%)
2025-11-24 06:54:14 +08:00
//--- 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
2025-11-23 19:13:51 +08:00
//--- Trailing Stop
input bool UseTrailingStop = true; // Enable trailing stop
2025-11-24 06:54:14 +08:00
input int TrailingStopType = 2; // 0=Fixed pips, 1=ATR based, 2=Dynamic (推荐)
2025-11-23 19:13:51 +08:00
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
2025-11-24 06:54:14 +08:00
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
2025-11-23 19:13:51 +08:00
//--- 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
2025-11-24 06:54:14 +08:00
//--- Multi-target TP tracking variables
double global_tp1_price = 0.0;
double global_tp2_price = 0.0;
double global_tp3_price = 0.0;
2025-11-23 19:13:51 +08:00
//--- 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;
2025-11-24 06:54:14 +08:00
int last_reset_day_year = 0;
2025-11-23 19:13:51 +08:00
//--- 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;
2025-11-24 06:54:14 +08:00
//--- 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)
2025-11-23 19:13:51 +08:00
//--- 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()
{
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
//--- 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);
2025-11-24 06:54:14 +08:00
// Get essential values
2025-11-23 19:13:51 +08:00
double ema50 = GetEMA50(0);
if(ema50 == 0)
return(false);
2025-11-24 06:54:14 +08:00
double ema50_prev = GetEMA50(1);
2025-11-23 19:13:51 +08:00
double current_close = iClose(_Symbol, PERIOD_M15, 0);
2025-11-24 06:54:14 +08:00
double prev_close = iClose(_Symbol, PERIOD_M15, 1);
double current_high = iHigh(_Symbol, PERIOD_M15, 0);
double current_low = iLow(_Symbol, PERIOD_M15, 0);
2025-11-23 19:13:51 +08:00
double prev_high = iHigh(_Symbol, PERIOD_M15, 1);
2025-11-24 06:54:14 +08:00
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
2025-11-23 19:13:51 +08:00
if(is_buy_signal)
{
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Trend alignment
bool ema50_sloping_up = ema50 > ema50_prev;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
return(result);
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Trend alignment
bool ema50_sloping_down = ema50 < ema50_prev;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
return(result);
2025-11-23 19:13:51 +08:00
}
}
//+------------------------------------------------------------------+
//| Check for midpoint pullback |
//+------------------------------------------------------------------+
bool IsMidpointPullback(const bool is_buy_signal)
{
if(!Enable_Midpoint_Pullback || ut_signal_candle_midpoint == 0)
return(false);
2025-11-24 06:54:14 +08:00
// Get essential price data
2025-11-23 19:13:51 +08:00
double current_price = iClose(_Symbol, PERIOD_M15, 0);
double prev_price = iClose(_Symbol, PERIOD_M15, 1);
2025-11-24 06:54:14 +08:00
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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Get trend strength
double trend_strength = GetTrendStrength();
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
double distance_current = MathAbs(current_price - ut_signal_candle_midpoint);
double distance_prev = MathAbs(prev_price - ut_signal_candle_midpoint);
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
return(midpoint_condition);
2025-11-23 19:13:51 +08:00
}
//+------------------------------------------------------------------+
2025-11-24 06:54:14 +08:00
//| Check for MACD pullback and re-expansion with volatility and trend adaptation |
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
bool IsMACDPullback(const bool is_buy_signal)
{
if(!Enable_MACD_Pullback)
return(false);
2025-11-24 06:54:14 +08:00
// 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();
2025-11-23 19:13:51 +08:00
double main[1], signal[1], histogram[1];
double main_prev[1], signal_prev[1], histogram_prev[1];
2025-11-24 06:54:14 +08:00
double main_prev_2[1], signal_prev_2[1], histogram_prev_2[1];
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Get more bars of MACD data for better trend analysis
2025-11-23 19:13:51 +08:00
if(!GetMACDValues(main, signal, histogram, 0) ||
2025-11-24 06:54:14 +08:00
!GetMACDValues(main_prev, signal_prev, histogram_prev, 1) ||
!GetMACDValues(main_prev_2, signal_prev_2, histogram_prev_2, 2))
2025-11-23 19:13:51 +08:00
return(false);
2025-11-24 06:54:14 +08:00
// 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);
2025-11-23 19:13:51 +08:00
if(is_buy_signal)
{
2025-11-24 06:54:14 +08:00
// Enhanced buy conditions with trend and volatility adaptation
2025-11-23 19:13:51 +08:00
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];
2025-11-24 06:54:14 +08:00
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);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Return true if required conditions are met
2025-11-23 19:13:51 +08:00
int condition_count = 0;
2025-11-24 06:54:14 +08:00
if(main_above_signal) condition_count += 2; // Weight main signal line position higher
2025-11-23 19:13:51 +08:00
if(histogram_positive) condition_count++;
if(improving_histogram) condition_count++;
if(main_line_improving) condition_count++;
2025-11-24 06:54:14 +08:00
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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
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);
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
// Enhanced sell conditions with trend and volatility adaptation
2025-11-23 19:13:51 +08:00
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];
2025-11-24 06:54:14 +08:00
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];
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Trend alignment - ensure MACD signals align with overall trend
bool trend_alignment = (trend_direction <= 0) ? true : (histogram_building && main_below_signal);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
int condition_count = 0;
2025-11-24 06:54:14 +08:00
if(main_below_signal) condition_count += 2; // Weight main signal line position higher
2025-11-23 19:13:51 +08:00
if(histogram_negative) condition_count++;
if(improving_histogram) condition_count++;
if(main_line_improving) condition_count++;
2025-11-24 06:54:14 +08:00
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);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
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;
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
}
//+------------------------------------------------------------------+
//| 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);
}
2025-11-24 06:54:14 +08:00
// Log which pullback methods are enabled and their current performance
2025-11-23 19:13:51 +08:00
Print("Pullback methods enabled - EMA50: ", Enable_EMA50_Pullback,
2025-11-24 06:54:14 +08:00
"(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), ")");
2025-11-23 19:13:51 +08:00
// 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);
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
if(ema50_pullback) Print("EMA50 pullback condition met");
if(midpoint_pullback) Print("Midpoint pullback condition met");
if(macd_pullback) Print("MACD pullback condition met");
2025-11-24 06:54:14 +08:00
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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
return(result);
2025-11-23 19:13:51 +08:00
}
//+------------------------------------------------------------------+
//| Apply all 6 pre-entry filters |
//+------------------------------------------------------------------+
bool ApplyPreEntryFilters(const bool is_buy_signal)
{
2025-11-24 06:54:14 +08:00
// Initialize filter pass count
int filter_pass_count = 0;
const int total_filters = 6;
// Step 1: Trend filter - strict trend alignment with enhanced conditions
2025-11-23 19:13:51 +08:00
TrendDirection trend = GetTrendDirection();
2025-11-24 06:54:14 +08:00
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");
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
if(is_sideways && !Allow_Trading_In_Sideways_Market)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
Print("Filter 1/6 FAILED: Market is sideways and trading in sideways market is disabled");
return(false);
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
// Strict trend direction check
2025-11-23 19:13:51 +08:00
bool trend_match = (is_buy_signal && trend == TREND_UP) || (!is_buy_signal && trend == TREND_DOWN);
if(!trend_match)
{
if(Allow_Counter_Trend_With_Confirmation)
{
2025-11-24 06:54:14 +08:00
// 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++;
}
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
Print("Filter 1/6 FAILED: Signal direction doesn't match trend and counter-trend trading is disabled");
return(false);
2025-11-23 19:13:51 +08:00
}
}
2025-11-24 06:54:14 +08:00
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);
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
Print("Filter 2/6 PASSED: UT Bot signal has sufficient strength");
filter_pass_count++;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Step 3: MACD momentum confirmation - now strictly enforced with additional checks
Print("Filter 3/6 - MACD confirmation");
2025-11-23 19:13:51 +08:00
if(!IsMACDConfirmed(is_buy_signal))
{
2025-11-24 06:54:14 +08:00
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);
}
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
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");
2025-11-23 19:13:51 +08:00
if(!IsVolumeSufficient())
{
2025-11-24 06:54:14 +08:00
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);
}
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
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");
2025-11-23 19:13:51 +08:00
if(!IsATRVolatilitySufficient())
{
2025-11-24 06:54:14 +08:00
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);
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
Print("Filter 6/6 PASSED: No news events nearby");
filter_pass_count++;
// Final validation: Ensure all filters passed
if(filter_pass_count != total_filters)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
Print("ERROR: Only ", filter_pass_count, " of ", total_filters, " filters passed");
2025-11-23 19:13:51 +08:00
return(false);
}
2025-11-24 06:54:14 +08:00
Print("SUCCESS: All 6 pre-entry filters passed - Strong signal confirmed");
2025-11-23 19:13:51 +08:00
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 |
//+------------------------------------------------------------------+
2025-11-24 06:54:14 +08:00
double CalculatePositionSize(double sl_pips, bool is_buy_signal = false)
2025-11-23 19:13:51 +08:00
{
// Ensure SL is positive
if(sl_pips <= 0)
{
Print("Error: SL pips must be positive");
return(0.0);
}
// Get account information
2025-11-24 06:54:14 +08:00
account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
account_equity = AccountInfoDouble(ACCOUNT_EQUITY);
double account_free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
if(account_balance <= 0 || account_equity <= 0)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
Print("Error: Invalid account balance or equity");
2025-11-23 19:13:51 +08:00
return(0.0);
}
2025-11-24 06:54:14 +08:00
// Calculate current drawdown
double max_equity = GetMaxAccountEquity();
double drawdown_percent = (max_equity > 0) ? ((max_equity - account_equity) / max_equity) * 100.0 : 0.0;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
// 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");
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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");
}
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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");
}
2025-11-23 19:13:51 +08:00
return(lot_size);
}
2025-11-24 06:54:14 +08:00
//+------------------------------------------------------------------+
//| 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));
}
}
}
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
//| 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();
}
2025-11-24 06:54:14 +08:00
//+------------------------------------------------------------------+
//| 计算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范围内
}
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
//| 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;
2025-11-24 06:54:14 +08:00
// 获取交易平台最小止损距离要求
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;
2025-11-23 19:13:51 +08:00
switch(SL_Type)
{
2025-11-24 06:54:14 +08:00
case 0: // Fixed ATR with Enhanced Volatility Adaptation
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 使用多级波动率比率替代单一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;
2025-11-23 19:13:51 +08:00
break;
}
2025-11-24 06:54:14 +08:00
case 1: // Dynamic ATR with Market Regime Detection
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 市场状态检测:趋势vs震荡
bool is_trending_market = trend_strength > 0.5;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 动态止损调整策略
double dynamic_factor = 1.0;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 应用最大止损限制
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;
2025-11-23 19:13:51 +08:00
break;
}
2025-11-24 06:54:14 +08:00
case 2: // Enhanced Swing Low/High with Validation
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 改进的摆动点检测,使用更高低点确认
int swing_lookback = 20; // 回看20个K线
2025-11-23 19:13:51 +08:00
double swing_point = 0;
if(is_buy)
{
2025-11-24 06:54:14 +08:00
// 为买入找到最近的显著低点,并确保有足够的确认
2025-11-23 19:13:51 +08:00
double lowest_low = iLow(_Symbol, PERIOD_M15, 0);
2025-11-24 06:54:14 +08:00
int lowest_low_index = 0;
2025-11-23 19:13:51 +08:00
for(int i = 1; i < swing_lookback; i++)
{
double current_low = iLow(_Symbol, PERIOD_M15, i);
if(current_low < lowest_low)
2025-11-24 06:54:14 +08:00
{
2025-11-23 19:13:51 +08:00
lowest_low = current_low;
2025-11-24 06:54:14 +08:00
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); // 如果没有有效低点,稍微扩大范围
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
else
swing_point = lowest_low;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 计算基于摆动低点的止损,添加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);
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
// 为卖出找到最近的显著高点,并确保有足够的确认
2025-11-23 19:13:51 +08:00
double highest_high = iHigh(_Symbol, PERIOD_M15, 0);
2025-11-24 06:54:14 +08:00
int highest_high_index = 0;
2025-11-23 19:13:51 +08:00
for(int i = 1; i < swing_lookback; i++)
{
double current_high = iHigh(_Symbol, PERIOD_M15, i);
if(current_high > highest_high)
2025-11-24 06:54:14 +08:00
{
2025-11-23 19:13:51 +08:00
highest_high = current_high;
2025-11-24 06:54:14 +08:00
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); // 如果没有有效高点,稍微扩大范围
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
else
swing_point = highest_high;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 计算基于摆动高点的止损,添加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);
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
// 摆动交易止损额外验证:确保足够的风险回报比
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; // 减小止损以提高风险回报比
2025-11-23 19:13:51 +08:00
break;
}
default:
{
2025-11-24 06:54:14 +08:00
sl_size_usd = atr_value * base_multiplier;
2025-11-23 19:13:51 +08:00
break;
}
}
2025-11-24 06:54:14 +08:00
// 确保止损满足交易平台的最小止损距离要求
if(sl_size_usd < min_stop_distance)
sl_size_usd = min_stop_distance;
// 确保止损至少为1个点
2025-11-23 19:13:51 +08:00
if(sl_size_usd < _Point)
sl_size_usd = _Point;
2025-11-24 06:54:14 +08:00
// 基于波动率设置最大止损限制
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;
// 计算最终止损价格
2025-11-23 19:13:51 +08:00
if(is_buy)
sl_price = entry_price - sl_size_usd;
else
sl_price = entry_price + sl_size_usd;
2025-11-24 06:54:14 +08:00
// 四舍五入到交易品种的位数
2025-11-23 19:13:51 +08:00
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 |
//+------------------------------------------------------------------+
2025-11-24 06:54:14 +08:00
double FindNearestSupportResistance(const bool is_buy, const double current_price, const double trend_strength = 0.5)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
double best_level = 0;
double best_distance = 1000000.0; // Very large initial value
2025-11-24 06:54:14 +08:00
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
2025-11-23 19:13:51 +08:00
if(is_buy)
{
// Find nearest resistance above current price
for(int i = 1; i < lookback; i++)
{
2025-11-24 06:54:14 +08:00
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++)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
double level = levels[j];
if(level > current_price && ValidateSupportResistanceLevel(level, i, is_buy, trend_strength))
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
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;
}
}
2025-11-23 19:13:51 +08:00
}
}
}
else
{
// Find nearest support below current price
for(int i = 1; i < lookback; i++)
{
2025-11-24 06:54:14 +08:00
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++)
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
double level = levels[j];
if(level < current_price && ValidateSupportResistanceLevel(level, i, is_buy, trend_strength))
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
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;
}
}
2025-11-23 19:13:51 +08:00
}
}
}
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
if(best_level == 0)
{
2025-11-24 06:54:14 +08:00
double atr_multiplier = 2.0 + (trend_strength * 1.0); // 2.0-3.0 based on trend
2025-11-23 19:13:51 +08:00
if(is_buy)
2025-11-24 06:54:14 +08:00
best_level = current_price + (atr_value * atr_multiplier);
2025-11-23 19:13:51 +08:00
else
2025-11-24 06:54:14 +08:00
best_level = current_price - (atr_value * atr_multiplier);
2025-11-23 19:13:51 +08:00
}
return(best_level);
}
2025-11-24 06:54:14 +08:00
//+------------------------------------------------------------------+
//| 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;
}
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
2025-11-24 06:54:14 +08:00
//| Enhanced Calculate take profit price with multiple strategies |
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
double CalculateTakeProfitPrice(const bool is_buy, const double entry_price, const double sl_size_usd)
{
double tp_price = 0;
2025-11-24 06:54:14 +08:00
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
2025-11-23 19:13:51 +08:00
switch(TP_Type)
{
2025-11-24 06:54:14 +08:00
case 0: // Fixed Risk/Reward with enhanced dynamic adjustment
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
double tp_size_usd = sl_size_usd * dynamic_rr;
2025-11-23 19:13:51 +08:00
if(is_buy)
tp_price = entry_price + tp_size_usd;
else
tp_price = entry_price - tp_size_usd;
2025-11-24 06:54:14 +08:00
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), "%");
2025-11-23 19:13:51 +08:00
break;
}
2025-11-24 06:54:14 +08:00
case 1: // Advanced Multi-Target with enhanced partial profit taking
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 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;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// TP2: 30% profit at adjusted 1.5-2.25:1 RR
double tp2_size_usd = sl_size_usd * tp2_rr;
2025-11-23 19:13:51 +08:00
double tp2_price = is_buy ? (entry_price + tp2_size_usd) : (entry_price - tp2_size_usd);
2025-11-24 06:54:14 +08:00
// 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)");
2025-11-23 19:13:51 +08:00
break;
}
2025-11-24 06:54:14 +08:00
case 4: // Dynamic Volatility-Adjusted with Profit Maximization
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
// 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);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Final target with profit maximization
double final_tp_size_usd = base_tp_size_usd * profit_max_factor;
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
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)");
2025-11-23 19:13:51 +08:00
break;
}
2025-11-24 06:54:14 +08:00
default: // Default to Enhanced Fixed RR
2025-11-23 19:13:51 +08:00
{
2025-11-24 06:54:14 +08:00
double tp_size_usd = sl_size_usd * dynamic_rr;
2025-11-23 19:13:51 +08:00
if(is_buy)
tp_price = entry_price + tp_size_usd;
else
tp_price = entry_price - tp_size_usd;
}
}
2025-11-24 06:54:14 +08:00
// 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;
}
2025-11-23 19:13:51 +08:00
// Round TP price to symbol's digits
tp_price = NormalizeDouble(tp_price, _Digits);
return(tp_price);
}
2025-11-24 06:54:14 +08:00
//+------------------------------------------------------------------+
//| 计算市场状态因子 - 用于止盈策略调整 |
//+------------------------------------------------------------------+
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;
}
2025-11-23 19:13:51 +08:00
//+------------------------------------------------------------------+
//| 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;
2025-11-24 06:54:14 +08:00
// Calculate position size with signal direction for dynamic risk adjustment
double lot_size = CalculatePositionSize(sl_pips, is_buy);
2025-11-23 19:13:51 +08:00
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;
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
double atr_value = GetATR(1); // Previous bar's ATR
if(atr_value == 0)
2025-11-24 06:54:14 +08:00
{
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");
2025-11-23 19:13:51 +08:00
return;
2025-11-24 06:54:14 +08:00
}
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
//--- Calculate ATR-based thresholds with stricter multiplier
double atr_threshold = atr_value * (UT_ATR_Multiplier > 0 ? UT_ATR_Multiplier : 1.2); // Ensure minimum multiplier
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
//--- Get price data for signal calculation with additional bars
2025-11-23 19:13:51 +08:00
double close_prev = iClose(_Symbol, PERIOD_M15, 1);
double close_prev_2 = iClose(_Symbol, PERIOD_M15, 2);
2025-11-24 06:54:14 +08:00
double close_prev_3 = iClose(_Symbol, PERIOD_M15, 3);
2025-11-23 19:13:51 +08:00
double high_prev = iHigh(_Symbol, PERIOD_M15, 1);
double low_prev = iLow(_Symbol, PERIOD_M15, 1);
2025-11-24 06:54:14 +08:00
double open_prev = iOpen(_Symbol, PERIOD_M15, 1);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// Get EMA values for trend alignment
double ema50_prev = GetEMA50(1);
double ema50_prev_2 = GetEMA50(2);
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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
2025-11-23 19:13:51 +08:00
bool ut_buy_signal = false;
2025-11-24 06:54:14 +08:00
// 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)
2025-11-23 19:13:51 +08:00
{
ut_buy_signal = true;
2025-11-24 06:54:14 +08:00
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);
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
//--- UT Bot Sell Signal logic: Enhanced with multiple confirmations
2025-11-23 19:13:51 +08:00
bool ut_sell_signal = false;
2025-11-24 06:54:14 +08:00
// 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)
2025-11-23 19:13:51 +08:00
{
ut_sell_signal = true;
2025-11-24 06:54:14 +08:00
Print("UT Bot Sell Signal conditions: Price: ", sell_price_condition, ", Trend: ", sell_trend_alignment, ", Volume: ", sell_volume_confirmation, ", Lower Highs: ", lower_highs, ", Close<Open: ", close_below_open, ", Strength: ", sell_signal_strength);
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
//--- Check for new signals with conflict resolution
if(ut_buy_signal && !ut_sell_signal)
2025-11-23 19:13:51 +08:00
{
// New UT Bot Buy signal detected
2025-11-24 06:54:14 +08:00
prev_ut_buy_signal = buy_signal_strength; // Store signal strength
2025-11-23 19:13:51 +08:00
// Store signal candle data for pullback calculations
ut_signal_candle_high = high_prev;
ut_signal_candle_low = low_prev;
ut_signal_candle_midpoint = (high_prev + low_prev) / 2;
// Set pullback direction and flag
current_pullback_direction = BUY;
waiting_for_pullback = true;
2025-11-24 06:54:14 +08:00
Print("UT Bot Buy Signal detected at bar close (Strength: ", buy_signal_strength, "). Waiting for pullback.");
2025-11-23 19:13:51 +08:00
}
2025-11-24 06:54:14 +08:00
else if(ut_sell_signal && !ut_buy_signal)
2025-11-23 19:13:51 +08:00
{
// New UT Bot Sell signal detected
2025-11-24 06:54:14 +08:00
prev_ut_sell_signal = sell_signal_strength; // Store signal strength
2025-11-23 19:13:51 +08:00
// Store signal candle data for pullback calculations
ut_signal_candle_high = high_prev;
ut_signal_candle_low = low_prev;
ut_signal_candle_midpoint = (high_prev + low_prev) / 2;
// Set pullback direction and flag
current_pullback_direction = SELL;
waiting_for_pullback = true;
2025-11-24 06:54:14 +08:00
Print("UT Bot Sell Signal detected at bar close (Strength: ", sell_signal_strength, "). Waiting for pullback.");
}
else if(ut_buy_signal && ut_sell_signal)
{
// Conflict: both signals detected. Choose stronger one if significant difference exists
if(buy_signal_strength > 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.");
}
2025-11-23 19:13:51 +08:00
}
}
//+------------------------------------------------------------------+
//| Manage open positions |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
2025-11-24 06:54:14 +08:00
// 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
}
}
}
2025-11-23 19:13:51 +08:00
}
//+------------------------------------------------------------------+
//| Apply trailing stop to open positions |
//+------------------------------------------------------------------+
2025-11-24 06:54:14 +08:00
// Advanced trailing stop function with profit threshold, trend strength adjustment,
// intelligent volatility adaptation, partial take profit based on price targets,
// and enhanced risk management
2025-11-23 19:13:51 +08:00
void ApplyTrailingStop()
{
2025-11-24 06:54:14 +08:00
// Get market data
2025-11-23 19:13:51 +08:00
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);
2025-11-24 06:54:14 +08:00
// Calculate trend strength for adaptive trailing
double trend_strength = CalculateTrendStrength();
2025-11-23 19:13:51 +08:00
2025-11-24 06:54:14 +08:00
// 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);
2025-11-23 19:13:51 +08:00
// 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);
2025-11-24 06:54:14 +08:00
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;
}
2025-11-23 19:13:51 +08:00
// 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;
2025-11-24 06:54:14 +08:00
// 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)
2025-11-23 19:13:51 +08:00
{
// Round to appropriate digits
new_trailing_level = NormalizeDouble(new_trailing_level, digits);
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
// Modify position
if(!trade.PositionModify(position_ticket, new_trailing_level, 0.0))
{
2025-11-24 06:54:14 +08:00
int error_code = GetLastError();
if(DebugMode) Print("Failed to modify trailing stop for BUY position #" + (string)position_ticket + ", Error: " + (string)error_code);
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
if(DebugMode) Print("Trailing stop updated for BUY position #", (string)position_ticket, ": New SL = ", DoubleToString(new_trailing_level, digits));
2025-11-23 19:13:51 +08:00
}
}
}
}
// 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;
2025-11-24 06:54:14 +08:00
// 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)
2025-11-23 19:13:51 +08:00
{
// Round to appropriate digits
new_trailing_level = NormalizeDouble(new_trailing_level, digits);
2025-11-24 06:54:14 +08:00
// 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);
}
2025-11-23 19:13:51 +08:00
// Modify position
if(!trade.PositionModify(position_ticket, new_trailing_level, 0.0))
{
2025-11-24 06:54:14 +08:00
int error_code = GetLastError();
if(DebugMode) Print("Failed to modify trailing stop for SELL position #" + (string)position_ticket + ", Error: " + (string)error_code);
2025-11-23 19:13:51 +08:00
}
else
{
2025-11-24 06:54:14 +08:00
if(DebugMode) Print("Trailing stop updated for SELL position #", (string)position_ticket, ": New SL = ", DoubleToString(new_trailing_level, digits));
2025-11-23 19:13:51 +08:00
}
}
}
}
2025-11-24 06:54:14 +08:00
}}
2025-11-23 19:13:51 +08:00
}
}