1808 lines
61 KiB
MQL5
1808 lines
61 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.1"
|
||
|
#property strict
|
||
|
#property description "Modular risk management system with advanced monitoring"
|
||
|
|
||
|
//--- Include system modules
|
||
|
#include <Trade/Trade.mqh>
|
||
|
#include "Modules_optimised/Dashboard_Enhanced.mqh"
|
||
|
#include "Modules_optimised/Datatypes_Optimised.mqh"
|
||
|
#include "Modules_optimised/EntrySystem_Optimised.mqh"
|
||
|
#include "Modules_optimised/Performance_Tracker.mqh"
|
||
|
#include "Modules_optimised/Risk_Manager_Optimised.mqh"
|
||
|
#include "Modules_optimised/Symbol_Manager.mqh"
|
||
|
#include "Modules_optimised/Trade_Executor.mqh"
|
||
|
#include "Modules_optimised/Trade_Manager_Optimised.mqh"
|
||
|
|
||
|
#include "Modules/TechnicalAnalysis.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();
|
||
|
|
||
|
//--- In OnInit()
|
||
|
// Initialize Symbol Manager (singleton)
|
||
|
CSymbolManager *symbolMgr = CSymbolManager::GetInstance();
|
||
|
|
||
|
// Initialize Performance Tracker
|
||
|
CPerformanceTracker *perfTracker = new CPerformanceTracker();
|
||
|
perfTracker.Initialize(1000, 252);
|
||
|
|
||
|
// Initialize Trade Executor
|
||
|
CTradeExecutor *tradeExec = new CTradeExecutor();
|
||
|
tradeExec.Initialize(TradingMagic, 10);
|
||
|
tradeExec.SetSymbolManager(symbolMgr);
|
||
|
|
||
|
// Update Dashboard to use new version
|
||
|
CDashboardEnhanced *dashboard = new CDashboardEnhanced();
|
||
|
dashboard.SetReferences(symbolMgr, perfTracker);
|
||
|
|
||
|
//--- 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);
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|