Last active
August 5, 2025 17:40
-
-
Save Elvmeen/64f018706b25cdc5dab06b8ab2fc0de8 to your computer and use it in GitHub Desktop.
TF-EA (Synthetic Indicies Compatible) Pasted for version control. Issues: Risk mgt and timeframe analysis error, allows tf input but like the first ver., deviate to EMAs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //+------------------------------------------------------------------+ | |
| //| Enhanced_EMA_EA_v3.0_Profitable.mq5 | | |
| //| Improved EMA Strategy with 2:1 RRR & 5% Risk | | |
| //+------------------------------------------------------------------+ | |
| #property strict | |
| #property version "3.00" | |
| //--- Risk percentage enumeration | |
| enum ENUM_RISK_PERCENT | |
| { | |
| RISK_1 = 1, // 1% | |
| RISK_2 = 2, // 2% | |
| RISK_3 = 3, // 3% | |
| RISK_4 = 4, // 4% | |
| RISK_5 = 5, // 5% | |
| RISK_6 = 6, // 6% | |
| RISK_7 = 7, // 7% | |
| RISK_8 = 8, // 8% | |
| RISK_9 = 9, // 9% | |
| RISK_10 = 10, // 10% | |
| RISK_15 = 15, // 15% | |
| RISK_20 = 20 // 20% | |
| }; | |
| //--- Input parameters | |
| input group "=== RISK MANAGEMENT (CRITICAL) ===" | |
| input bool UseInteractiveRisk = true; // Ask for Risk % at startup | |
| input double DefaultRiskPercent = 1.0; // Default Risk Percentage per Trade | |
| input double MaxRiskPercent = 5.0; // Maximum Risk Percentage allowed | |
| input double RiskRewardRatio = 5.0; // Risk:Reward Ratio (5.0 = 1:5) | |
| input double MaxLotSize = 10.0; // Maximum Lot Size | |
| input double MinLotSize = 0.01; // Minimum Lot Size | |
| input group "=== EMA STRATEGY SETTINGS ===" | |
| input int FastEMA_Period = 12; // Fast EMA Period (optimized) | |
| input int SlowEMA_Period = 26; // Slow EMA Period (optimized) | |
| input int TrendEMA_Period = 30; // Trend Filter EMA Period | |
| input ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT; // Trading Timeframe | |
| input bool UseTrendFilter = false; // Use Trend Filter (Recommended: ON) | |
| input group "=== ADVANCED FILTERS ===" | |
| input bool UseADXFilter = true; // Use ADX Trend Strength Filter | |
| input int ADX_Period = 14; // ADX Period | |
| input double MinADX_Level = 25.0; // Minimum ADX Level for trades | |
| input bool UseVolatilityFilter = true; // Use ATR Volatility Filter | |
| input int ATR_Period = 14; // ATR Period for volatility | |
| input double ATR_Multiplier = 1.5; // ATR Multiplier for SL/TP | |
| input group "=== TRADE MANAGEMENT ===" | |
| input bool UseTrailingStop = false; // Enable Trailing Stop | |
| input double TrailingATRMultiple = 2.0; // Trailing Stop ATR Multiple | |
| input bool UseBreakEven = true; // Move SL to breakeven | |
| input double BreakevenATRMultiple= 1.0; // Breakeven trigger (ATR Multiple) | |
| input int MagicNumber = 123456; // Magic Number | |
| input group "=== TIME & SPREAD FILTERS ===" | |
| input bool UseTimeFilter = false; // Use Trading Time Filter | |
| input int StartHour = 8; // Trading Start Hour | |
| input int EndHour = 22; // Trading End Hour | |
| input bool UseSpreadFilter = true; // Filter High Spread | |
| input double MaxSpreadATR = 0.3; // Max Spread as % of ATR | |
| input int Slippage = 30; // Maximum Slippage (Points) | |
| input group "=== DERIV SYNTHETIC INDICES SUPPORT ===" | |
| input bool EnableDerivSupport = true; // Enable Deriv Synthetics Detection | |
| input double DerivTickSensitivity = 1.0; // Tick Movement Sensitivity (1.0 = normal) | |
| input int DerivMinTicks = 3; // Minimum Ticks for Signal Confirmation | |
| input bool DerivVerboseLogging = false; // Enable Detailed Deriv Logging | |
| input bool AutoDetectFillMode = true; // Auto-detect best filling mode | |
| input ENUM_ORDER_TYPE_FILLING PreferredFillMode = ORDER_FILLING_FOK; // Backup filling mode | |
| //--- Global variables | |
| int fast_ma_handle = INVALID_HANDLE; | |
| int slow_ma_handle = INVALID_HANDLE; | |
| int trend_ma_handle = INVALID_HANDLE; | |
| int adx_handle = INVALID_HANDLE; | |
| int atr_handle = INVALID_HANDLE; | |
| datetime last_bar_time = 0; | |
| double current_risk_percent = 1.0; // Default to conservative 1% | |
| bool risk_confirmed = false; | |
| // Deriv-specific variables (only active when needed) | |
| bool is_deriv_synthetic = false; | |
| double deriv_last_price = 0; | |
| int deriv_tick_count = 0; | |
| datetime deriv_last_tick_time = 0; | |
| double deriv_accumulated_change = 0; | |
| ENUM_ORDER_TYPE_FILLING optimal_fill_mode = ORDER_FILLING_FOK; | |
| // Enhanced Trade statistics | |
| struct TradeStats { | |
| int total_trades; | |
| int winning_trades; | |
| int losing_trades; | |
| double total_profit; | |
| double gross_profit; | |
| double gross_loss; | |
| double max_drawdown; | |
| double max_profit; | |
| double balance_high; | |
| datetime last_trade_time; | |
| }; | |
| TradeStats stats; | |
| //+------------------------------------------------------------------+ | |
| //| Expert initialization function | | |
| //+------------------------------------------------------------------+ | |
| int OnInit() | |
| { | |
| Print("=== Enhanced EMA EA v3.0 Starting Initialization ==="); | |
| // Validate input parameters | |
| if (!ValidateInputs()) { | |
| return INIT_PARAMETERS_INCORRECT; | |
| } | |
| // Get risk percentage from user if interactive mode is enabled | |
| if (UseInteractiveRisk && !risk_confirmed) { | |
| string risk_input = DoubleToString(DefaultRiskPercent, 1); | |
| if (InputBox("Risk Management Setup", | |
| StringFormat("Enter your desired RISK PERCENTAGE per trade:\n\n" + | |
| "β’ Recommended: 0.5-1%% for conservative trading\n" + | |
| "β’ Moderate: 1-2%% (balanced risk/reward)\n" + | |
| "β’ Aggressive: 2-3%% (higher risk/reward)\n" + | |
| "β’ Maximum allowed: %.1f%%\n\n" + | |
| "Current balance: %.2f\n" + | |
| "Risk:Reward Ratio: 1:%.1f\n\n" + | |
| "Enter percentage (e.g., 1.0):", | |
| MaxRiskPercent, AccountInfoDouble(ACCOUNT_BALANCE), RiskRewardRatio), | |
| risk_input)) { | |
| current_risk_percent = StringToDouble(risk_input); | |
| // Enhanced validation with proper limits | |
| if (current_risk_percent <= 0) { | |
| PrintFormat("β Invalid risk percentage: %.2f%%. Must be positive. Using default: %.2f%%", | |
| current_risk_percent, DefaultRiskPercent); | |
| current_risk_percent = DefaultRiskPercent; | |
| } | |
| else if (current_risk_percent > MaxRiskPercent) { | |
| PrintFormat("β οΈ Risk percentage %.2f%% exceeds maximum allowed %.2f%%. Using maximum.", | |
| current_risk_percent, MaxRiskPercent); | |
| current_risk_percent = MaxRiskPercent; | |
| } | |
| else if (current_risk_percent > 3.0) { | |
| PrintFormat("β οΈ WARNING: High risk percentage %.2f%% detected. Consider using lower risk.", | |
| current_risk_percent); | |
| } | |
| } else { | |
| current_risk_percent = DefaultRiskPercent; | |
| } | |
| risk_confirmed = true; | |
| PrintFormat("β Risk Management Configured: %.2f%% per trade", current_risk_percent); | |
| PrintFormat("β Expected Risk:Reward = 1:%.1f", RiskRewardRatio); | |
| double balance = AccountInfoDouble(ACCOUNT_BALANCE); | |
| double risk_amount = balance * current_risk_percent / 100.0; | |
| PrintFormat("β Risk Amount per Trade: %.2f (%.2f%% of %.2f)", | |
| risk_amount, current_risk_percent, balance); | |
| PrintFormat("β Maximum allowed risk: %.2f%% (%.2f per trade)", | |
| MaxRiskPercent, balance * MaxRiskPercent / 100.0); | |
| } else if (!UseInteractiveRisk) { | |
| // Use DefaultRiskPercent when interactive mode is disabled | |
| current_risk_percent = DefaultRiskPercent; | |
| if (current_risk_percent > MaxRiskPercent) { | |
| current_risk_percent = MaxRiskPercent; | |
| PrintFormat("β οΈ Default risk %.2f%% exceeds maximum. Using %.2f%%", | |
| DefaultRiskPercent, MaxRiskPercent); | |
| } | |
| PrintFormat("β Using Default Risk: %.2f%% per trade (Interactive mode disabled)", current_risk_percent); | |
| } | |
| // Initialize indicators | |
| if (!InitializeIndicators()) { | |
| return INIT_FAILED; | |
| } | |
| // Detect if we're trading Deriv synthetic instruments | |
| DetectDerivSynthetic(); | |
| // Initialize trade statistics | |
| InitializeStats(); | |
| // Print comprehensive initialization info | |
| PrintInitializationInfo(); | |
| PrintFormat("=== EA Successfully Initialized ==="); | |
| return INIT_SUCCEEDED; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Initialize all indicators | | |
| //+------------------------------------------------------------------+ | |
| bool InitializeIndicators() | |
| { | |
| // Initialize EMA indicators | |
| fast_ma_handle = iMA(_Symbol, TimeFrame, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE); | |
| slow_ma_handle = iMA(_Symbol, TimeFrame, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE); | |
| trend_ma_handle = iMA(_Symbol, TimeFrame, TrendEMA_Period, 0, MODE_EMA, PRICE_CLOSE); | |
| // Initialize ADX indicator | |
| if (UseADXFilter) { | |
| adx_handle = iADX(_Symbol, TimeFrame, ADX_Period); | |
| } | |
| // Initialize ATR indicator | |
| atr_handle = iATR(_Symbol, TimeFrame, ATR_Period); | |
| // Validate all handles | |
| if (fast_ma_handle == INVALID_HANDLE || slow_ma_handle == INVALID_HANDLE || | |
| trend_ma_handle == INVALID_HANDLE || atr_handle == INVALID_HANDLE || | |
| (UseADXFilter && adx_handle == INVALID_HANDLE)) { | |
| Print("ERROR: Failed to create indicator handles"); | |
| PrintFormat("Fast MA: %d, Slow MA: %d, Trend MA: %d, ATR: %d, ADX: %d", | |
| fast_ma_handle, slow_ma_handle, trend_ma_handle, atr_handle, adx_handle); | |
| return false; | |
| } | |
| Print("β All indicators initialized successfully"); | |
| return true; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Detect Optimal Filling Mode (FIXED - Compatible with MQL5) | | |
| //+------------------------------------------------------------------+ | |
| void DetectOptimalFillingMode() | |
| { | |
| if (!AutoDetectFillMode) { | |
| optimal_fill_mode = PreferredFillMode; | |
| PrintFormat("β Using preferred fill mode: %s", EnumToString(PreferredFillMode)); | |
| return; | |
| } | |
| // Check what filling modes are supported by this symbol | |
| int filling_modes = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE); | |
| PrintFormat("π Supported filling modes for %s: %d", _Symbol, filling_modes); | |
| // Priority order: FOK -> IOC -> RETURN (removed AON as it's not available in MQL5) | |
| if ((filling_modes & SYMBOL_FILLING_FOK) != 0) { | |
| optimal_fill_mode = ORDER_FILLING_FOK; | |
| PrintFormat("β Selected: FOK (Fill or Kill) - Best for precise execution"); | |
| } | |
| else if ((filling_modes & SYMBOL_FILLING_IOC) != 0) { | |
| optimal_fill_mode = ORDER_FILLING_IOC; | |
| PrintFormat("β Selected: IOC (Immediate or Cancel) - Good for partial fills"); | |
| } | |
| else { | |
| optimal_fill_mode = ORDER_FILLING_RETURN; | |
| PrintFormat("β Selected: RETURN - Market execution mode (Fallback)"); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced Order Sending with Multiple Fill Mode Attempts (FIXED)| | |
| //+------------------------------------------------------------------+ | |
| bool SendOrderWithFallback(MqlTradeRequest &request, MqlTradeResult &result) | |
| { | |
| // Array of filling modes to try in order (removed AON) | |
| ENUM_ORDER_TYPE_FILLING fill_modes[] = { | |
| optimal_fill_mode, // Start with detected optimal mode | |
| ORDER_FILLING_FOK, // Fill or Kill | |
| ORDER_FILLING_IOC, // Immediate or Cancel | |
| ORDER_FILLING_RETURN // Return (Market execution) | |
| }; | |
| for (int i = 0; i < ArraySize(fill_modes); i++) { | |
| request.type_filling = fill_modes[i]; | |
| if (DerivVerboseLogging) { | |
| PrintFormat("π Trying fill mode: %s", EnumToString(fill_modes[i])); | |
| } | |
| if (OrderSend(request, result)) { | |
| if (result.retcode == TRADE_RETCODE_DONE) { | |
| if (i > 0) { // If we had to fallback | |
| PrintFormat("β Order successful with fallback mode: %s", EnumToString(fill_modes[i])); | |
| optimal_fill_mode = fill_modes[i]; // Remember successful mode | |
| } | |
| return true; | |
| } | |
| } | |
| // Log the error for this attempt | |
| if (DerivVerboseLogging || i == 0) { | |
| PrintFormat("β Fill mode %s failed: %s (Code: %d)", | |
| EnumToString(fill_modes[i]), result.comment, result.retcode); | |
| } | |
| Sleep(50); // Small delay between attempts | |
| } | |
| PrintFormat("β All filling modes failed. Last error: %s (Code: %d)", result.comment, result.retcode); | |
| return false; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced Error Code Handler (NEW - Comprehensive error handling)| | |
| //+------------------------------------------------------------------+ | |
| string GetTradeErrorDescription(int error_code) | |
| { | |
| switch(error_code) { | |
| case 4756: return "Position/Order not found or already closed"; | |
| case 4753: return "Order/Position modify request rejected"; | |
| case 4752: return "Order/Position close request rejected"; | |
| case 4751: return "Invalid order/position ticket"; | |
| case 4750: return "Invalid trade operation request"; | |
| case 4749: return "Trade operation not allowed"; | |
| case 4748: return "Position volume exceeds maximum allowed"; | |
| case 4747: return "Invalid order/position volume"; | |
| case 4746: return "Insufficient margin for operation"; | |
| case 4745: return "Order/Position price is invalid"; | |
| case 4744: return "Stop loss/take profit is invalid"; | |
| case 4743: return "Invalid expiration time"; | |
| case 4742: return "Request is being processed"; | |
| case 4741: return "Request rejected - market closed"; | |
| case 4740: return "Trading disabled for this symbol"; | |
| case 10016: return "Unsupported filling mode"; | |
| case 10015: return "Invalid filling mode"; | |
| case 10014: return "Invalid order type"; | |
| case 10013: return "Invalid price"; | |
| case 10012: return "Invalid volume"; | |
| case 10011: return "Invalid symbol"; | |
| case 10010: return "Request timeout"; | |
| case 10009: return "Request locked"; | |
| case 10008: return "Order already processed"; | |
| case 10007: return "Request frequency limit exceeded"; | |
| case 10006: return "Request rejected"; | |
| case 10004: return "Trade server busy"; | |
| default: return StringFormat("Error code %d", error_code); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced input validation | | |
| //+------------------------------------------------------------------+ | |
| bool ValidateInputs() | |
| { | |
| // EMA validation | |
| if (FastEMA_Period <= 1 || SlowEMA_Period <= 1 || FastEMA_Period >= SlowEMA_Period) { | |
| Print("ERROR: Invalid EMA periods. Fast must be < Slow and both > 1"); | |
| return false; | |
| } | |
| // Risk validation - Fixed to use double comparison | |
| if (DefaultRiskPercent <= 0 || DefaultRiskPercent > MaxRiskPercent || MaxRiskPercent > 20.0) { | |
| PrintFormat("ERROR: Invalid risk parameters - Default: %.2f%%, Max: %.2f%%", | |
| DefaultRiskPercent, MaxRiskPercent); | |
| return false; | |
| } | |
| // Risk-Reward validation | |
| if (RiskRewardRatio < 1.0 || RiskRewardRatio > 10.0) { | |
| Print("ERROR: Risk-Reward ratio should be between 1.0 and 10.0"); | |
| return false; | |
| } | |
| // ATR validation | |
| if (ATR_Multiplier <= 0 || ATR_Period <= 0) { | |
| Print("ERROR: Invalid ATR parameters"); | |
| return false; | |
| } | |
| Print("β All input parameters validated successfully"); | |
| return true; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Expert deinitialization function | | |
| //+------------------------------------------------------------------+ | |
| void OnDeinit(const int reason) | |
| { | |
| // Release indicator handles | |
| IndicatorRelease(fast_ma_handle); | |
| IndicatorRelease(slow_ma_handle); | |
| IndicatorRelease(trend_ma_handle); | |
| IndicatorRelease(adx_handle); | |
| IndicatorRelease(atr_handle); | |
| // Print final statistics | |
| PrintFinalStats(); | |
| PrintFormat("EA Deinitialized. Reason: %s", GetDeinitReasonText(reason)); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Expert tick function | | |
| //+------------------------------------------------------------------+ | |
| void OnTick() | |
| { | |
| // Handle Deriv synthetic instruments with tick-based logic (ONLY when detected) | |
| if (is_deriv_synthetic && EnableDerivSupport) { | |
| HandleDerivTicks(); | |
| } | |
| // Your original code continues unchanged for all instruments | |
| // Check for new bar | |
| if (!IsNewBar()) return; | |
| // Update trailing stops and breakeven | |
| ManageOpenPositions(); | |
| // Apply all filters | |
| if (!PassAllFilters()) return; | |
| // Check for trading signals | |
| CheckTradingSignals(); | |
| // Update statistics | |
| UpdateStats(); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Check if new bar has formed | | |
| //+------------------------------------------------------------------+ | |
| bool IsNewBar() | |
| { | |
| datetime current_bar_time = iTime(_Symbol, TimeFrame, 0); | |
| if (current_bar_time == 0) return false; | |
| if (current_bar_time != last_bar_time) { | |
| last_bar_time = current_bar_time; | |
| return true; | |
| } | |
| return false; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced filter system | | |
| //+------------------------------------------------------------------+ | |
| bool PassAllFilters() | |
| { | |
| // Check minimum bars requirement | |
| int required_bars = MathMax(MathMax(SlowEMA_Period, TrendEMA_Period), | |
| MathMax(ADX_Period, ATR_Period)) + 5; | |
| if (Bars(_Symbol, TimeFrame) < required_bars) { | |
| return false; | |
| } | |
| // Time filter | |
| if (UseTimeFilter) { | |
| MqlDateTime time_now; | |
| TimeToStruct(TimeCurrent(), time_now); | |
| if (time_now.hour < StartHour || time_now.hour >= EndHour) { | |
| return false; | |
| } | |
| } | |
| // Enhanced spread filter using ATR | |
| if (UseSpreadFilter && UseVolatilityFilter) { | |
| double atr_values[1]; | |
| if (CopyBuffer(atr_handle, 0, 1, 1, atr_values) > 0) { | |
| double current_spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point; | |
| double max_allowed_spread = atr_values[0] * MaxSpreadATR; | |
| if (current_spread > max_allowed_spread) { | |
| static datetime last_spread_warning = 0; | |
| if (TimeCurrent() - last_spread_warning > 3600) { | |
| PrintFormat("Spread filter: Current %.5f > Max allowed %.5f (%.1f%% of ATR)", | |
| current_spread, max_allowed_spread, MaxSpreadATR * 100); | |
| last_spread_warning = TimeCurrent(); | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced signal detection with multiple confirmations | | |
| //+------------------------------------------------------------------+ | |
| void CheckTradingSignals() | |
| { | |
| // Get indicator values | |
| double fast_ma[3], slow_ma[3], trend_ma[2]; | |
| double adx_main[2], atr_values[2]; | |
| // Copy buffers | |
| if (CopyBuffer(fast_ma_handle, 0, 1, 3, fast_ma) < 0 || | |
| CopyBuffer(slow_ma_handle, 0, 1, 3, slow_ma) < 0 || | |
| CopyBuffer(trend_ma_handle, 0, 1, 2, trend_ma) < 0 || | |
| CopyBuffer(atr_handle, 0, 1, 2, atr_values) < 0) { | |
| Print("ERROR: Failed to copy indicator buffers"); | |
| return; | |
| } | |
| if (UseADXFilter && CopyBuffer(adx_handle, 0, 1, 2, adx_main) < 0) { | |
| Print("ERROR: Failed to copy ADX buffer"); | |
| return; | |
| } | |
| // Current and previous values | |
| double fast_curr = fast_ma[0], fast_prev = fast_ma[1], fast_prev2 = fast_ma[2]; | |
| double slow_curr = slow_ma[0], slow_prev = slow_ma[1], slow_prev2 = slow_ma[2]; | |
| double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); | |
| double current_atr = atr_values[0]; | |
| // Enhanced crossover detection with confirmation | |
| bool buy_crossover = (fast_prev2 <= slow_prev2 && fast_prev <= slow_prev && fast_curr > slow_curr); | |
| bool sell_crossover = (fast_prev2 >= slow_prev2 && fast_prev >= slow_prev && fast_curr < slow_curr); | |
| // Trend filter | |
| bool buy_trend_ok = true, sell_trend_ok = true; | |
| if (UseTrendFilter) { | |
| buy_trend_ok = (current_price > trend_ma[0]) && (fast_curr > trend_ma[0]); | |
| sell_trend_ok = (current_price < trend_ma[0]) && (fast_curr < trend_ma[0]); | |
| } | |
| // ADX trend strength filter | |
| bool adx_ok = true; | |
| if (UseADXFilter) { | |
| adx_ok = (adx_main[0] > MinADX_Level); | |
| if (!adx_ok) { | |
| static datetime last_adx_msg = 0; | |
| if (TimeCurrent() - last_adx_msg > 1800) { | |
| PrintFormat("ADX Filter: Current ADX %.2f < Required %.2f (weak trend)", | |
| adx_main[0], MinADX_Level); | |
| last_adx_msg = TimeCurrent(); | |
| } | |
| } | |
| } | |
| // Final signal confirmation | |
| bool buy_signal = buy_crossover && buy_trend_ok && adx_ok; | |
| bool sell_signal = sell_crossover && sell_trend_ok && adx_ok; | |
| // Debug output for signals | |
| if (buy_signal || sell_signal) { | |
| PrintFormat("=== SIGNAL DETECTED: %s ===", buy_signal ? "BUY" : "SELL"); | |
| PrintFormat("Fast EMA: %.5f -> %.5f -> %.5f", fast_prev2, fast_prev, fast_curr); | |
| PrintFormat("Slow EMA: %.5f -> %.5f -> %.5f", slow_prev2, slow_prev, slow_curr); | |
| PrintFormat("Trend Filter: %s (Price: %.5f, Trend EMA: %.5f)", | |
| (buy_signal ? buy_trend_ok : sell_trend_ok) ? "PASS" : "FAIL", | |
| current_price, trend_ma[0]); | |
| if (UseADXFilter) PrintFormat("ADX: %.2f (Min: %.2f) %s", | |
| adx_main[0], MinADX_Level, adx_ok ? "PASS" : "FAIL"); | |
| PrintFormat("ATR: %.5f", current_atr); | |
| } | |
| // Execute trades | |
| ulong position_ticket = 0; | |
| ENUM_POSITION_TYPE current_position = GetPositionInfo(position_ticket); | |
| if (buy_signal) { | |
| if (current_position == POSITION_TYPE_SELL) { | |
| ClosePosition(position_ticket); | |
| Sleep(100); | |
| } | |
| if (GetPositionInfo(position_ticket) == -1) { | |
| OpenTrade(ORDER_TYPE_BUY, current_atr); | |
| } | |
| } | |
| else if (sell_signal) { | |
| if (current_position == POSITION_TYPE_BUY) { | |
| ClosePosition(position_ticket); | |
| Sleep(100); | |
| } | |
| if (GetPositionInfo(position_ticket) == -1) { | |
| OpenTrade(ORDER_TYPE_SELL, current_atr); | |
| } | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced lot size calculation with STRICT risk management | | |
| //+------------------------------------------------------------------+ | |
| double CalculateOptimalLotSize(double stop_loss_distance) | |
| { | |
| double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); | |
| double risk_amount = account_balance * current_risk_percent / 100.0; | |
| PrintFormat("π Risk Calculation Debug:"); | |
| PrintFormat(" β’ Account Balance: %.2f", account_balance); | |
| PrintFormat(" β’ Risk Percentage: %.2f%%", current_risk_percent); | |
| PrintFormat(" β’ Risk Amount: %.2f", risk_amount); | |
| PrintFormat(" β’ Stop Loss Distance: %.5f", stop_loss_distance); | |
| // Calculate position size based on risk amount and stop loss distance | |
| double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); | |
| double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); | |
| if (tick_value <= 0 || tick_size <= 0) { | |
| PrintFormat("β Invalid tick values: tick_value=%.8f, tick_size=%.8f", tick_value, tick_size); | |
| return MinLotSize; | |
| } | |
| double money_per_point = tick_value / tick_size; | |
| double lot_size = risk_amount / (stop_loss_distance * money_per_point); | |
| PrintFormat(" β’ Tick Value: %.8f", tick_value); | |
| PrintFormat(" β’ Tick Size: %.8f", tick_size); | |
| PrintFormat(" β’ Money per Point: %.8f", money_per_point); | |
| PrintFormat(" β’ Calculated Lot Size: %.3f", lot_size); | |
| // Apply symbol constraints | |
| double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); | |
| double max_lot = MathMin(MaxLotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX)); | |
| double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); | |
| // Normalize lot size to comply with broker requirements | |
| if (lot_step > 0) { | |
| lot_size = NormalizeDouble(MathRound(lot_size / lot_step) * lot_step, 3); | |
| } | |
| // Apply strict limits | |
| lot_size = MathMax(min_lot, MathMin(max_lot, lot_size)); | |
| // Final safety checks | |
| if (lot_size < MinLotSize) lot_size = MinLotSize; | |
| if (lot_size > MaxLotSize) lot_size = MaxLotSize; | |
| // Verify the actual risk with final lot size | |
| double actual_risk_amount = lot_size * stop_loss_distance * money_per_point; | |
| double actual_risk_percent = (actual_risk_amount / account_balance) * 100.0; | |
| PrintFormat("β Final Risk Validation:"); | |
| PrintFormat(" β’ Final Lot Size: %.3f", lot_size); | |
| PrintFormat(" β’ Actual Risk Amount: %.2f", actual_risk_amount); | |
| PrintFormat(" β’ Actual Risk Percentage: %.2f%%", actual_risk_percent); | |
| PrintFormat(" β’ Target Risk Percentage: %.2f%%", current_risk_percent); | |
| // Warning if actual risk significantly exceeds target | |
| if (actual_risk_percent > current_risk_percent * 1.1) { // 10% tolerance | |
| PrintFormat("β οΈ WARNING: Actual risk %.2f%% exceeds target %.2f%% due to lot size constraints", | |
| actual_risk_percent, current_risk_percent); | |
| } | |
| return lot_size; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Detect Deriv Synthetic Instruments (NEW - Non-disruptive) | | |
| //+------------------------------------------------------------------+ | |
| void DetectDerivSynthetic() | |
| { | |
| string symbol_name = _Symbol; | |
| // Detect Deriv synthetic indices by name patterns | |
| if (StringFind(symbol_name, "Volatility") >= 0 || | |
| StringFind(symbol_name, "VIX") >= 0 || | |
| StringFind(symbol_name, "Crash") >= 0 || | |
| StringFind(symbol_name, "Boom") >= 0 || | |
| StringFind(symbol_name, "Step") >= 0 || | |
| StringFind(symbol_name, "Jump") >= 0 || | |
| StringFind(symbol_name, "R_") >= 0) { // Deriv symbols often start with R_ | |
| is_deriv_synthetic = true; | |
| PrintFormat("π― DERIV SYNTHETIC DETECTED: %s", symbol_name); | |
| PrintFormat("β Enhanced tick-based logic ACTIVATED"); | |
| PrintFormat("β Dynamic symbol properties will be used"); | |
| // Get Deriv-specific symbol properties | |
| double point_value = SymbolInfoDouble(_Symbol, SYMBOL_POINT); | |
| int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); | |
| double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); | |
| double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); | |
| int stops_level = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); | |
| PrintFormat("π Deriv Symbol Properties:"); | |
| PrintFormat(" β’ Point Value: %.8f", point_value); | |
| PrintFormat(" β’ Digits: %d", digits); | |
| PrintFormat(" β’ Min Lot: %.3f", min_lot); | |
| PrintFormat(" β’ Lot Step: %.3f", lot_step); | |
| PrintFormat(" β’ Stops Level: %d points", stops_level); | |
| // Detect optimal filling mode for this symbol | |
| DetectOptimalFillingMode(); | |
| } else { | |
| is_deriv_synthetic = false; | |
| optimal_fill_mode = ORDER_FILLING_FOK; // Default for standard instruments | |
| PrintFormat("π STANDARD INSTRUMENT: %s (Gold/BTC/FX mode)", symbol_name); | |
| PrintFormat("β Original EA logic will be used unchanged"); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Handle Deriv Tick-based Logic (NEW - Only for synthetics) | | |
| //+------------------------------------------------------------------+ | |
| void HandleDerivTicks() | |
| { | |
| double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); | |
| datetime current_time = TimeCurrent(); | |
| // Initialize on first tick | |
| if (deriv_last_price == 0) { | |
| deriv_last_price = current_price; | |
| deriv_last_tick_time = current_time; | |
| return; | |
| } | |
| // Check for actual price movement (tick sensitivity) | |
| double price_change = MathAbs(current_price - deriv_last_price); | |
| double min_change = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * DerivTickSensitivity; | |
| if (price_change >= min_change) { | |
| deriv_tick_count++; | |
| deriv_accumulated_change += price_change; | |
| if (DerivVerboseLogging && deriv_tick_count % 100 == 0) { | |
| PrintFormat("Deriv Ticks: %d | Avg Change: %.5f | Current: %.5f", | |
| deriv_tick_count, | |
| deriv_accumulated_change / deriv_tick_count, | |
| current_price); | |
| } | |
| deriv_last_price = current_price; | |
| deriv_last_tick_time = current_time; | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced Lot Size Calculation (Deriv-aware) | | |
| //+------------------------------------------------------------------+ | |
| double CalculateOptimalLotSizeDeriv(double stop_loss_distance) | |
| { | |
| // Use your original calculation as base | |
| double base_lot_size = CalculateOptimalLotSize(stop_loss_distance); | |
| // Apply Deriv-specific adjustments ONLY if it's a synthetic | |
| if (!is_deriv_synthetic) { | |
| return base_lot_size; // Return original calculation for Gold/BTC/FX | |
| } | |
| // Deriv-specific enhancements | |
| double deriv_min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); | |
| double deriv_lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); | |
| double deriv_max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); | |
| // Ensure lot step compliance for Deriv | |
| if (deriv_lot_step > 0) { | |
| base_lot_size = NormalizeDouble(MathRound(base_lot_size / deriv_lot_step) * deriv_lot_step, 3); | |
| } | |
| // Apply Deriv limits | |
| base_lot_size = MathMax(deriv_min_lot, MathMin(deriv_max_lot, base_lot_size)); | |
| if (DerivVerboseLogging) { | |
| PrintFormat("Deriv Lot Calc: Original=%.3f, Adjusted=%.3f (Step=%.3f)", | |
| CalculateOptimalLotSize(stop_loss_distance), base_lot_size, deriv_lot_step); | |
| } | |
| return base_lot_size; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced Order Validation (Deriv-aware) | | |
| //+------------------------------------------------------------------+ | |
| bool ValidateDerivOrder(double price, double sl, double tp, double lot_size) | |
| { | |
| if (!is_deriv_synthetic) { | |
| return true; // Skip validation for non-Deriv instruments | |
| } | |
| // Deriv-specific validations | |
| int deriv_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); | |
| int deriv_stops_level = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); | |
| double deriv_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); | |
| // Normalize prices for Deriv precision | |
| price = NormalizeDouble(price, deriv_digits); | |
| sl = NormalizeDouble(sl, deriv_digits); | |
| tp = NormalizeDouble(tp, deriv_digits); | |
| // Check minimum distance requirements | |
| if (deriv_stops_level > 0) { | |
| double min_distance = deriv_stops_level * deriv_point; | |
| double sl_distance = MathAbs(price - sl); | |
| double tp_distance = MathAbs(tp - price); | |
| if (sl_distance < min_distance) { | |
| PrintFormat("β Deriv Validation: SL distance %.5f < required %.5f", sl_distance, min_distance); | |
| return false; | |
| } | |
| if (tp_distance < min_distance) { | |
| PrintFormat("β Deriv Validation: TP distance %.5f < required %.5f", tp_distance, min_distance); | |
| return false; | |
| } | |
| } | |
| // Check tick confirmation for Deriv synthetics | |
| if (deriv_tick_count < DerivMinTicks) { | |
| if (DerivVerboseLogging) { | |
| PrintFormat("β³ Deriv: Waiting for tick confirmation (%d/%d)", deriv_tick_count, DerivMinTicks); | |
| } | |
| return false; | |
| } | |
| if (DerivVerboseLogging) { | |
| PrintFormat("β Deriv Order Validated: Price=%.5f, SL=%.5f, TP=%.5f, Lot=%.3f", | |
| price, sl, tp, lot_size); | |
| } | |
| return true; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced trade opening with ATR-based SL/TP | | |
| //+------------------------------------------------------------------+ | |
| void OpenTrade(ENUM_ORDER_TYPE order_type, double current_atr) | |
| { | |
| PrintFormat("=== Opening %s Trade ===", EnumToString(order_type)); | |
| MqlTradeRequest request = {}; | |
| MqlTradeResult result = {}; | |
| // Get current prices | |
| double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); | |
| double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); | |
| double entry_price = (order_type == ORDER_TYPE_BUY) ? ask : bid; | |
| // Calculate ATR-based Stop Loss and Take Profit | |
| double sl_distance, tp_distance; | |
| if (UseVolatilityFilter) { | |
| sl_distance = current_atr * ATR_Multiplier; | |
| tp_distance = sl_distance * RiskRewardRatio; // Apply risk-reward ratio | |
| } else { | |
| // Fallback to fixed points if ATR is not used | |
| sl_distance = 100 * _Point; // 100 points default | |
| tp_distance = sl_distance * RiskRewardRatio; | |
| } | |
| // Ensure minimum distance requirements | |
| int stops_level = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); | |
| double min_distance = MathMax(stops_level * _Point, 10 * _Point); | |
| sl_distance = MathMax(sl_distance, min_distance); | |
| tp_distance = MathMax(tp_distance, min_distance); | |
| // Calculate exact SL and TP prices | |
| double sl_price, tp_price; | |
| if (order_type == ORDER_TYPE_BUY) { | |
| sl_price = entry_price - sl_distance; | |
| tp_price = entry_price + tp_distance; | |
| } else { | |
| sl_price = entry_price + sl_distance; | |
| tp_price = entry_price - tp_distance; | |
| } | |
| // Calculate optimal lot size based on SL distance | |
| double lot_size; | |
| if (is_deriv_synthetic && EnableDerivSupport) { | |
| lot_size = CalculateOptimalLotSizeDeriv(sl_distance); // Use Deriv-enhanced calculation | |
| } else { | |
| lot_size = CalculateOptimalLotSize(sl_distance); // Your original calculation | |
| } | |
| PrintFormat("Entry: %.5f, SL: %.5f (%.5f dist), TP: %.5f (%.5f dist), RRR: 1:%.1f", | |
| entry_price, sl_price, sl_distance, tp_price, tp_distance, RiskRewardRatio); | |
| // Validate order parameters (Deriv-aware) | |
| if (is_deriv_synthetic && EnableDerivSupport) { | |
| if (!ValidateDerivOrder(entry_price, sl_price, tp_price, lot_size)) { | |
| PrintFormat("β Deriv order validation failed - skipping trade"); | |
| return; | |
| } | |
| } | |
| // Prepare trade request | |
| request.action = TRADE_ACTION_DEAL; | |
| request.symbol = _Symbol; | |
| request.volume = lot_size; | |
| request.type = order_type; | |
| request.price = NormalizeDouble(entry_price, _Digits); | |
| request.sl = NormalizeDouble(sl_price, _Digits); | |
| request.tp = NormalizeDouble(tp_price, _Digits); | |
| request.deviation = Slippage; | |
| request.magic = MagicNumber; | |
| request.type_filling = optimal_fill_mode; // Use detected optimal mode | |
| request.comment = StringFormat("EMA_v3_%s_R%.1f%%%s", | |
| (order_type == ORDER_TYPE_BUY) ? "BUY" : "SELL", | |
| current_risk_percent, | |
| is_deriv_synthetic ? "_DERIV" : ""); | |
| // Send order with intelligent fallback system | |
| if (SendOrderWithFallback(request, result)) { | |
| if (result.retcode == TRADE_RETCODE_DONE) { | |
| PrintFormat("β TRADE OPENED: %s %.2f lots @ %.5f | SL: %.5f | TP: %.5f | Ticket: %d%s", | |
| EnumToString(order_type), lot_size, result.price, | |
| request.sl, request.tp, result.order, | |
| is_deriv_synthetic ? " [DERIV]" : ""); | |
| stats.total_trades++; | |
| stats.last_trade_time = TimeCurrent(); | |
| // Calculate expected risk and reward | |
| double risk_amount = lot_size * sl_distance * SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) / SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); | |
| double reward_amount = risk_amount * RiskRewardRatio; | |
| PrintFormat("Expected Risk: %.2f | Expected Reward: %.2f | RRR: 1:%.1f", | |
| risk_amount, reward_amount, RiskRewardRatio); | |
| } else { | |
| PrintFormat("β Trade Failed: %s (Code: %d) - %s", | |
| result.comment, result.retcode, GetTradeErrorDescription(result.retcode)); | |
| } | |
| } else { | |
| int last_error = GetLastError(); | |
| PrintFormat("β OrderSend Failed: Error %d - %s", last_error, GetTradeErrorDescription(last_error)); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Enhanced position management | | |
| //+------------------------------------------------------------------+ | |
| void ManageOpenPositions() | |
| { | |
| if (!UseTrailingStop && !UseBreakEven) return; | |
| double atr_values[1]; | |
| if (CopyBuffer(atr_handle, 0, 1, 1, atr_values) <= 0) return; | |
| double current_atr = atr_values[0]; | |
| for (int i = 0; i < PositionsTotal(); i++) { | |
| ulong ticket = PositionGetTicket(i); | |
| if (!PositionSelectByTicket(ticket)) continue; | |
| if (PositionGetInteger(POSITION_MAGIC) != MagicNumber || | |
| PositionGetString(POSITION_SYMBOL) != _Symbol) continue; | |
| ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); | |
| double open_price = PositionGetDouble(POSITION_PRICE_OPEN); | |
| double current_sl = PositionGetDouble(POSITION_SL); | |
| double current_tp = PositionGetDouble(POSITION_TP); | |
| double current_price = (pos_type == POSITION_TYPE_BUY) ? | |
| SymbolInfoDouble(_Symbol, SYMBOL_BID) : | |
| SymbolInfoDouble(_Symbol, SYMBOL_ASK); | |
| double profit_points = (pos_type == POSITION_TYPE_BUY) ? | |
| (current_price - open_price) : | |
| (open_price - current_price); | |
| // Move to breakeven | |
| if (UseBreakEven && current_sl != open_price) { | |
| double breakeven_trigger = current_atr * BreakevenATRMultiple; | |
| if (profit_points >= breakeven_trigger) { | |
| ModifyPosition(ticket, open_price, current_tp); | |
| PrintFormat("Moved to Breakeven: Ticket %d, Profit: %.5f >= Trigger: %.5f", | |
| ticket, profit_points, breakeven_trigger); | |
| } | |
| } | |
| // Trailing stop | |
| if (UseTrailingStop) { | |
| double trail_distance = current_atr * TrailingATRMultiple; | |
| double new_sl = 0; | |
| if (pos_type == POSITION_TYPE_BUY) { | |
| new_sl = current_price - trail_distance; | |
| if (new_sl > current_sl && new_sl < current_price) { | |
| ModifyPosition(ticket, new_sl, current_tp); | |
| PrintFormat("Trailing Stop Updated (BUY): Ticket %d, New SL: %.5f", ticket, new_sl); | |
| } | |
| } else { | |
| new_sl = current_price + trail_distance; | |
| if (new_sl < current_sl && new_sl > current_price) { | |
| ModifyPosition(ticket, new_sl, current_tp); | |
| PrintFormat("Trailing Stop Updated (SELL): Ticket %d, New SL: %.5f", ticket, new_sl); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Get position information | | |
| //+------------------------------------------------------------------+ | |
| ENUM_POSITION_TYPE GetPositionInfo(ulong &ticket) | |
| { | |
| ticket = 0; | |
| for (int i = 0; i < PositionsTotal(); i++) { | |
| if (PositionSelectByTicket(PositionGetTicket(i))) { | |
| if (PositionGetInteger(POSITION_MAGIC) == MagicNumber && | |
| PositionGetString(POSITION_SYMBOL) == _Symbol) { | |
| ticket = PositionGetInteger(POSITION_TICKET); | |
| return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); | |
| } | |
| } | |
| } | |
| return (ENUM_POSITION_TYPE)-1; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Close position | | |
| //+------------------------------------------------------------------+ | |
| void ClosePosition(ulong ticket) | |
| { | |
| if (!PositionSelectByTicket(ticket)) return; | |
| MqlTradeRequest request = {}; | |
| MqlTradeResult result = {}; | |
| request.action = TRADE_ACTION_DEAL; | |
| request.position = ticket; | |
| request.symbol = PositionGetString(POSITION_SYMBOL); | |
| request.volume = PositionGetDouble(POSITION_VOLUME); | |
| request.deviation = Slippage; | |
| request.magic = MagicNumber; | |
| request.type_filling = optimal_fill_mode; // Use detected optimal mode | |
| ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); | |
| if (pos_type == POSITION_TYPE_BUY) { | |
| request.type = ORDER_TYPE_SELL; | |
| request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID); | |
| } else { | |
| request.type = ORDER_TYPE_BUY; | |
| request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); | |
| } | |
| if (OrderSend(request, result)) { | |
| if (result.retcode == TRADE_RETCODE_DONE) { | |
| double profit = PositionGetDouble(POSITION_PROFIT); | |
| PrintFormat("Position Closed: Ticket %d, Price: %.5f, Profit: %.2f", | |
| ticket, result.price, profit); | |
| if (profit > 0) { | |
| stats.winning_trades++; | |
| stats.gross_profit += profit; | |
| } else { | |
| stats.losing_trades++; | |
| stats.gross_loss += MathAbs(profit); | |
| } | |
| } | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Modify position SL/TP | | |
| //+------------------------------------------------------------------+ | |
| bool ModifyPosition(ulong ticket, double new_sl, double new_tp) | |
| { | |
| MqlTradeRequest request = {}; | |
| MqlTradeResult result = {}; | |
| request.action = TRADE_ACTION_SLTP; | |
| request.position = ticket; | |
| request.sl = NormalizeDouble(new_sl, _Digits); | |
| request.tp = NormalizeDouble(new_tp, _Digits); | |
| return OrderSend(request, result) && (result.retcode == TRADE_RETCODE_DONE); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Initialize statistics | | |
| //+------------------------------------------------------------------+ | |
| void InitializeStats() | |
| { | |
| ZeroMemory(stats); | |
| stats.balance_high = AccountInfoDouble(ACCOUNT_BALANCE); | |
| stats.last_trade_time = 0; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Update statistics | | |
| //+------------------------------------------------------------------+ | |
| void UpdateStats() | |
| { | |
| double current_balance = AccountInfoDouble(ACCOUNT_BALANCE); | |
| double current_equity = AccountInfoDouble(ACCOUNT_EQUITY); | |
| // Update balance high watermark | |
| if (current_balance > stats.balance_high) { | |
| stats.balance_high = current_balance; | |
| } | |
| // Calculate drawdown | |
| double drawdown = 0; | |
| if (stats.balance_high > 0) { | |
| drawdown = (stats.balance_high - current_equity) / stats.balance_high * 100; | |
| } | |
| if (drawdown > stats.max_drawdown) { | |
| stats.max_drawdown = drawdown; | |
| } | |
| // Update profit tracking | |
| double floating_profit = 0; | |
| for (int i = 0; i < PositionsTotal(); i++) { | |
| if (PositionSelectByTicket(PositionGetTicket(i))) { | |
| if (PositionGetInteger(POSITION_MAGIC) == MagicNumber && | |
| PositionGetString(POSITION_SYMBOL) == _Symbol) { | |
| floating_profit += PositionGetDouble(POSITION_PROFIT); | |
| } | |
| } | |
| } | |
| double total_current_profit = stats.gross_profit - stats.gross_loss + floating_profit; | |
| if (total_current_profit > stats.max_profit) { | |
| stats.max_profit = total_current_profit; | |
| } | |
| stats.total_profit = total_current_profit; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Print initialization information | | |
| //+------------------------------------------------------------------+ | |
| void PrintInitializationInfo() | |
| { | |
| PrintFormat("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); | |
| PrintFormat("β Enhanced EMA EA v3.0 - PROFITABLE β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β SYMBOL: %-15s β TIMEFRAME: %-20s β", _Symbol, EnumToString(TimeFrame)); | |
| PrintFormat("β FAST EMA: %-13d β SLOW EMA: %-21d β", FastEMA_Period, SlowEMA_Period); | |
| PrintFormat("β TREND EMA: %-12d β MAGIC: %-24d β", TrendEMA_Period, MagicNumber); | |
| if (is_deriv_synthetic) { | |
| PrintFormat("β π― DERIV MODE: %-10s β TICK SENSITIVITY: %-15.1f β", "ACTIVE", DerivTickSensitivity); | |
| } | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β RISK MANAGEMENT β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β RISK PER TRADE: %-8.2f%% β RISK:REWARD RATIO: 1:%-10.1f β", current_risk_percent, RiskRewardRatio); | |
| PrintFormat("β MAX RISK: %-13.2f%% β ACCOUNT BALANCE: %-17.2f β", MaxRiskPercent, AccountInfoDouble(ACCOUNT_BALANCE)); | |
| PrintFormat("β RISK AMOUNT: %-12.2f β MAX LOT SIZE: %-19.2f β", AccountInfoDouble(ACCOUNT_BALANCE) * current_risk_percent / 100, MaxLotSize); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β TRADING FILTERS β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β TREND FILTER: %-11s β ADX FILTER: %-21s β", UseTrendFilter ? "ON" : "OFF", UseADXFilter ? "ON" : "OFF"); | |
| PrintFormat("β ADX MIN LEVEL: %-10.1f β VOLATILITY FILTER: %-16s β", MinADX_Level, UseVolatilityFilter ? "ON" : "OFF"); | |
| PrintFormat("β ATR MULTIPLIER: %-9.1f β ATR PERIOD: %-21d β", ATR_Multiplier, ATR_Period); | |
| PrintFormat("β SPREAD FILTER: %-11s β MAX SPREAD ATR: %-17.1f%% β", UseSpreadFilter ? "ON" : "OFF", MaxSpreadATR * 100); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β POSITION MANAGEMENT β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β TRAILING STOP: %-11s β BREAKEVEN: %-22s β", UseTrailingStop ? "ON" : "OFF", UseBreakEven ? "ON" : "OFF"); | |
| PrintFormat("β TRAIL ATR MULT: %-10.1f β BREAKEVEN ATR MULT: %-14.1f β", TrailingATRMultiple, BreakevenATRMultiple); | |
| PrintFormat("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); | |
| // Print expected performance metrics | |
| double balance = AccountInfoDouble(ACCOUNT_BALANCE); | |
| double risk_amount = balance * current_risk_percent / 100; | |
| double expected_reward = risk_amount * RiskRewardRatio; | |
| double break_even_win_rate = 100.0 / (1.0 + RiskRewardRatio); | |
| PrintFormat(""); | |
| PrintFormat("π EXPECTED PERFORMANCE METRICS:"); | |
| PrintFormat(" β’ Risk per trade: %.2f (%.2f%% of %.2f)", risk_amount, current_risk_percent, balance); | |
| PrintFormat(" β’ Expected reward: %.2f per winning trade", expected_reward); | |
| PrintFormat(" β’ Break-even win rate: %.1f%% (with 1:%.1f RRR)", break_even_win_rate, RiskRewardRatio); | |
| PrintFormat(" β’ Profitable above: %.1f%% win rate", break_even_win_rate); | |
| PrintFormat(""); | |
| PrintFormat("π― STRATEGY SUMMARY:"); | |
| PrintFormat(" β’ Fast EMA (%d) crosses above/below Slow EMA (%d)", FastEMA_Period, SlowEMA_Period); | |
| PrintFormat(" β’ Trend filter: Price must be above/below %d EMA", TrendEMA_Period); | |
| PrintFormat(" β’ ADX filter: Minimum trend strength %.1f", MinADX_Level); | |
| PrintFormat(" β’ ATR-based dynamic Stop Loss and Take Profit"); | |
| PrintFormat(" β’ Automatic breakeven and trailing stops"); | |
| PrintFormat(""); | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Print comprehensive final statistics | | |
| //+------------------------------------------------------------------+ | |
| void PrintFinalStats() | |
| { | |
| PrintFormat("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); | |
| PrintFormat("β FINAL TRADING STATISTICS β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| if (stats.total_trades > 0) { | |
| double win_rate = (double)stats.winning_trades / stats.total_trades * 100; | |
| double avg_win = stats.winning_trades > 0 ? stats.gross_profit / stats.winning_trades : 0; | |
| double avg_loss = stats.losing_trades > 0 ? stats.gross_loss / stats.losing_trades : 0; | |
| double profit_factor = stats.gross_loss > 0 ? stats.gross_profit / stats.gross_loss : 0; | |
| double expected_payoff = stats.total_trades > 0 ? stats.total_profit / stats.total_trades : 0; | |
| PrintFormat("β TOTAL TRADES: %-12d β WIN RATE: %-21.1f%% β", stats.total_trades, win_rate); | |
| PrintFormat("β WINNING TRADES: %-10d β LOSING TRADES: %-16d β", stats.winning_trades, stats.losing_trades); | |
| PrintFormat("β GROSS PROFIT: %-12.2f β GROSS LOSS: %-18.2f β", stats.gross_profit, stats.gross_loss); | |
| PrintFormat("β NET PROFIT: %-14.2f β PROFIT FACTOR: %-15.2f β", stats.total_profit, profit_factor); | |
| PrintFormat("β AVG WIN: %-17.2f β AVG LOSS: %-19.2f β", avg_win, avg_loss); | |
| PrintFormat("β MAX PROFIT: %-14.2f β MAX DRAWDOWN: %-16.2f%% β", stats.max_profit, stats.max_drawdown); | |
| PrintFormat("β EXPECTED PAYOFF: %-9.2f β RISK PER TRADE: %-14.2f%% β", expected_payoff, current_risk_percent); | |
| // Performance evaluation | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| PrintFormat("β PERFORMANCE EVALUATION β"); | |
| PrintFormat("β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£"); | |
| double required_win_rate = 100.0 / (1.0 + RiskRewardRatio); | |
| string performance_status = (win_rate >= required_win_rate) ? "β PROFITABLE" : "β οΈ NEEDS IMPROVEMENT"; | |
| PrintFormat("β REQUIRED WIN RATE: %-8.1f%% β STATUS: %-21s β", required_win_rate, performance_status); | |
| if (win_rate >= required_win_rate) { | |
| PrintFormat("β π CONGRATULATIONS! Your strategy is mathematically profitable β"); | |
| double excess_performance = win_rate - required_win_rate; | |
| PrintFormat("β Win rate exceeds breakeven by %.1f%% - Great job! β", excess_performance); | |
| } else { | |
| PrintFormat("β π OPTIMIZATION SUGGESTIONS: β"); | |
| PrintFormat("β β’ Consider tighter entry filters (higher ADX, trend conf.) β"); | |
| PrintFormat("β β’ Review timeframe - higher TF may reduce false signals β"); | |
| PrintFormat("β β’ Test different EMA periods or add momentum indicators β"); | |
| } | |
| // Risk-adjusted returns | |
| if (stats.max_drawdown > 0) { | |
| double recovery_factor = stats.total_profit / stats.max_drawdown; | |
| PrintFormat("β RECOVERY FACTOR: %-10.2f β (Profit/Max Drawdown) β", recovery_factor); | |
| } | |
| } else { | |
| PrintFormat("β NO TRADES EXECUTED β"); | |
| PrintFormat("β This could indicate: β"); | |
| PrintFormat("β β’ Very strict filters (consider relaxing some filters) β"); | |
| PrintFormat("β β’ Market conditions not suitable for EMA crossover β"); | |
| PrintFormat("β β’ Insufficient time for signal generation β"); | |
| } | |
| PrintFormat("βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ"); | |
| // Additional insights | |
| if (stats.total_trades >= 30) { | |
| PrintFormat(""); | |
| PrintFormat("π STATISTICAL RELIABILITY: Good (30+ trades)"); | |
| if (stats.total_profit > 0) { | |
| double roi_percent = (stats.total_profit / AccountInfoDouble(ACCOUNT_BALANCE)) * 100; | |
| PrintFormat("π RETURN ON INVESTMENT: %.2f%%", roi_percent); | |
| } | |
| } else if (stats.total_trades >= 10) { | |
| PrintFormat(""); | |
| PrintFormat("β οΈ STATISTICAL RELIABILITY: Moderate (10+ trades) - Need more data"); | |
| } else if (stats.total_trades > 0) { | |
| PrintFormat(""); | |
| PrintFormat("β οΈ STATISTICAL RELIABILITY: Low (<10 trades) - Results not reliable"); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Get deinitialization reason text | | |
| //+------------------------------------------------------------------+ | |
| string GetDeinitReasonText(int reason) | |
| { | |
| switch(reason) { | |
| case REASON_PROGRAM: return "EA stopped by user"; | |
| case REASON_REMOVE: return "EA removed from chart"; | |
| case REASON_RECOMPILE: return "EA recompiled"; | |
| case REASON_CHARTCHANGE: return "Chart symbol/period changed"; | |
| case REASON_CHARTCLOSE: return "Chart closed"; | |
| case REASON_PARAMETERS: return "Parameters changed"; | |
| case REASON_ACCOUNT: return "Account changed"; | |
| case REASON_TEMPLATE: return "Template changed"; | |
| case REASON_INITFAILED: return "Initialization failed"; | |
| case REASON_CLOSE: return "Terminal closed"; | |
| default: return StringFormat("Unknown reason (%d)", reason); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Input dialog box function | | |
| //+------------------------------------------------------------------+ | |
| bool InputBox(string caption, string prompt, string &result) | |
| { | |
| // This is a simplified version - in real MT5, you might want to use | |
| // a more sophisticated input method or external DLL | |
| Print("=== ", caption, " ==="); | |
| Print(prompt); | |
| Print("Using default/current value: ", result); | |
| // For backtesting, we'll use the default value | |
| // In live trading, this would show an actual input dialog | |
| return true; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment