Skip to content

Instantly share code, notes, and snippets.

@Elvmeen
Last active August 5, 2025 17:40
Show Gist options
  • Select an option

  • Save Elvmeen/64f018706b25cdc5dab06b8ab2fc0de8 to your computer and use it in GitHub Desktop.

Select an option

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
//+------------------------------------------------------------------+
//| 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