mql5/Experts/Advisors/ERMT_7.1_main.mq5

1784 lines
No EOL
60 KiB
MQL5

//+------------------------------------------------------------------+
//| Risk Management Overlay EA v7.1 |
//| Fully Optimized Version |
//| Fixed Multi-Symbol Detection |
//+------------------------------------------------------------------+
#property copyright "Institutional Risk Management System v7.1"
#property version "7.0"
#property strict
#property description "Modular risk management system with advanced monitoring"
//--- Include system modules
#include <Trade/Trade.mqh>
#include "Modules/RiskManager.mqh"
#include "Modules/TradeManager.mqh"
#include "Modules/EntrySystem.mqh"
#include "Modules/TechnicalAnalysis.mqh"
#include "Modules/Dashboard_Enhanced.mqh"
#include "Modules/Utilities.mqh"
#include "Modules/MultiTradeReporter.mqh"
//+------------------------------------------------------------------+
//| INPUT PARAMETERS - GROUPED BY MODULE |
//+------------------------------------------------------------------+
//--- System Configuration
input group "=== SYSTEM CONFIGURATION ==="
input bool SystemEnabled = true; // Enable System
input int TradingMagic = 12345; // EA Magic Number
input bool ManageExternalTrades = true; // Manage External Trades
input int MagicNumberFilter = 0; // External Magic Filter (0=all)
input ENUM_LOG_LEVEL LogLevel = LOG_INFO; // Logging Level
//--- Entry System Parameters
input group "=== ENTRY SYSTEM ==="
input ENUM_ENTRY_MODE EntryMode = ENTRY_MA_CROSS; // Entry System Type
input bool EnableMultipleEntries = false; // Allow Multiple Entries
input int MaxPositions = 3; // Maximum Positions
input double MinTimeBetweenTrades = 60; // Min Minutes Between Trades
//--- Risk Management Parameters
input group "=== RISK MANAGEMENT ==="
input ENUM_POSITION_SIZING SizingMode = PS_RISK_PERCENT; // Position Sizing Mode
input double RiskPercent = 2.5; // Risk Per Trade (%)
input double MaxRiskPercent = 5.0; // Maximum Risk (%)
input double MaxDailyDrawdown = 5.0; // Max Daily Drawdown (%)
input double MaxTotalExposure = 10.0; // Max Total Exposure (%)
input bool UseKellyCriterion = false; // Use Kelly Criterion
input double KellyFraction = 0.25; // Kelly Fraction
input double MinLotSize = 0.01; // Minimum Lot Size
input double MaxLotSize = 10.0; // Maximum Lot Size
//--- Trade Management Parameters
input group "=== TRADE MANAGEMENT ==="
input ENUM_TP_MODE TPMode = TP_ADAPTIVE; // Take Profit Mode
input ENUM_SL_MODE SLMode = SL_ATR; // Stop Loss Mode
input bool EnableBreakeven = true; // Enable Breakeven
input double BreakevenTrigger = 1.0; // Breakeven at X:1
input bool EnableTrailing = true; // Enable Trailing Stop
input double TrailingStart = 1.5; // Start Trailing at X:1
input bool EnablePartialClose = true; // Enable Partial Closes
input string PartialLevels = "1.0:33,2.0:33,3.0:20"; // Partial Close Levels
//--- External Trade Management
input group "=== EXTERNAL TRADE RULES ==="
input bool ForceRiskRules = true; // Force Risk Rules
input bool ForceStopLoss = true; // Force Stop Loss
input bool ForceTakeProfit = true; // Force Take Profit
input bool OverrideExternalSL = false; // Override Existing SL
input bool OverrideExternalTP = false; // Override Existing TP
input double DefaultExternalRisk = 1.0; // Default External Risk (%)
input bool CloseExcessiveRisk = true; // Close Excessive Risk
//--- Technical Analysis Parameters
input group "=== TECHNICAL ANALYSIS ==="
input bool UseSupportResistance = true; // Use S/R Levels
input bool UseFibonacci = true; // Use Fibonacci
input bool UsePivotPoints = true; // Use Pivot Points
input bool UseMarketStructure = true; // Use Market Structure
input bool EnableLogging = true; // Enable Detailed Logging
input int TechnicalLookback = 200; // Technical Lookback Bars
//--- Dashboard Parameters
input group "=== DASHBOARD SETTINGS ==="
input bool ShowDashboard = true; // Show Dashboard
input int DashboardX = 20; // Dashboard X Position
input int DashboardY = 30; // Dashboard Y Position
input color DashboardColor = clrWhiteSmoke; // Dashboard Text Color
input color ProfitColor = clrLime; // Profit Color
input color LossColor = clrRed; // Loss Color
input int UpdateFrequency = 1; // Update Frequency (seconds)
//--- Enhanced Display Settings
input group "=== ENHANCED DISPLAY SETTINGS ==="
input int MaxTradesDisplay = 10; // Maximum Trades to Display
input bool GroupBySource = true; // Group Trades by Source
input bool ShowCorrelation = true; // Show Position Correlation
input color InternalTradeColor = clrAqua; // Internal Trade Color
input color ExternalTradeColor = clrOrange; // External Trade Color
input color WarningColor = clrYellow; // Warning Color
//--- Reporting Options
input group "=== REPORTING OPTIONS ==="
input bool GenerateReports = true; // Generate Periodic Reports
input bool ExportCSV = true; // Export Reports to CSV
input bool DetectEAsOnStartup = true; // Detect All EAs on Startup
input bool AutoDetectSymbols = true; // Auto-Detect Traded Symbols
input bool DebugMode = false; // Enable Debug Mode
//+------------------------------------------------------------------+
//| GLOBAL OBJECTS |
//+------------------------------------------------------------------+
//--- Module instances
CRiskManager *RiskMgr;
CTradeManager *TradeMgr;
CEntrySystem *EntrySys;
CTechnicalAnalysis *TechAnalysis;
CDashboardEnhanced *Dashboard;
CUtilities *Utils;
CMultiTradeReporter *Reporter;
//--- System state
SystemState g_SystemState;
PerformanceMetrics g_Performance;
MarketConditions g_MarketConditions;
//--- Trade tracking
ManagedTrade g_ManagedTrades[];
int g_TotalTrades = 0;
//--- Multi-symbol monitoring
string g_MonitoredSymbols[];
int g_SymbolCount = 0;
//--- Timing variables
datetime g_LastUpdateTime = 0;
datetime g_LastTradeTime = 0;
datetime g_LastReportTime = 0;
datetime g_LastSymbolScan = 0;
datetime g_LastDiagnostic = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Initialize system state
g_SystemState.is_active = SystemEnabled;
g_SystemState.magic_number = TradingMagic;
g_SystemState.start_balance = AccountInfoDouble(ACCOUNT_BALANCE);
g_SystemState.peak_balance = g_SystemState.start_balance;
g_SystemState.session_start = TimeCurrent();
//--- Create module instances
RiskMgr = new CRiskManager();
TradeMgr = new CTradeManager();
EntrySys = new CEntrySystem();
TechAnalysis = new CTechnicalAnalysis();
Dashboard = new CDashboardEnhanced();
Utils = new CUtilities();
Reporter = new CMultiTradeReporter();
//--- Initialize modules
if(!InitializeModules())
{
Print("Failed to initialize modules");
return(INIT_FAILED);
}
//--- Wait for indicators to be ready
if(MQLInfoInteger(MQL_TESTER))
{
Print("Backtest mode: Waiting for indicators to initialize...");
int required_bars = MathMax(200, TechnicalLookback);
if(Bars(_Symbol, PERIOD_CURRENT) < required_bars)
{
Print("Insufficient bars for indicator calculation. Need ", required_bars,
", have ", Bars(_Symbol, PERIOD_CURRENT));
return(INIT_FAILED);
}
double test_atr = (*TechAnalysis).GetATR(14);
if(test_atr <= 0)
{
Print("ATR indicator not ready. Please ensure sufficient historical data.");
return(INIT_FAILED);
}
Print("Indicators initialized successfully. ATR: ", test_atr);
}
//--- Initialize performance metrics
ZeroMemory(g_Performance);
//--- Initialize managed trades array
ArrayResize(g_ManagedTrades, 0);
ArraySetAsSeries(g_ManagedTrades, false);
//--- Initialize multi-symbol monitoring
if(ManageExternalTrades)
{
InitializeSymbolMonitoring();
//--- Initial detection
if(DetectEAsOnStartup)
{
DetectAllEAs();
}
//--- Initial external trade scan
Print("Performing initial external trade scan...");
CheckForNewExternalTradesEnhanced();
//--- Run diagnostic if in debug mode
if(DebugMode)
{
RunExternalTradeDiagnostics();
}
}
//--- Load existing positions
LoadExistingPositions();
//--- Create dashboard
if(ShowDashboard)
{
(*Dashboard).Create(DashboardX, DashboardY, DashboardColor, ProfitColor, LossColor);
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
}
//--- Initialize reporter
if(Reporter != NULL && GenerateReports)
{
(*Reporter).Initialize("Reports");
}
//--- Set timer for periodic updates
EventSetTimer(UpdateFrequency);
//--- Log initialization
(*Utils).Log(LOG_INFO, StringFormat("Risk Management Overlay v7.1 initialized. Balance: %.2f",
g_SystemState.start_balance));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Kill timer
EventKillTimer();
//--- Save final report
if(Utils != NULL && GenerateReports)
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
//--- Cleanup modules
if(Dashboard != NULL && ShowDashboard)
(*Dashboard).Destroy();
//--- Delete module instances
delete Reporter;
delete RiskMgr;
delete TradeMgr;
delete EntrySys;
delete TechAnalysis;
delete Dashboard;
delete Utils;
//--- Log shutdown
Print("Risk Management Overlay shutdown: ", (*Utils).GetUninitReasonText(reason));
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check if system is enabled
if(!g_SystemState.is_active || !SystemEnabled)
return;
//--- Check terminal connection
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
return;
//--- Check terminal connection and auto trading
if(Utils != NULL)
{
if(!(*Utils).CheckTerminalConnection())
return;
if(!(*Utils).CheckAutoTrading())
return;
}
//--- Update market conditions
UpdateMarketConditions();
//--- Process existing trades
ProcessManagedTrades();
//--- Check for new external trades (enhanced multi-symbol)
if(ManageExternalTrades)
{
CheckForNewExternalTradesEnhanced();
}
//--- Check entry signals (if enabled)
if(EntryMode != ENTRY_DISABLED)
CheckEntrySignals();
//--- Update dashboard
if(ShowDashboard && TimeCurrent() - g_LastUpdateTime >= UpdateFrequency)
{
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
g_LastUpdateTime = TimeCurrent();
}
//--- Periodic tasks
PerformPeriodicTasks();
//--- Debug output if enabled
if(DebugMode)
{
static datetime last_debug = 0;
if(TimeCurrent() - last_debug > 2)
{
string debug = StringFormat("DEBUG: Pos=%d Array=%d Ext=%d",
PositionsTotal(),
ArraySize(g_ManagedTrades),
CountExternalTrades());
Comment(debug);
last_debug = TimeCurrent();
}
}
}
//+------------------------------------------------------------------+
//| Timer function for periodic updates |
//+------------------------------------------------------------------+
void OnTimer()
{
//--- Update dashboard
if(ShowDashboard)
{
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
}
//--- Monitor positions
if(DebugMode)
{
MonitorPositions();
}
//--- Periodic EA detection
static datetime last_ea_detect = 0;
if(ManageExternalTrades && TimeCurrent() - last_ea_detect > 300) // Every 5 minutes
{
if(AutoDetectSymbols)
{
DetectTradedSymbols();
}
last_ea_detect = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Initialize symbol monitoring list |
//+------------------------------------------------------------------+
void InitializeSymbolMonitoring()
{
if(AutoDetectSymbols)
{
// Auto-detect all symbols with open positions
DetectTradedSymbols();
}
else
{
// Manual symbol list - customize as needed
g_SymbolCount = 10;
ArrayResize(g_MonitoredSymbols, g_SymbolCount);
g_MonitoredSymbols[0] = "EURUSD";
g_MonitoredSymbols[1] = "GBPUSD";
g_MonitoredSymbols[2] = "USDJPY";
g_MonitoredSymbols[3] = "AUDUSD";
g_MonitoredSymbols[4] = "USDCAD";
g_MonitoredSymbols[5] = "NZDUSD";
g_MonitoredSymbols[6] = "USDCHF";
g_MonitoredSymbols[7] = "EURJPY";
g_MonitoredSymbols[8] = "GBPJPY";
g_MonitoredSymbols[9] = "GOLD";
}
Print("Symbol Monitoring initialized with ", g_SymbolCount, " symbols");
}
//+------------------------------------------------------------------+
//| Auto-detect all symbols with positions |
//+------------------------------------------------------------------+
void DetectTradedSymbols()
{
// Clear existing array
ArrayFree(g_MonitoredSymbols);
g_SymbolCount = 0;
// Temporary array to collect unique symbols
string temp_symbols[];
int temp_count = 0;
// Scan all positions
int total_positions = PositionsTotal();
for(int i = 0; i < total_positions; i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
string symbol = PositionGetString(POSITION_SYMBOL);
// Check if symbol already in list
bool found = false;
for(int j = 0; j < temp_count; j++)
{
if(temp_symbols[j] == symbol)
{
found = true;
break;
}
}
// Add new symbol
if(!found)
{
ArrayResize(temp_symbols, temp_count + 1);
temp_symbols[temp_count] = symbol;
temp_count++;
}
}
}
// Also add current chart symbol if not already included
bool current_found = false;
for(int i = 0; i < temp_count; i++)
{
if(temp_symbols[i] == _Symbol)
{
current_found = true;
break;
}
}
if(!current_found)
{
ArrayResize(temp_symbols, temp_count + 1);
temp_symbols[temp_count] = _Symbol;
temp_count++;
}
// Copy to global array
g_SymbolCount = temp_count;
ArrayResize(g_MonitoredSymbols, g_SymbolCount);
for(int i = 0; i < g_SymbolCount; i++)
{
g_MonitoredSymbols[i] = temp_symbols[i];
}
// Log detected symbols
if(g_SymbolCount > 0)
{
string symbol_list = "Detected symbols: ";
for(int i = 0; i < g_SymbolCount; i++)
{
symbol_list += g_MonitoredSymbols[i];
if(i < g_SymbolCount - 1) symbol_list += ", ";
}
(*Utils).Log(LOG_INFO, symbol_list);
}
}
//+------------------------------------------------------------------+
//| Enhanced multi-symbol external trade detection |
//+------------------------------------------------------------------+
void CheckForNewExternalTradesEnhanced()
{
static datetime last_symbol_update = 0;
// Update symbol list periodically (every 60 seconds)
if(AutoDetectSymbols && TimeCurrent() - last_symbol_update > 60)
{
DetectTradedSymbols();
last_symbol_update = TimeCurrent();
}
// Scan all positions
int total_positions = PositionsTotal();
int external_trades_found = 0;
int trades_added = 0;
for(int i = 0; i < total_positions; i++)
{
ulong ticket = PositionGetTicket(i);
if(!ticket) continue;
if(PositionSelectByTicket(ticket))
{
// Get position details
string symbol = PositionGetString(POSITION_SYMBOL);
ulong magic = PositionGetInteger(POSITION_MAGIC);
// Skip our own trades
if(magic == TradingMagic)
{
continue;
}
// Check if already managed
if(IsTradeManaged(ticket))
{
continue;
}
// Apply magic number filter
if(MagicNumberFilter != 0 && magic != MagicNumberFilter)
{
continue;
}
external_trades_found++;
// Create managed trade entry
ManagedTrade trade;
if(CreateManagedTradeEnhanced(trade, ticket, symbol))
{
// Mark as external trade
trade.is_external = true;
trade.entry_reason = "External: " + GetEANameFromMagic(magic);
// Apply risk rules if configured
if(ForceRiskRules)
{
(*Utils).Log(LOG_INFO, StringFormat("Applying risk rules to external trade #%d on %s",
ticket, symbol));
(*RiskMgr).EnforceRiskRules(trade, DefaultExternalRisk, CloseExcessiveRisk);
}
// Add to managed trades array
if(AddManagedTrade(trade))
{
trades_added++;
(*Utils).Log(LOG_INFO, StringFormat("Added external trade #%d (%s %.2f lots) from %s to management",
ticket, symbol, trade.volume, GetEANameFromMagic(magic)));
// Apply default stops if needed
if((trade.sl == 0 && ForceStopLoss) || (trade.tp == 0 && ForceTakeProfit))
{
(*Utils).Log(LOG_INFO, "Applying default stops to external trade");
// Get ATR for the specific symbol
double symbol_atr = GetSymbolATR(symbol);
(*TradeMgr).ApplyDefaultStops(trade, symbol_atr);
}
}
}
}
}
if(external_trades_found > 0 || trades_added > 0)
{
(*Utils).Log(LOG_DEBUG, StringFormat("External trade scan complete: Found %d, Added %d new trades",
external_trades_found, trades_added));
}
}
//+------------------------------------------------------------------+
//| Create managed trade with multi-symbol support |
//+------------------------------------------------------------------+
bool CreateManagedTradeEnhanced(ManagedTrade &trade, ulong ticket, string symbol)
{
// Ensure we're working with the correct position
if(!PositionSelectByTicket(ticket))
{
(*Utils).Log(LOG_ERROR, StringFormat("Failed to select position #%d", ticket));
return false;
}
// Verify symbol matches
if(PositionGetString(POSITION_SYMBOL) != symbol)
{
(*Utils).Log(LOG_ERROR, StringFormat("Symbol mismatch for position #%d", ticket));
return false;
}
// Fill trade structure
trade.ticket = ticket;
trade.symbol = symbol;
trade.magic = PositionGetInteger(POSITION_MAGIC);
trade.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
trade.volume = PositionGetDouble(POSITION_VOLUME);
trade.current_volume = trade.volume;
trade.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
trade.open_time = (datetime)PositionGetInteger(POSITION_TIME);
trade.sl = PositionGetDouble(POSITION_SL);
trade.tp = PositionGetDouble(POSITION_TP);
trade.profit = PositionGetDouble(POSITION_PROFIT);
trade.swap = PositionGetDouble(POSITION_SWAP);
trade.comment = PositionGetString(POSITION_COMMENT);
// Get commission from deals
trade.commission = (*Utils).CalculatePositionCommission(ticket);
// Calculate risk metrics for the specific symbol
if(trade.sl > 0)
{
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double stop_distance;
if(trade.type == POSITION_TYPE_BUY)
stop_distance = trade.open_price - trade.sl;
else
stop_distance = trade.sl - trade.open_price;
if(stop_distance > 0 && tick_size > 0)
{
double potential_loss = (stop_distance / tick_size) * tick_value * trade.volume;
trade.risk_amount = potential_loss;
trade.risk_percent = (potential_loss / AccountInfoDouble(ACCOUNT_BALANCE)) * 100;
}
}
else
{
// No stop loss - use default risk calculation
trade.risk_amount = 0;
trade.risk_percent = DefaultExternalRisk;
}
// Calculate current metrics
double current_price = (trade.type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(symbol, SYMBOL_BID) :
SymbolInfoDouble(symbol, SYMBOL_ASK);
// Calculate R-multiple
if(trade.risk_amount > 0)
{
double current_pl = trade.profit + trade.swap + trade.commission;
trade.r_multiple = current_pl / trade.risk_amount;
}
else
{
trade.r_multiple = 0;
}
// Calculate MAE/MFE (simplified for initial creation)
trade.mae = 0;
trade.mfe = 0;
// Management flags
trade.is_managed = true;
trade.is_external = true;
trade.be_activated = false;
trade.trailing_activated = false;
trade.partial_count = 0;
// Time tracking
trade.bars_in_trade = 0;
trade.management_start = TimeCurrent();
return true;
}
//+------------------------------------------------------------------+
//| Get ATR for specific symbol |
//+------------------------------------------------------------------+
double GetSymbolATR(string symbol, int period = 14)
{
int atr_handle = iATR(symbol, PERIOD_CURRENT, period);
if(atr_handle == INVALID_HANDLE)
{
(*Utils).Log(LOG_ERROR, "Failed to create ATR handle for " + symbol);
return 50 * SymbolInfoDouble(symbol, SYMBOL_POINT); // Default fallback
}
double atr_buffer[1];
if(CopyBuffer(atr_handle, 0, 0, 1, atr_buffer) > 0 && atr_buffer[0] > 0)
{
IndicatorRelease(atr_handle);
return atr_buffer[0];
}
IndicatorRelease(atr_handle);
// Fallback calculation
double sum = 0;
for(int i = 1; i <= period; i++)
{
double high = iHigh(symbol, PERIOD_CURRENT, i);
double low = iLow(symbol, PERIOD_CURRENT, i);
double close_prev = iClose(symbol, PERIOD_CURRENT, i + 1);
if(high > 0 && low > 0 && close_prev > 0)
{
double tr = MathMax(high - low, MathMax(MathAbs(high - close_prev), MathAbs(low - close_prev)));
sum += tr;
}
}
return (sum > 0) ? sum / period : 50 * SymbolInfoDouble(symbol, SYMBOL_POINT);
}
//+------------------------------------------------------------------+
//| Count external trades in array |
//+------------------------------------------------------------------+
int CountExternalTrades()
{
int count = 0;
for(int i = 0; i < ArraySize(g_ManagedTrades); i++)
{
if(g_ManagedTrades[i].is_external)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Monitor positions (debug function) |
//+------------------------------------------------------------------+
void MonitorPositions()
{
static datetime last_check = 0;
static int last_total = -1;
// Check every 5 seconds
if(TimeCurrent() - last_check < 5) return;
last_check = TimeCurrent();
int total = PositionsTotal();
// Only print if changed
if(total != last_total)
{
last_total = total;
string summary = StringFormat("\n[POSITION MONITOR] Total: %d | Managed Array: %d | ",
total, ArraySize(g_ManagedTrades));
// Count by type
int own = 0, ext = 0, managed_ext = 0;
for(int i = 0; i < total; i++)
{
if(PositionSelectByTicket(PositionGetTicket(i)))
{
ulong magic = PositionGetInteger(POSITION_MAGIC);
if(magic == TradingMagic)
own++;
else
{
ext++;
if(IsTradeManaged(PositionGetTicket(i)))
managed_ext++;
}
}
}
summary += StringFormat("Own: %d | External: %d (Managed: %d)",
own, ext, managed_ext);
Print(summary);
// If external trades exist but aren't managed, run diagnostic
if(ext > 0 && managed_ext == 0 && ManageExternalTrades)
{
Print("⚠️ External trades not being managed! Running diagnostic...");
RunExternalTradeDiagnostics();
}
}
}
//+------------------------------------------------------------------+
//| Run external trade diagnostics |
//+------------------------------------------------------------------+
void RunExternalTradeDiagnostics()
{
Print("\n========== EXTERNAL TRADE DETECTION DIAGNOSTICS ==========");
Print("Timestamp: ", TimeToString(TimeCurrent()));
Print("Running from: ", _Symbol, " ", EnumToString((ENUM_TIMEFRAMES)_Period));
//--- 1. Check EA Settings
Print("\n[1] EA CONFIGURATION CHECK:");
Print(" SystemEnabled: ", SystemEnabled ? "YES" : "NO");
Print(" ManageExternalTrades: ", ManageExternalTrades ? "ENABLED" : "DISABLED");
Print(" MagicNumberFilter: ", MagicNumberFilter, (MagicNumberFilter == 0) ? " (All external trades)" : " (Specific magic only)");
Print(" TradingMagic (Own): ", TradingMagic);
Print(" ForceRiskRules: ", ForceRiskRules ? "YES" : "NO");
Print(" DefaultExternalRisk: ", DoubleToString(DefaultExternalRisk, 2), "%");
Print(" AutoDetectSymbols: ", AutoDetectSymbols ? "YES" : "NO");
//--- 2. Scan all open positions
Print("\n[2] POSITION SCAN:");
int total_positions = PositionsTotal();
Print(" Total positions in terminal: ", total_positions);
if(total_positions == 0)
{
Print(" ⚠️ NO OPEN POSITIONS FOUND!");
Print(" Make sure other EAs have open trades.");
return;
}
//--- Categorize positions
int own_trades = 0;
int external_trades = 0;
int filtered_trades = 0;
int managed_trades = 0;
Print("\n[3] POSITION DETAILS:");
for(int i = 0; i < total_positions; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
{
Print(" Error getting ticket at index ", i);
continue;
}
if(!PositionSelectByTicket(ticket))
{
Print(" Error selecting position ", ticket);
continue;
}
string symbol = PositionGetString(POSITION_SYMBOL);
ulong magic = PositionGetInteger(POSITION_MAGIC);
double volume = PositionGetDouble(POSITION_VOLUME);
string type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? "BUY" : "SELL";
bool is_managed = IsTradeManaged(ticket);
Print("\n Position #", ticket, ":");
Print(" - Symbol: ", symbol);
Print(" - Type: ", type);
Print(" - Volume: ", DoubleToString(volume, 2));
Print(" - Magic: ", magic, " (", GetEANameFromMagic(magic), ")");
// Determine status
if(magic == TradingMagic)
{
own_trades++;
Print(" - Status: OWN TRADE (will skip)");
}
else if(MagicNumberFilter != 0 && magic != MagicNumberFilter)
{
filtered_trades++;
Print(" - Status: FILTERED OUT (magic doesn't match filter ", MagicNumberFilter, ")");
}
else if(is_managed)
{
managed_trades++;
Print(" - Status: ALREADY MANAGED ✓");
}
else
{
external_trades++;
Print(" - Status: EXTERNAL TRADE - SHOULD BE ADDED! ⚠️");
if(!ManageExternalTrades)
{
Print(" → ISSUE: ManageExternalTrades is DISABLED!");
}
}
}
//--- 4. Summary
Print("\n[4] SUMMARY:");
Print(" Own trades: ", own_trades);
Print(" External trades (eligible): ", external_trades);
Print(" Filtered out: ", filtered_trades);
Print(" Already managed: ", managed_trades);
//--- 5. Check managed trades array
Print("\n[5] MANAGED TRADES ARRAY:");
int array_size = ArraySize(g_ManagedTrades);
Print(" Size of g_ManagedTrades: ", array_size);
if(array_size > 0)
{
int internal_count = 0;
int external_count = 0;
for(int i = 0; i < array_size; i++)
{
if(g_ManagedTrades[i].is_external)
external_count++;
else
internal_count++;
Print(" Trade[", i, "]: #", g_ManagedTrades[i].ticket,
" ", g_ManagedTrades[i].symbol,
" Magic: ", g_ManagedTrades[i].magic,
" External: ", g_ManagedTrades[i].is_external ? "YES" : "NO",
" Valid: ", (g_ManagedTrades[i].ticket > 0) ? "YES" : "NO");
}
Print(" Internal trades in array: ", internal_count);
Print(" External trades in array: ", external_count);
}
else
{
Print(" ⚠️ Managed trades array is EMPTY!");
}
//--- 6. Symbol monitoring
Print("\n[6] SYMBOL MONITORING:");
Print(" Monitored symbols: ", g_SymbolCount);
for(int i = 0; i < g_SymbolCount; i++)
{
Print(" - ", g_MonitoredSymbols[i]);
}
//--- 7. Dashboard check
Print("\n[7] DASHBOARD STATUS:");
Print(" ShowDashboard: ", ShowDashboard ? "ENABLED" : "DISABLED");
Print(" Dashboard pointer: ", (Dashboard != NULL) ? "VALID" : "NULL");
//--- 8. Recommendations
Print("\n[8] DIAGNOSTIC RECOMMENDATIONS:");
if(!SystemEnabled)
{
Print(" ❌ CRITICAL: SystemEnabled is FALSE - EA is disabled!");
}
if(!ManageExternalTrades)
{
Print(" ❌ CRITICAL: Enable ManageExternalTrades to detect external trades!");
}
if(external_trades > 0 && external_count == 0)
{
Print(" ❌ ISSUE: External trades exist but none in managed array!");
Print(" → Verify CheckForNewExternalTradesEnhanced() is being called in OnTick()");
Print(" → Check if AddManagedTrade() function is working correctly");
Print(" → Ensure CreateManagedTradeEnhanced() is creating valid trade objects");
}
if(MagicNumberFilter != 0 && filtered_trades > 0)
{
Print(" ⚠️ WARNING: ", filtered_trades, " trades filtered out by MagicNumberFilter");
Print(" → Set MagicNumberFilter = 0 to manage ALL external trades");
}
if(array_size == 0 && (own_trades > 0 || external_trades > 0))
{
Print(" ❌ CRITICAL: Managed trades array is empty but trades exist!");
Print(" → Check array initialization in OnInit()");
Print(" → Verify ProcessManagedTrades() isn't clearing the array");
}
Print("\n========== END OF DIAGNOSTICS ==========\n");
}
//+------------------------------------------------------------------+
//| Add managed trade to array with validation |
//+------------------------------------------------------------------+
bool AddManagedTrade(ManagedTrade &trade)
{
// Validate trade
if(trade.ticket == 0)
{
(*Utils).Log(LOG_ERROR, "Cannot add trade with ticket 0");
return false;
}
// Check if already exists
if(IsTradeManaged(trade.ticket))
{
(*Utils).Log(LOG_WARNING, StringFormat("Trade #%d already managed", trade.ticket));
return false;
}
// Add to array
int size = ArraySize(g_ManagedTrades);
if(ArrayResize(g_ManagedTrades, size + 1) < 0)
{
(*Utils).Log(LOG_ERROR, "Failed to resize managed trades array");
return false;
}
g_ManagedTrades[size] = trade;
g_TotalTrades++;
(*Utils).Log(LOG_DEBUG, StringFormat("Added trade #%d to position %d in array",
trade.ticket, size));
return true;
}
//+------------------------------------------------------------------+
//| Create managed trade structure (for compatibility) |
//+------------------------------------------------------------------+
bool CreateManagedTrade(ManagedTrade &trade, ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
string symbol = PositionGetString(POSITION_SYMBOL);
return CreateManagedTradeEnhanced(trade, ticket, symbol);
}
//+------------------------------------------------------------------+
//| Initialize all modules |
//+------------------------------------------------------------------+
bool InitializeModules()
{
//--- Initialize Risk Manager
RiskManagerConfig riskConfig;
riskConfig.sizing_mode = SizingMode;
riskConfig.risk_percent = RiskPercent;
riskConfig.max_risk_percent = MaxRiskPercent;
riskConfig.max_daily_dd = MaxDailyDrawdown;
riskConfig.max_exposure = MaxTotalExposure;
riskConfig.use_kelly = UseKellyCriterion;
riskConfig.kelly_fraction = KellyFraction;
riskConfig.min_lot_size = MinLotSize;
riskConfig.max_lot_size = MaxLotSize;
if(!(*RiskMgr).Initialize(riskConfig))
return false;
//--- Initialize Trade Manager
TradeManagerConfig tradeConfig;
tradeConfig.tp_mode = TPMode;
tradeConfig.sl_mode = SLMode;
tradeConfig.enable_breakeven = EnableBreakeven;
tradeConfig.breakeven_trigger = BreakevenTrigger;
tradeConfig.enable_trailing = EnableTrailing;
tradeConfig.trailing_start = TrailingStart;
tradeConfig.enable_partial = EnablePartialClose;
tradeConfig.partial_levels = PartialLevels;
tradeConfig.force_sl = ForceStopLoss;
tradeConfig.force_tp = ForceTakeProfit;
tradeConfig.override_sl = OverrideExternalSL;
tradeConfig.override_tp = OverrideExternalTP;
if(!(*TradeMgr).Initialize(tradeConfig, TradingMagic))
return false;
//--- Initialize Entry System
EntrySystemConfig entryConfig;
entryConfig.entry_mode = EntryMode;
entryConfig.max_positions = MaxPositions;
entryConfig.min_time_between = MinTimeBetweenTrades;
entryConfig.allow_multiple = EnableMultipleEntries;
if(!(*EntrySys).Initialize(entryConfig))
return false;
//--- Initialize Technical Analysis
TechnicalConfig techConfig;
techConfig.use_sr = UseSupportResistance;
techConfig.use_fib = UseFibonacci;
techConfig.use_pivot = UsePivotPoints;
techConfig.use_structure = UseMarketStructure;
techConfig.lookback = TechnicalLookback;
if(!(*TechAnalysis).Initialize(techConfig))
return false;
//--- Initialize Utilities
if(!(*Utils).Initialize(LogLevel))
return false;
return true;
}
//+------------------------------------------------------------------+
//| Load existing positions into management system |
//+------------------------------------------------------------------+
void LoadExistingPositions()
{
int total = PositionsTotal();
int loaded = 0;
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
string symbol = PositionGetString(POSITION_SYMBOL);
ulong magic = PositionGetInteger(POSITION_MAGIC);
bool should_manage = false;
//--- Check if we should manage this position
if(magic == TradingMagic)
{
should_manage = true;
}
else if(ManageExternalTrades && (MagicNumberFilter == 0 || magic == MagicNumberFilter))
{
should_manage = true;
}
if(should_manage && !IsTradeManaged(ticket))
{
ManagedTrade trade;
if(CreateManagedTradeEnhanced(trade, ticket, symbol))
{
if(magic != TradingMagic)
trade.is_external = true;
if(AddManagedTrade(trade))
{
loaded++;
(*Utils).Log(LOG_INFO, StringFormat("Loaded position #%d (%s) into management",
ticket, symbol));
}
}
}
}
if(loaded > 0)
{
(*Utils).Log(LOG_INFO, StringFormat("Loaded %d existing positions", loaded));
}
}
//+------------------------------------------------------------------+
//| Update market conditions |
//+------------------------------------------------------------------+
void UpdateMarketConditions()
{
// Get fresh market analysis
g_MarketConditions = (*TechAnalysis).AnalyzeMarket();
// Additional validation and fallback
if(g_MarketConditions.volatility <= 0)
{
Print("UpdateMarketConditions: Invalid volatility from TechAnalysis, calculating fallback");
// Direct ATR calculation
double atr = (*TechAnalysis).GetATR(14);
if(atr > 0)
{
g_MarketConditions.volatility = atr;
Print("UpdateMarketConditions: Set volatility to ", atr);
}
else
{
// Emergency fallback
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
g_MarketConditions.volatility = price * 0.001; // 0.1% of price
Print("UpdateMarketConditions: Using emergency volatility: ", g_MarketConditions.volatility);
}
}
g_MarketConditions.spread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
//--- Update risk manager with market conditions
(*RiskMgr).UpdateMarketConditions(g_MarketConditions);
}
//+------------------------------------------------------------------+
//| Process all managed trades |
//+------------------------------------------------------------------+
void ProcessManagedTrades()
{
for(int i = ArraySize(g_ManagedTrades) - 1; i >= 0; i--)
{
//--- Check if position still exists
if(!PositionSelectByTicket(g_ManagedTrades[i].ticket))
{
//--- Position closed, update performance
UpdatePerformanceMetrics(g_ManagedTrades[i]);
//--- Save to history if external
if(g_ManagedTrades[i].is_external)
{
(*Utils).SaveTradeHistory(g_ManagedTrades[i]);
}
RemoveManagedTrade(i);
continue;
}
//--- Update trade information
UpdateTradeInfo(g_ManagedTrades[i]);
//--- Apply risk management
(*RiskMgr).ValidateAndAdjustRisk(g_ManagedTrades[i]);
//--- Apply trade management
(*TradeMgr).ManageTrade(g_ManagedTrades[i], g_MarketConditions);
}
}
//+------------------------------------------------------------------+
//| Check entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
//--- Check position limits
if(ArraySize(g_ManagedTrades) >= MaxPositions)
{
if(EnableLogging)
(*Utils).Log(LOG_DEBUG, "Max positions reached");
return;
}
//--- Check time restriction
if(TimeCurrent() - g_LastTradeTime < MinTimeBetweenTrades * 60)
{
if(EnableLogging)
(*Utils).Log(LOG_DEBUG, "Time restriction active");
return;
}
//--- Ensure valid market conditions
if(g_MarketConditions.volatility <= 0)
{
UpdateMarketConditions();
}
//--- Get entry signal
EntrySignal signal = (*EntrySys).CheckSignal(g_MarketConditions);
if(signal.signal_type != SIGNAL_NONE)
{
// Additional safety check
if(signal.stop_loss_distance <= 0)
{
(*Utils).Log(LOG_ERROR, StringFormat("Zero stop distance detected! Signal: %s",
signal.comment));
// Emergency fallback
double emergency_stop = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
signal.stop_loss_distance = emergency_stop;
signal.take_profit_distance = emergency_stop * 2;
(*Utils).Log(LOG_WARNING, StringFormat("Applied emergency stop: %.5f", emergency_stop));
}
//--- Calculate position size
double lotSize = (*RiskMgr).CalculatePositionSize(signal.stop_loss_distance);
if(lotSize > 0 && (*RiskMgr).ValidateNewPosition(lotSize, signal.stop_loss_distance))
{
//--- Prepare trade request
TradeRequest request;
request.symbol = _Symbol;
request.volume = lotSize;
request.type = (signal.signal_type == SIGNAL_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
request.sl_distance = signal.stop_loss_distance;
request.tp_distance = signal.take_profit_distance;
request.comment = signal.comment;
//--- Execute trade
ulong ticket = (*TradeMgr).OpenPosition(request);
if(ticket > 0)
{
g_LastTradeTime = TimeCurrent();
//--- Add to managed trades
ManagedTrade trade;
if(CreateManagedTrade(trade, ticket))
{
trade.entry_reason = signal.comment;
trade.entry_signal = signal;
trade.is_external = false;
if(AddManagedTrade(trade))
{
(*Utils).Log(LOG_INFO, StringFormat("Opened %s position #%d, Lots: %.2f, Risk: %.2f%%",
(signal.signal_type == SIGNAL_BUY) ? "BUY" : "SELL",
ticket, lotSize, RiskPercent));
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Perform periodic maintenance tasks |
//+------------------------------------------------------------------+
void PerformPeriodicTasks()
{
//--- Daily reset check
MqlDateTime current_time;
TimeToStruct(TimeCurrent(), current_time);
static int last_day = -1;
if(current_time.day != last_day)
{
last_day = current_time.day;
ResetDailyMetrics();
}
//--- Generate periodic reports
if(GenerateReports && TimeCurrent() - g_LastReportTime > 3600) // Hourly
{
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
if(Reporter != NULL)
{
(*Reporter).GenerateFullReport(g_ManagedTrades, g_Performance);
}
g_LastReportTime = TimeCurrent();
}
//--- Check for news events
if((*Utils).IsNewsTime())
{
g_MarketConditions.news_event = true;
}
}
//+------------------------------------------------------------------+
//| Remove trade from managed array |
//+------------------------------------------------------------------+
void RemoveManagedTrade(int index)
{
int size = ArraySize(g_ManagedTrades);
if(index < 0 || index >= size)
return;
for(int i = index; i < size - 1; i++)
{
g_ManagedTrades[i] = g_ManagedTrades[i + 1];
}
ArrayResize(g_ManagedTrades, size - 1);
}
//+------------------------------------------------------------------+
//| Check if trade is already managed |
//+------------------------------------------------------------------+
bool IsTradeManaged(ulong ticket)
{
for(int i = 0; i < ArraySize(g_ManagedTrades); i++)
{
if(g_ManagedTrades[i].ticket == ticket)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Update trade information |
//+------------------------------------------------------------------+
void UpdateTradeInfo(ManagedTrade &trade)
{
if(!PositionSelectByTicket(trade.ticket))
return;
trade.current_volume = PositionGetDouble(POSITION_VOLUME);
trade.sl = PositionGetDouble(POSITION_SL);
trade.tp = PositionGetDouble(POSITION_TP);
trade.profit = PositionGetDouble(POSITION_PROFIT);
trade.commission = (*Utils).CalculatePositionCommission(trade.ticket);
trade.swap = PositionGetDouble(POSITION_SWAP);
//--- Update max profit/drawdown
double total_profit = trade.profit + trade.commission + trade.swap;
if(total_profit > trade.max_profit)
trade.max_profit = total_profit;
if(total_profit < trade.max_drawdown)
trade.max_drawdown = total_profit;
//--- Update R-multiple
if(trade.risk_amount > 0)
{
trade.r_multiple = total_profit / trade.risk_amount;
}
}
//+------------------------------------------------------------------+
//| Update performance metrics |
//+------------------------------------------------------------------+
void UpdatePerformanceMetrics(ManagedTrade &trade)
{
double final_profit = trade.profit + trade.commission + trade.swap;
//--- Update counts
g_Performance.total_trades++;
if(final_profit > 0)
{
g_Performance.winning_trades++;
g_Performance.gross_profit += final_profit;
g_Performance.consecutive_wins++;
g_Performance.consecutive_losses = 0;
}
else
{
g_Performance.losing_trades++;
g_Performance.gross_loss += MathAbs(final_profit);
g_Performance.consecutive_losses++;
g_Performance.consecutive_wins = 0;
}
//--- Update max consecutive
if(g_Performance.consecutive_wins > g_Performance.max_consecutive_wins)
g_Performance.max_consecutive_wins = g_Performance.consecutive_wins;
if(g_Performance.consecutive_losses > g_Performance.max_consecutive_losses)
g_Performance.max_consecutive_losses = g_Performance.consecutive_losses;
//--- Update profit factor and win rate
if(g_Performance.gross_loss > 0)
g_Performance.profit_factor = g_Performance.gross_profit / g_Performance.gross_loss;
else
g_Performance.profit_factor = (g_Performance.gross_profit > 0) ? 999.99 : 0;
g_Performance.win_rate = (g_Performance.total_trades > 0) ?
(double)g_Performance.winning_trades / g_Performance.total_trades * 100 : 0;
//--- Update drawdown
double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(current_balance > g_SystemState.peak_balance)
g_SystemState.peak_balance = current_balance;
double drawdown = (g_SystemState.peak_balance - current_balance) / g_SystemState.peak_balance * 100;
if(drawdown > g_Performance.max_drawdown_percent)
g_Performance.max_drawdown_percent = drawdown;
//--- Update net profit
g_Performance.net_profit = g_Performance.gross_profit - g_Performance.gross_loss;
//--- Update expectancy
if(g_Performance.total_trades > 0)
g_Performance.expectancy = g_Performance.net_profit / g_Performance.total_trades;
//--- Update average win/loss
if(g_Performance.winning_trades > 0)
g_Performance.avg_win = g_Performance.gross_profit / g_Performance.winning_trades;
if(g_Performance.losing_trades > 0)
g_Performance.avg_loss = g_Performance.gross_loss / g_Performance.losing_trades;
//--- Update daily metrics
g_Performance.daily_trades++;
g_Performance.daily_profit += final_profit;
}
//+------------------------------------------------------------------+
//| Reset daily metrics |
//+------------------------------------------------------------------+
void ResetDailyMetrics()
{
g_Performance.daily_profit = 0;
g_Performance.daily_trades = 0;
g_Performance.daily_volume = 0;
(*Utils).Log(LOG_INFO, "Daily metrics reset");
}
//+------------------------------------------------------------------+
//| Trade transaction event |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
//--- Handle trade events
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
if(trans.deal_type == DEAL_TYPE_BUY || trans.deal_type == DEAL_TYPE_SELL)
{
(*Utils).Log(LOG_DEBUG, StringFormat("New deal: #%d, Volume: %.2f",
trans.deal, trans.volume));
}
}
}
//+------------------------------------------------------------------+
//| Chart event handler |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//--- Handle keyboard shortcuts for debugging
if(id == CHARTEVENT_KEYDOWN)
{
if(lparam == 'D' || lparam == 'd') // D key
{
RunExternalTradeDiagnostics();
}
else if(lparam == 'R' || lparam == 'r') // R key
{
Print("Forcing external trade refresh...");
CheckForNewExternalTradesEnhanced();
}
else if(lparam == 'T' || lparam == 't') // T key
{
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
}
}
}
//+------------------------------------------------------------------+
//| EA Detection and Monitoring Functions |
//+------------------------------------------------------------------+
struct EAInfo
{
string symbol;
long magic_number;
string ea_name;
int trade_count;
double total_volume;
double total_risk;
double total_profit;
};
//+------------------------------------------------------------------+
//| Detect all EAs running on the account |
//+------------------------------------------------------------------+
void DetectAllEAs()
{
Print("=== DETECTING ALL EXPERT ADVISORS ===");
EAInfo detected_eas[];
int ea_count = 0;
// Scan all open positions
int total_positions = PositionsTotal();
for(int i = 0; i < total_positions; i++)
{
if(PositionSelectByTicket(PositionGetTicket(i)))
{
string symbol = PositionGetString(POSITION_SYMBOL);
long magic = PositionGetInteger(POSITION_MAGIC);
double volume = PositionGetDouble(POSITION_VOLUME);
double profit = PositionGetDouble(POSITION_PROFIT);
// Check if this magic number already recorded
bool found = false;
int index = -1;
for(int j = 0; j < ea_count; j++)
{
if(detected_eas[j].magic_number == magic &&
detected_eas[j].symbol == symbol)
{
found = true;
index = j;
break;
}
}
// Add new EA or update existing
if(!found)
{
ArrayResize(detected_eas, ea_count + 1);
detected_eas[ea_count].symbol = symbol;
detected_eas[ea_count].magic_number = magic;
detected_eas[ea_count].ea_name = GetEANameFromMagic(magic);
detected_eas[ea_count].trade_count = 1;
detected_eas[ea_count].total_volume = volume;
detected_eas[ea_count].total_profit = profit;
detected_eas[ea_count].total_risk = CalculatePositionRisk(PositionGetTicket(i));
ea_count++;
}
else
{
detected_eas[index].trade_count++;
detected_eas[index].total_volume += volume;
detected_eas[index].total_profit += profit;
detected_eas[index].total_risk += CalculatePositionRisk(PositionGetTicket(i));
}
}
}
// Display results
Print("\n╔════════════════════════════════════════════════════════╗");
Print("║ DETECTED EXPERT ADVISORS ║");
Print("╚════════════════════════════════════════════════════════╝");
if(ea_count == 0)
{
Print("No active EA trades detected.");
}
else
{
// Sort by magic number
for(int i = 0; i < ea_count - 1; i++)
{
for(int j = i + 1; j < ea_count; j++)
{
if(detected_eas[i].magic_number > detected_eas[j].magic_number)
{
EAInfo temp = detected_eas[i];
detected_eas[i] = detected_eas[j];
detected_eas[j] = temp;
}
}
}
// Display each EA
for(int i = 0; i < ea_count; i++)
{
Print("\n┌─────────────────────────────────────────────────────────┐");
Print("│ EA #", (i+1), ": ", detected_eas[i].ea_name,
" (Magic: ", detected_eas[i].magic_number, ")");
Print("├─────────────────────────────────────────────────────────┤");
Print("│ Symbol: ", detected_eas[i].symbol);
Print("│ Active Trades: ", detected_eas[i].trade_count);
Print("│ Total Volume: ", DoubleToString(detected_eas[i].total_volume, 2), " lots");
Print("│ Total Risk: ", DoubleToString(detected_eas[i].total_risk, 2), "%");
Print("│ Current P/L: $", DoubleToString(detected_eas[i].total_profit, 2));
Print("└─────────────────────────────────────────────────────────┘");
}
}
// Configuration suggestions
Print("\n╔════════════════════════════════════════════════════════╗");
Print("║ CONFIGURATION SUGGESTIONS ║");
Print("╚════════════════════════════════════════════════════════╝");
if(ea_count > 0)
{
Print("\nTo manage specific EAs, use these MagicNumberFilter values:");
for(int i = 0; i < ea_count; i++)
{
if(detected_eas[i].magic_number != 0) // Skip manual trades
{
Print("- For ", detected_eas[i].ea_name, ": MagicNumberFilter = ",
detected_eas[i].magic_number);
}
}
Print("\nTo manage ALL external trades: MagicNumberFilter = 0");
}
// Risk warnings
double total_risk = 0;
for(int i = 0; i < ea_count; i++)
{
total_risk += detected_eas[i].total_risk;
}
if(total_risk > 5.0)
{
Print("\n⚠️ WARNING: Total risk across all EAs: ",
DoubleToString(total_risk, 2), "%");
Print("Consider reducing position sizes or number of EAs.");
}
}
//+------------------------------------------------------------------+
//| Get EA name from magic number |
//+------------------------------------------------------------------+
string GetEANameFromMagic(long magic)
{
// Add your known EA magic numbers here
switch((int)magic)
{
case 0: return "Manual Trading";
case 12345: return "Risk Manager EA";
case 55555: return "Commercial EA";
case 77777: return "Scalper EA";
case 88888: return "Grid EA";
case 99999: return "Martingale EA";
// Magic number ranges
default:
{
if(magic >= 10000 && magic < 20000)
return StringFormat("External EA (%d)", magic);
else if(magic >= 20000 && magic < 30000)
return StringFormat("Signal Service (%d)", magic);
else if(magic >= 30000 && magic < 40000)
return StringFormat("Copy Trader (%d)", magic);
else if(magic >= 80000 && magic < 90000)
return StringFormat("PAMM/MAM (%d)", magic);
else
return StringFormat("Unknown EA (%d)", magic);
}
}
}
//+------------------------------------------------------------------+
//| Calculate position risk percentage |
//+------------------------------------------------------------------+
double CalculatePositionRisk(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return 0;
string symbol = PositionGetString(POSITION_SYMBOL);
double sl = PositionGetDouble(POSITION_SL);
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double volume = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(sl == 0) return 0; // No stop loss
double stop_distance;
if(type == POSITION_TYPE_BUY)
stop_distance = open_price - sl;
else
stop_distance = sl - open_price;
if(stop_distance <= 0) return 0;
double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick_size <= 0) return 0;
double potential_loss = (stop_distance / tick_size) * tick_value * volume;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
return (balance > 0) ? (potential_loss / balance) * 100 : 0;
}
//+------------------------------------------------------------------+
//| Create EA summary dashboard label |
//+------------------------------------------------------------------+
void CreateEASummaryDashboard()
{
string summary = "═══ EA SUMMARY ═══\n";
int total_positions = PositionsTotal();
long magic_numbers[];
int magic_count = 0;
// Collect unique magic numbers
for(int i = 0; i < total_positions; i++)
{
if(PositionSelectByTicket(PositionGetTicket(i)))
{
long magic = PositionGetInteger(POSITION_MAGIC);
bool found = false;
for(int j = 0; j < magic_count; j++)
{
if(magic_numbers[j] == magic)
{
found = true;
break;
}
}
if(!found)
{
ArrayResize(magic_numbers, magic_count + 1);
magic_numbers[magic_count] = magic;
magic_count++;
}
}
}
// Build summary
for(int i = 0; i < magic_count; i++)
{
int count = 0;
double profit = 0;
for(int j = 0; j < total_positions; j++)
{
if(PositionSelectByTicket(PositionGetTicket(j)))
{
if(PositionGetInteger(POSITION_MAGIC) == magic_numbers[i])
{
count++;
profit += PositionGetDouble(POSITION_PROFIT);
}
}
}
summary += GetEANameFromMagic(magic_numbers[i]) + ": " +
IntegerToString(count) + " trades, P/L: $" +
DoubleToString(profit, 2) + "\n";
}
// Display on chart
Comment(summary);
}
//+------------------------------------------------------------------+