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