Skip to content

Instantly share code, notes, and snippets.

@Elvmeen
Last active October 20, 2025 12:50
Show Gist options
  • Select an option

  • Save Elvmeen/0265bb0d0beb57bee1a17de4cd1e2964 to your computer and use it in GitHub Desktop.

Select an option

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
//+------------------------------------------------------------------+
//| 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 |
//+------------------------------------------------------------------+
@wamahium
Copy link

wamahium commented Aug 4, 2025

interesting, let try it out. thanks for sharing.

@Elvmeen
Copy link
Author

Elvmeen commented Aug 4, 2025

@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