Last active
October 20, 2025 12:50
-
-
Save Elvmeen/0265bb0d0beb57bee1a17de4cd1e2964 to your computer and use it in GitHub Desktop.
Main EA Demo; Contains Timeframa Analysis Error & outdated input parameters First Version meant for version tracking & without synthetic indicis support
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" | |
| #property copyright "Enhanced by Elameen - Profitable EMA Strategy" | |
| //--- Input parameters | |
| input group "=== RISK MANAGEMENT (CRITICAL) ===" | |
| input bool UseInteractiveRisk = true; // Ask for Risk % at startup | |
| input double DefaultRiskPercent = 2.5; // Default Risk Percentage per Trade | |
| input double MaxRiskPercent = 5.0; // Maximum Risk Percentage allowed | |
| input double RiskRewardRatio = 2.0; // Risk:Reward Ratio (2.0 = 1:2) | |
| 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 = 100; // Trend Filter EMA Period | |
| input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Trading Timeframe | |
| input bool UseTrendFilter = true; // 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 = true; // 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) | |
| //--- 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 = 2.5; | |
| bool risk_confirmed = false; | |
| // 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 = "2.5"; | |
| if (InputBox("Risk Management Setup", | |
| StringFormat("Enter your desired RISK PERCENTAGE per trade:\n\n" + | |
| "• Recommended: 1-3%% for conservative trading\n" + | |
| "• Aggressive: 3-5%% (higher risk/reward)\n" + | |
| "• Maximum allowed: %.1f%%\n\n" + | |
| "Current balance: %.2f\n" + | |
| "Risk:Reward Ratio: 1:%.1f\n\n" + | |
| "Enter percentage (e.g., 2.5):", | |
| MaxRiskPercent, AccountInfoDouble(ACCOUNT_BALANCE), RiskRewardRatio), | |
| risk_input)) { | |
| current_risk_percent = StringToDouble(risk_input); | |
| if (current_risk_percent <= 0 || current_risk_percent > MaxRiskPercent) { | |
| PrintFormat("Invalid risk percentage: %.2f%%. Using default: %.2f%%", | |
| current_risk_percent, DefaultRiskPercent); | |
| current_risk_percent = DefaultRiskPercent; | |
| } | |
| } 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); | |
| } | |
| // Initialize indicators | |
| if (!InitializeIndicators()) { | |
| return INIT_FAILED; | |
| } | |
| // 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; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| 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 | |
| if (DefaultRiskPercent <= 0 || DefaultRiskPercent > MaxRiskPercent || MaxRiskPercent > 10) { | |
| Print("ERROR: Invalid risk parameters"); | |
| return false; | |
| } | |
| // Risk-Reward validation | |
| if (RiskRewardRatio < 1.0 || RiskRewardRatio > 5.0) { | |
| Print("ERROR: Risk-Reward ratio should be between 1.0 and 5.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() | |
| { | |
| // 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 proper risk management | | |
| //+------------------------------------------------------------------+ | |
| double CalculateOptimalLotSize(double stop_loss_distance) | |
| { | |
| double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); | |
| double risk_amount = account_balance * current_risk_percent / 100.0; | |
| // 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); | |
| double money_per_point = tick_value / tick_size; | |
| double lot_size = risk_amount / (stop_loss_distance * money_per_point); | |
| // 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 | |
| lot_size = MathMax(min_lot, MathMin(max_lot, lot_size)); | |
| lot_size = NormalizeDouble(MathRound(lot_size / lot_step) * lot_step, 2); | |
| // Verify final lot size | |
| if (lot_size < MinLotSize) lot_size = MinLotSize; | |
| if (lot_size > MaxLotSize) lot_size = MaxLotSize; | |
| PrintFormat("Risk Calculation: Balance=%.2f, Risk=%.2f%% (%.2f), SL Distance=%.5f, Lot=%.2f", | |
| account_balance, current_risk_percent, risk_amount, stop_loss_distance, lot_size); | |
| return lot_size; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| 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 = CalculateOptimalLotSize(sl_distance); | |
| 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); | |
| // 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 = ORDER_FILLING_IOC; | |
| request.comment = StringFormat("EMA_v3_%s_R%.1f%%", | |
| (order_type == ORDER_TYPE_BUY) ? "BUY" : "SELL", | |
| current_risk_percent); | |
| // Send order | |
| if (OrderSend(request, result)) { | |
| if (result.retcode == TRADE_RETCODE_DONE) { | |
| PrintFormat("✅ TRADE OPENED: %s %.2f lots @ %.5f | SL: %.5f | TP: %.5f | Ticket: %d", | |
| EnumToString(order_type), lot_size, result.price, | |
| request.sl, request.tp, result.order); | |
| 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)", result.comment, result.retcode); | |
| } | |
| } else { | |
| PrintFormat("❌ OrderSend Failed: Error %d", GetLastError()); | |
| } | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| 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; | |
| 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); | |
| 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; | |
| } | |
| //+------------------------------------------------------------------+ | |
| //| Personal notes: | | |
| //| of id have to work on this again, no touching or distorting RM, % changig defualts is the root cause of distortions in newer versions | | |
| //+------------------------------------------------------------------+ |
Author
interesting, let try it out. thanks for sharing.
Author
@wamahium try this at your own risk, this is not the version that is working, the version thats working is not here and i wont share it now, even if you try this, it wont work because you dont have the right input parameters for it work as I deisgned. be cationed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://drive.google.com/file/d/1tk9URr90dKzkjp_33-DAB8CGOdvw3w2K/view?usp=drive_link
🔍 Strategy Overview (How It Works)
This Expert Advisor (EA) is a trend-following strategy based on Exponential Moving Average (EMA) crossovers, enhanced with volatility (ATR) and trend strength (ADX) filters, along with risk management, trade management, and statistical tracking.
🧠 Strategy Logic – Step-by-Step
1. ✅ Entry Signal: EMA Crossover
Buy Signal: When the
Fast EMA (12)crosses above theSlow EMA (26).Sell Signal: When the
Fast EMA (12)crosses below theSlow EMA (26).2. ✅ Trend Confirmation: Trend EMA
Trend EMA (100)checks the broader market direction:Buy only if price > Trend EMA and Fast EMA > Trend EMA.
Sell only if price < Trend EMA and Fast EMA < Trend EMA.
3. ✅ Strength Confirmation: ADX Filter
Uses ADX (Average Directional Index):
Period:
14Minimum Required Value:
25.04. ✅ Volatility Filter: ATR
ATR (Average True Range) measures market volatility:
Period:
14ATR Multiplier:
1.5(used for Stop Loss distance)If volatility (ATR) is too low, or spread is too high relative to ATR, the trade is blocked.
Also used for:
SL = ATR × 1.5
TP = SL × Risk:Reward (default 2.0)
5. ✅ Spread Filter (Dynamic, based on ATR)
Current spread must be less than:
ATR × MaxSpreadATR, whereMaxSpreadATR = 0.36. ✅ Optional: Time Filter
Only trades between:
Start Hour:
08:00End Hour:
22:00📉 Exit Strategy: SL, TP, Breakeven & Trailing Stop
🔹 Stop Loss & Take Profit
SL: Calculated dynamically with ATR ×
1.5TP: SL ×
RiskRewardRatio (2.0)🔹 Breakeven Function (Optional)
When trade is in profit by ATR × 1.0, SL is moved to entry price.
🔹 Trailing Stop (Optional)
Moves SL to trail price at a distance of:
ATR × 2.0(TrailingATRMultiple)🧮 Risk Management
Risk Per Trade: Dynamically calculated as a % of account balance.
Default =
2.5%Max allowed =
5.0%Lot Size: Adjusted based on SL distance to keep the risk amount fixed.
🧠 Magic Number: What It Does
Defined as:
Purpose:
A unique ID to distinguish this EA’s trades from others.
Ensures this EA only manages its own positions, not trades from other EAs or manual entries.
✅ Summary Table of Indicator Use
Let me know if you want: