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
}
}