Revert to functional ERMT6.8 with modules detailed in main EA. Working file. Upgrades to ERMT6.9 fundamentally flawed due to EA missing references to all modules. Branched out for WiP
1113 lines
No EOL
38 KiB
MQL5
1113 lines
No EOL
38 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Risk Management Overlay EA v6.8 |
|
|
//| Enhanced External Trade Management System |
|
|
//| Optimized Main Coordinator File |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#property copyright "ERMT v6.8 - Enhanced Architecture"
|
|
#property link ""
|
|
#property version "6.80"
|
|
#property description "Institutional-grade risk management and trade optimization"
|
|
#property strict
|
|
|
|
// Include all modules
|
|
#include "Modules/DataTypes.mqh"
|
|
#include "Modules/Utilities.mqh"
|
|
#include "Modules/RiskManager.mqh"
|
|
#include "Modules/TechnicalAnalysis.mqh"
|
|
#include "Modules/ExternalTradeManager.mqh"
|
|
#include "Modules/TradeManager.mqh"
|
|
#include "Modules/Dashboard.mqh"
|
|
#include "Modules/Dashboard_SelfContained.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input Parameters |
|
|
//+------------------------------------------------------------------+
|
|
// Risk Management
|
|
input group "=== Risk Management ==="
|
|
input double InpMaxRiskPerTrade = 2.0; // Max Risk Per Trade (%)
|
|
input double InpMaxDailyLoss = 6.0; // Max Daily Loss (%)
|
|
input double InpMaxDrawdown = 20.0; // Max Drawdown (%)
|
|
input int InpMaxPositions = 3; // Max Concurrent Positions
|
|
input double InpRiskRewardMin = 1.5; // Minimum Risk/Reward Ratio
|
|
|
|
// Position Management
|
|
input group "=== Position Management ==="
|
|
input ENUM_POSITION_SIZING InpSizingMethod = SIZING_FIXED_PERCENT; // Position Sizing Method
|
|
input double InpFixedLotSize = 0.1; // Fixed Lot Size
|
|
input double InpATRMultiplierSL = 2.0; // ATR Multiplier for SL
|
|
input double InpATRMultiplierTP = 3.0; // ATR Multiplier for TP
|
|
input ENUM_TP_METHOD InpTPMethod = TP_FIXED_RR; // Take Profit Method
|
|
input double InpTPRatio = 2.0; // TP Risk/Reward Ratio
|
|
|
|
// External Trade Management
|
|
input group "=== External Trade Management ==="
|
|
input bool InpManageExternal = true; // Manage External Trades
|
|
input ENUM_MANAGEMENT_MODE InpExternalMode = MODE_STOPS_ONLY; // Management Mode
|
|
input double InpExternalRiskLimit = 5.0; // External Trade Risk Limit (%)
|
|
input bool InpApplyTrailingExternal = true; // Apply Trailing to External
|
|
|
|
// Trailing Stop
|
|
input group "=== Trailing Stop Settings ==="
|
|
input ENUM_TRAILING_METHOD InpTrailingMethod = TRAIL_ATR; // Trailing Method
|
|
input double InpTrailStart = 30; // Trail Start (points)
|
|
input double InpTrailStep = 10; // Trail Step (points)
|
|
input double InpTrailDistance = 20; // Trail Distance (points)
|
|
input bool InpMoveToBreakeven = true; // Move to Breakeven
|
|
input double InpBreakevenTrigger = 20; // Breakeven Trigger (points)
|
|
input double InpBreakevenOffset = 2; // Breakeven Offset (points)
|
|
|
|
// Trading Settings
|
|
input group "=== Trading Settings ==="
|
|
input int InpMagicNumber = 68000; // Magic Number
|
|
input string InpComment = "ERMT_v6.8"; // Trade Comment
|
|
input double InpSlippage = 3; // Max Slippage (points)
|
|
input bool InpTradeOnNewBar = false; // Trade Only on New Bar
|
|
input bool InpAllowHedging = false; // Allow Hedging
|
|
input bool InpCloseOnOpposite = true; // Close on Opposite Signal
|
|
|
|
// Technical Indicators
|
|
input group "=== Technical Settings ==="
|
|
input int InpATRPeriod = 14; // ATR Period
|
|
input int InpRSIPeriod = 14; // RSI Period
|
|
input int InpMAPeriod = 20; // MA Period
|
|
input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // MA Method
|
|
input double InpBBDeviation = 2.0; // Bollinger Band Deviation
|
|
input int InpBBPeriod = 20; // Bollinger Band Period
|
|
|
|
// Trading Hours
|
|
input group "=== Trading Hours ==="
|
|
input bool InpUseTradeHours = false; // Use Trading Hours
|
|
input int InpStartHour = 8; // Start Hour (Server Time)
|
|
input int InpEndHour = 20; // End Hour (Server Time)
|
|
input bool InpFridayClose = true; // Close All on Friday
|
|
input int InpFridayCloseHour = 20; // Friday Close Hour
|
|
|
|
// Dashboard
|
|
input group "=== Dashboard Settings ==="
|
|
input bool InpShowDashboard = true; // Show Dashboard
|
|
input int InpDashboardX = 20; // Dashboard X Position
|
|
input int InpDashboardY = 50; // Dashboard Y Position
|
|
input color InpDashboardColor = clrWhite; // Dashboard Text Color
|
|
input int InpDashboardFontSize = 9; // Dashboard Font Size
|
|
|
|
// Logging
|
|
input group "=== Logging Settings ==="
|
|
input ENUM_LOG_LEVEL InpLogLevel = LOG_INFO; // Log Level
|
|
input bool InpSaveLog = true; // Save Log to File
|
|
input bool InpEmailAlerts = false; // Send Email Alerts
|
|
input bool InpPushAlerts = false; // Send Push Notifications
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global Variables |
|
|
//+------------------------------------------------------------------+
|
|
// Core components
|
|
CUtilities* g_utils;
|
|
CRiskManager* g_risk;
|
|
CTechnicalAnalysis* g_tech;
|
|
CExternalTradeManager* g_external;
|
|
CTradeManager* g_trade;
|
|
CDashboard* g_dashboard;
|
|
CDashboardSC* Dashboard = NULL; // That's it! No color variables needed
|
|
|
|
// State tracking
|
|
datetime g_last_bar_time = 0;
|
|
bool g_initialized = false;
|
|
int g_tick_count = 0;
|
|
double g_session_profit = 0;
|
|
datetime g_session_start = 0;
|
|
double g_starting_balance = 0;
|
|
|
|
// Market conditions
|
|
ENUM_MARKET_CONDITION g_current_market = MARKET_RANGING;
|
|
double g_current_volatility = 0;
|
|
double g_current_spread = 0;
|
|
bool g_trade_allowed = true;
|
|
|
|
// Performance tracking
|
|
int g_trades_today = 0;
|
|
int g_wins_today = 0;
|
|
int g_losses_today = 0;
|
|
datetime g_last_trade_time = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
Print("========================================");
|
|
Print("=== ERMT v6.8 Initializing ===");
|
|
Print("========================================");
|
|
|
|
// Validate inputs
|
|
if(!ValidateInputs())
|
|
{
|
|
Print("Invalid input parameters");
|
|
return INIT_PARAMETERS_INCORRECT;
|
|
}
|
|
|
|
// Create component instances
|
|
g_utils = new CUtilities();
|
|
g_risk = new CRiskManager();
|
|
g_tech = new CTechnicalAnalysis();
|
|
g_external = new CExternalTradeManager();
|
|
g_trade = new CTradeManager();
|
|
g_dashboard = new CDashboard();
|
|
|
|
// Initialize utilities first
|
|
if(!g_utils.Initialize(InpLogLevel))
|
|
{
|
|
Print("Failed to initialize utilities");
|
|
Cleanup();
|
|
return INIT_FAILED;
|
|
}
|
|
g_utils.EnableFileLogging(InpSaveLog);
|
|
|
|
// Initialize risk manager
|
|
RiskParameters risk_params;
|
|
risk_params.max_risk_per_trade = InpMaxRiskPerTrade;
|
|
risk_params.max_daily_loss = InpMaxDailyLoss;
|
|
risk_params.max_drawdown = InpMaxDrawdown;
|
|
risk_params.max_positions = InpMaxPositions;
|
|
risk_params.min_risk_reward = InpRiskRewardMin;
|
|
risk_params.use_money_management = true;
|
|
risk_params.scale_with_equity = true;
|
|
risk_params.risk_multiplier = 1.0;
|
|
|
|
if(!g_risk.Initialize(g_utils, risk_params))
|
|
{
|
|
Print("Failed to initialize risk manager");
|
|
Cleanup();
|
|
return INIT_FAILED;
|
|
}
|
|
g_risk.SetMagicNumber(InpMagicNumber);
|
|
|
|
// Initialize technical analysis
|
|
if(!g_tech.Initialize(g_utils, InpATRPeriod, InpRSIPeriod, InpMAPeriod))
|
|
{
|
|
Print("Failed to initialize technical analysis");
|
|
Cleanup();
|
|
return INIT_FAILED;
|
|
}
|
|
g_tech.SetBollingerParams(InpBBPeriod, InpBBDeviation);
|
|
|
|
// Initialize external trade manager
|
|
if(!g_external.Initialize(g_utils, InpMagicNumber))
|
|
{
|
|
Print("Failed to initialize external trade manager");
|
|
Cleanup();
|
|
return INIT_FAILED;
|
|
}
|
|
g_external.SetManagementMode(InpExternalMode);
|
|
g_external.SetExternalRiskLimit(InpExternalRiskLimit);
|
|
|
|
// Initialize trade manager
|
|
if(!g_trade.Initialize(g_utils, g_risk, g_tech, g_external, InpMagicNumber))
|
|
{
|
|
Print("Failed to initialize trade manager");
|
|
Cleanup();
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Configure trade manager
|
|
g_trade.SetSizingMethod(InpSizingMethod);
|
|
g_trade.SetFixedLotSize(InpFixedLotSize);
|
|
g_trade.SetATRMultipliers(InpATRMultiplierSL, InpATRMultiplierTP);
|
|
g_trade.SetTrailingMethod(InpTrailingMethod);
|
|
g_trade.SetTrailingParams(InpTrailStart, InpTrailStep, InpTrailDistance);
|
|
g_trade.SetBreakevenEnabled(InpMoveToBreakeven);
|
|
g_trade.SetSlippage(InpSlippage);
|
|
g_trade.SetComment(InpComment);
|
|
|
|
// Initialize dashboard
|
|
if(InpShowDashboard)
|
|
{
|
|
if(!g_dashboard.Initialize(InpDashboardX, InpDashboardY))
|
|
{
|
|
g_utils.Log("Failed to initialize dashboard", LOG_WARNING);
|
|
// Non-critical, continue
|
|
}
|
|
else
|
|
{
|
|
g_dashboard.SetColors(InpDashboardColor, clrRed, clrLime);
|
|
g_dashboard.SetFontSize(InpDashboardFontSize);
|
|
}
|
|
}
|
|
|
|
// Session initialization
|
|
g_session_start = TimeCurrent();
|
|
g_starting_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
g_last_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
g_initialized = true;
|
|
|
|
// Log configuration
|
|
LogConfiguration();
|
|
|
|
// Initial market scan
|
|
UpdateMarketConditions();
|
|
|
|
// Detect any existing external trades
|
|
if(InpManageExternal)
|
|
{
|
|
g_external.DetectExternalTrades();
|
|
int external_count = g_external.GetExternalTradeCount();
|
|
if(external_count > 0)
|
|
{
|
|
g_utils.Log(StringFormat("Found %d external trades to manage", external_count), LOG_INFO);
|
|
}
|
|
}
|
|
|
|
g_utils.Log("ERMT v6.8 initialization complete", LOG_INFO);
|
|
Print("=== Initialization Complete ===");
|
|
|
|
// Set timer for periodic tasks (every 5 seconds)
|
|
EventSetTimer(5);
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
EventKillTimer();
|
|
|
|
string reason_text = GetDeInitReasonText(reason);
|
|
g_utils.Log(StringFormat("EA Deinitializing. Reason: %s", reason_text), LOG_INFO);
|
|
|
|
// Generate final report
|
|
if(g_trade != NULL)
|
|
{
|
|
PerformanceMetrics metrics;
|
|
g_trade.GetPerformanceMetrics(metrics);
|
|
|
|
string report = g_utils.GenerateReportEnhanced(metrics,
|
|
g_external.GetExternalTradeCount(),
|
|
g_external.GetExternalProfit());
|
|
g_utils.Log(report, LOG_INFO);
|
|
|
|
// Save final report to file
|
|
g_utils.SaveToFile(StringFormat("ERMT_FinalReport_%s.txt",
|
|
TimeToString(TimeCurrent(), TIME_DATE)), report);
|
|
}
|
|
|
|
// Clean up
|
|
Cleanup();
|
|
|
|
Comment("");
|
|
Print("=== ERMT v6.8 Shutdown Complete ===");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if(!g_initialized) return;
|
|
|
|
// Check if trading is allowed
|
|
if(!CheckTradingConditions())
|
|
{
|
|
g_trade_allowed = false;
|
|
return;
|
|
}
|
|
g_trade_allowed = true;
|
|
|
|
g_tick_count++;
|
|
g_utils.StartTimer();
|
|
|
|
// Check for new bar
|
|
bool new_bar = false;
|
|
datetime current_bar = iTime(_Symbol, PERIOD_CURRENT, 0);
|
|
if(current_bar != g_last_bar_time)
|
|
{
|
|
g_last_bar_time = current_bar;
|
|
new_bar = true;
|
|
|
|
// New bar processing
|
|
OnNewBar();
|
|
}
|
|
|
|
// Skip if configured to trade only on new bar
|
|
if(InpTradeOnNewBar && !new_bar) return;
|
|
|
|
// Core processing
|
|
ProcessExternalTrades();
|
|
ProcessManagedTrades();
|
|
|
|
// Check for new signals on new bar
|
|
if(new_bar && g_trade_allowed)
|
|
{
|
|
CheckEntrySignals();
|
|
}
|
|
|
|
// Update dashboard on every tick (if shown)
|
|
if(InpShowDashboard)
|
|
{
|
|
UpdateDashboard();
|
|
}
|
|
|
|
// Performance tracking
|
|
double elapsed = g_utils.GetElapsedTime();
|
|
if(elapsed > 100) // Log if processing takes > 100ms
|
|
{
|
|
g_utils.Log(StringFormat("Slow tick processing: %.1f ms", elapsed), LOG_WARNING);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Timer Event |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
if(!g_initialized) return;
|
|
|
|
// Periodic maintenance tasks
|
|
PeriodicTasks();
|
|
|
|
// Check Friday close
|
|
if(InpFridayClose && CheckFridayClose())
|
|
{
|
|
CloseAllPositions("Friday close");
|
|
}
|
|
|
|
// Monitor system health
|
|
CheckSystemHealth();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Trade Event |
|
|
//+------------------------------------------------------------------+
|
|
void OnTrade()
|
|
{
|
|
if(!g_initialized) return;
|
|
|
|
// Update session profit
|
|
double current_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
g_session_profit = current_balance - g_starting_balance;
|
|
|
|
// Track daily trades
|
|
MqlDateTime current_time;
|
|
TimeToStruct(TimeCurrent(), current_time);
|
|
|
|
MqlDateTime last_trade_time;
|
|
TimeToStruct(g_last_trade_time, last_trade_time);
|
|
|
|
if(current_time.day != last_trade_time.day)
|
|
{
|
|
// Reset daily counters
|
|
g_trades_today = 0;
|
|
g_wins_today = 0;
|
|
g_losses_today = 0;
|
|
}
|
|
|
|
g_last_trade_time = TimeCurrent();
|
|
|
|
// Log trade event
|
|
g_utils.Log("Trade event detected", LOG_DEBUG);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| New Bar Processing |
|
|
//+------------------------------------------------------------------+
|
|
void OnNewBar()
|
|
{
|
|
// Update market analysis
|
|
UpdateMarketConditions();
|
|
|
|
// Check daily reset
|
|
MqlDateTime current;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
MqlDateTime session_start;
|
|
TimeToStruct(g_session_start, session_start);
|
|
|
|
if(current.day != session_start.day)
|
|
{
|
|
// New trading day
|
|
g_risk.ResetDailyCounters();
|
|
g_trades_today = 0;
|
|
g_wins_today = 0;
|
|
g_losses_today = 0;
|
|
g_utils.Log("New trading day started", LOG_INFO);
|
|
}
|
|
|
|
// Log periodic status
|
|
if(g_tick_count % 100 == 0)
|
|
{
|
|
LogStatus();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Market Conditions |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateMarketConditions()
|
|
{
|
|
// Analyze current market state
|
|
ENUM_SIGNAL_TYPE signal = g_tech.AnalyzeMarket();
|
|
|
|
// Update volatility
|
|
g_current_volatility = g_utils.GetATR(InpATRPeriod);
|
|
|
|
// Update spread
|
|
g_current_spread = g_utils.CalculateSpread(_Symbol);
|
|
|
|
// Determine market condition
|
|
double trend_strength = g_tech.GetTrendStrength();
|
|
double volatility_ratio = g_tech.GetVolatilityRatio();
|
|
|
|
if(trend_strength > 0.7)
|
|
{
|
|
g_current_market = (signal == SIGNAL_BUY) ? MARKET_TRENDING_UP : MARKET_TRENDING_DOWN;
|
|
}
|
|
else if(trend_strength < 0.3)
|
|
{
|
|
g_current_market = MARKET_RANGING;
|
|
}
|
|
else if(volatility_ratio > 1.5)
|
|
{
|
|
g_current_market = MARKET_VOLATILE;
|
|
}
|
|
else if(g_tech.IsBreakout())
|
|
{
|
|
g_current_market = MARKET_BREAKOUT;
|
|
}
|
|
else if(g_tech.CheckReversal())
|
|
{
|
|
g_current_market = MARKET_REVERSAL;
|
|
}
|
|
else
|
|
{
|
|
g_current_market = MARKET_RANGING;
|
|
}
|
|
|
|
g_utils.Log(StringFormat("Market: %s, Trend: %.2f, Vol: %.5f, Spread: %.1f",
|
|
MarketConditionToString(g_current_market),
|
|
trend_strength, g_current_volatility, g_current_spread), LOG_DEBUG);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process External Trades |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessExternalTrades()
|
|
{
|
|
if(!InpManageExternal) return;
|
|
|
|
// Detect new external trades
|
|
g_external.DetectExternalTrades();
|
|
|
|
// Process existing external trades
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket == 0) continue;
|
|
|
|
if(g_external.IsTicketExternal(ticket))
|
|
{
|
|
// Apply management based on mode
|
|
g_external.ManageExternalTrade(ticket);
|
|
|
|
// Apply trailing if enabled and profitable
|
|
if(InpApplyTrailingExternal && InpTrailingMethod != TRAIL_NONE)
|
|
{
|
|
if(PositionGetDouble(POSITION_PROFIT) > 0)
|
|
{
|
|
double profit_points = PositionGetDouble(POSITION_PROFIT) /
|
|
PositionGetDouble(POSITION_VOLUME);
|
|
|
|
if(profit_points >= InpTrailStart)
|
|
{
|
|
g_external.ApplyTrailingToExternal(ticket, InpTrailDistance);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for breakeven
|
|
if(InpMoveToBreakeven)
|
|
{
|
|
double profit_points = PositionGetDouble(POSITION_PROFIT) /
|
|
PositionGetDouble(POSITION_VOLUME);
|
|
|
|
if(profit_points >= InpBreakevenTrigger)
|
|
{
|
|
g_external.MoveExternalToBreakeven(ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Monitor and clean up
|
|
g_external.MonitorExternalTrades();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process Managed Trades |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessManagedTrades()
|
|
{
|
|
TradeInfo managed_trades[];
|
|
int trade_count = g_trade.GetManagedTrades(managed_trades);
|
|
|
|
for(int i = 0; i < trade_count; i++)
|
|
{
|
|
// Check risk rules
|
|
if(!g_risk.ValidateAndAdjustRisk(managed_trades[i]))
|
|
{
|
|
g_utils.Log(StringFormat("Risk violation for trade %d",
|
|
managed_trades[i].ticket), LOG_WARNING);
|
|
|
|
// Process close request if flagged
|
|
if(managed_trades[i].close_requested)
|
|
{
|
|
g_trade.CloseTrade(managed_trades[i].ticket, managed_trades[i].close_reason);
|
|
continue;
|
|
}
|
|
|
|
// Process partial close if flagged
|
|
if(managed_trades[i].partial_close_volume > 0)
|
|
{
|
|
g_trade.PartialClose(managed_trades[i].ticket,
|
|
managed_trades[i].partial_close_volume,
|
|
managed_trades[i].partial_close_reason);
|
|
}
|
|
}
|
|
|
|
// Apply trailing stop
|
|
if(InpTrailingMethod != TRAIL_NONE && managed_trades[i].profit > 0)
|
|
{
|
|
g_trade.ApplyTrailingStop(managed_trades[i].ticket);
|
|
}
|
|
|
|
// Check for breakeven
|
|
if(InpMoveToBreakeven)
|
|
{
|
|
g_trade.CheckBreakeven(managed_trades[i].ticket);
|
|
}
|
|
|
|
// Monitor for exit signals
|
|
ENUM_SIGNAL_TYPE exit_signal = g_tech.CheckExitSignal(managed_trades[i]);
|
|
if(exit_signal != SIGNAL_NONE)
|
|
{
|
|
// Check if we should close on opposite signal
|
|
if(InpCloseOnOpposite)
|
|
{
|
|
if((managed_trades[i].type == ORDER_TYPE_BUY && exit_signal == SIGNAL_SELL) ||
|
|
(managed_trades[i].type == ORDER_TYPE_SELL && exit_signal == SIGNAL_BUY))
|
|
{
|
|
g_trade.CloseTrade(managed_trades[i].ticket, "Opposite signal");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check for Entry Signals |
|
|
//+------------------------------------------------------------------+
|
|
void CheckEntrySignals()
|
|
{
|
|
// Check if we can open new positions
|
|
if(!g_risk.CanOpenNewPosition())
|
|
{
|
|
g_utils.Log("Cannot open new position - risk limits", LOG_DEBUG);
|
|
return;
|
|
}
|
|
|
|
// Get market signal
|
|
ENUM_SIGNAL_TYPE signal = g_tech.AnalyzeMarket();
|
|
if(signal == SIGNAL_NONE) return;
|
|
|
|
// Check for hedging restrictions
|
|
if(!InpAllowHedging)
|
|
{
|
|
// Check if we have opposite positions
|
|
for(int i = 0; i < PositionsTotal(); i++)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
|
{
|
|
ENUM_ORDER_TYPE pos_type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if((signal == SIGNAL_BUY && pos_type == ORDER_TYPE_SELL) ||
|
|
(signal == SIGNAL_SELL && pos_type == ORDER_TYPE_BUY))
|
|
{
|
|
g_utils.Log("Signal rejected - hedging not allowed", LOG_DEBUG);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare trade request
|
|
MqlTradeRequest request = {};
|
|
request.symbol = _Symbol;
|
|
request.type = (signal == SIGNAL_BUY) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
|
request.comment = InpComment;
|
|
request.magic = InpMagicNumber;
|
|
|
|
// Calculate position size
|
|
double atr = g_utils.GetATR(InpATRPeriod);
|
|
double stop_distance = atr * InpATRMultiplierSL;
|
|
double lot_size = g_risk.CalculatePositionSize(_Symbol, stop_distance, InpSizingMethod);
|
|
|
|
if(lot_size == 0)
|
|
{
|
|
g_utils.Log("Position size calculation returned 0", LOG_WARNING);
|
|
return;
|
|
}
|
|
|
|
request.volume = lot_size;
|
|
|
|
// Calculate stops
|
|
double entry_price = (signal == SIGNAL_BUY) ?
|
|
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
double sl = (signal == SIGNAL_BUY) ?
|
|
entry_price - stop_distance :
|
|
entry_price + stop_distance;
|
|
|
|
double tp = 0;
|
|
if(InpTPMethod == TP_FIXED_RR)
|
|
{
|
|
double tp_distance = stop_distance * InpTPRatio;
|
|
tp = (signal == SIGNAL_BUY) ?
|
|
entry_price + tp_distance :
|
|
entry_price - tp_distance;
|
|
}
|
|
else if(InpTPMethod == TP_ATR_BASED)
|
|
{
|
|
double tp_distance = atr * InpATRMultiplierTP;
|
|
tp = (signal == SIGNAL_BUY) ?
|
|
entry_price + tp_distance :
|
|
entry_price - tp_distance;
|
|
}
|
|
else if(InpTPMethod == TP_TECHNICAL_LEVEL)
|
|
{
|
|
tp = g_tech.GetNearestLevel(entry_price, signal == SIGNAL_BUY);
|
|
}
|
|
|
|
request.price = entry_price;
|
|
request.sl = g_utils.NormalizePrice(sl, _Symbol);
|
|
request.tp = g_utils.NormalizePrice(tp, _Symbol);
|
|
|
|
// Final validation with risk manager
|
|
if(!g_risk.ValidateNewPosition(_Symbol, lot_size, stop_distance))
|
|
{
|
|
g_utils.Log("Trade rejected by risk manager", LOG_INFO);
|
|
return;
|
|
}
|
|
|
|
// Execute trade
|
|
ulong ticket = g_trade.OpenTrade(request);
|
|
if(ticket > 0)
|
|
{
|
|
g_trades_today++;
|
|
|
|
string message = StringFormat("Trade opened: #%d %s %s %.2f @ %.5f SL=%.5f TP=%.5f",
|
|
ticket,
|
|
(signal == SIGNAL_BUY) ? "BUY" : "SELL",
|
|
_Symbol, lot_size, entry_price, sl, tp);
|
|
g_utils.Log(message, LOG_INFO);
|
|
|
|
// Send alerts if enabled
|
|
if(InpEmailAlerts) SendMail("ERMT Trade", message);
|
|
if(InpPushAlerts) SendNotification(message);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Periodic Maintenance Tasks |
|
|
//+------------------------------------------------------------------+
|
|
void PeriodicTasks()
|
|
{
|
|
// Update performance metrics
|
|
PerformanceMetrics metrics;
|
|
g_trade.GetPerformanceMetrics(metrics);
|
|
|
|
// Update risk manager with performance
|
|
g_risk.UpdatePerformanceMetrics(metrics.win_rate,
|
|
metrics.average_win,
|
|
metrics.average_loss);
|
|
|
|
// Log periodic report
|
|
static int report_counter = 0;
|
|
report_counter++;
|
|
if(report_counter >= 120) // Every 10 minutes (5 sec timer * 120)
|
|
{
|
|
report_counter = 0;
|
|
g_utils.LogPerformance(metrics);
|
|
}
|
|
|
|
// Check daily loss limit
|
|
if(g_risk.IsDailyLossLimitReached())
|
|
{
|
|
g_utils.Log("Daily loss limit reached - stopping trading", LOG_WARNING);
|
|
g_trade_allowed = false;
|
|
|
|
// Close all positions if critical
|
|
if(g_risk.GetDailyLoss() > g_risk.GetCurrentDrawdown() * 1.5)
|
|
{
|
|
CloseAllPositions("Daily loss limit exceeded");
|
|
}
|
|
}
|
|
|
|
// Clean up old data
|
|
g_trade.CleanupHistory();
|
|
|
|
// Save performance snapshot
|
|
static int save_counter = 0;
|
|
save_counter++;
|
|
if(save_counter >= 720) // Every hour
|
|
{
|
|
save_counter = 0;
|
|
SavePerformanceSnapshot(metrics);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Dashboard Display |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateDashboard()
|
|
{
|
|
if(g_dashboard == NULL) return;
|
|
|
|
// Get current metrics
|
|
PerformanceMetrics metrics;
|
|
g_trade.GetPerformanceMetrics(metrics);
|
|
|
|
// Prepare dashboard data
|
|
DashboardData data;
|
|
data.balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
data.equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
data.free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
|
|
data.profit = metrics.total_profit;
|
|
data.positions = g_trade.GetOpenTradeCount();
|
|
data.win_rate = metrics.win_rate;
|
|
data.profit_factor = metrics.profit_factor;
|
|
data.drawdown = metrics.current_drawdown;
|
|
data.daily_profit = g_session_profit;
|
|
data.external_trades = g_external.GetExternalTradeCount();
|
|
data.external_profit = g_external.GetExternalProfit();
|
|
data.risk_status = g_risk.GetCurrentRiskLevel();
|
|
data.market_condition = MarketConditionToString(g_current_market);
|
|
|
|
// Additional info
|
|
data.spread = g_current_spread;
|
|
data.daily_trades = g_trades_today;
|
|
|
|
// Update dashboard
|
|
g_dashboard.Update(data);
|
|
|
|
// Show comment if dashboard not available
|
|
if(!InpShowDashboard)
|
|
{
|
|
string comment = StringFormat(
|
|
"ERMT v6.8 | Balance: %.2f | Equity: %.2f | DD: %.2f%% | Trades: %d | P/L: %.2f",
|
|
data.balance, data.equity, data.drawdown, data.positions, data.profit
|
|
);
|
|
Comment(comment);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Helper Functions |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Validate Input Parameters |
|
|
//+------------------------------------------------------------------+
|
|
bool ValidateInputs()
|
|
{
|
|
if(InpMaxRiskPerTrade <= 0 || InpMaxRiskPerTrade > 10)
|
|
{
|
|
Print("Invalid MaxRiskPerTrade: must be between 0 and 10");
|
|
return false;
|
|
}
|
|
|
|
if(InpMaxDailyLoss <= 0 || InpMaxDailyLoss > 20)
|
|
{
|
|
Print("Invalid MaxDailyLoss: must be between 0 and 20");
|
|
return false;
|
|
}
|
|
|
|
if(InpMaxDrawdown <= 0 || InpMaxDrawdown > 50)
|
|
{
|
|
Print("Invalid MaxDrawdown: must be between 0 and 50");
|
|
return false;
|
|
}
|
|
|
|
if(InpMaxPositions <= 0 || InpMaxPositions > 10)
|
|
{
|
|
Print("Invalid MaxPositions: must be between 1 and 10");
|
|
return false;
|
|
}
|
|
|
|
if(InpRiskRewardMin < 0.5 || InpRiskRewardMin > 10)
|
|
{
|
|
Print("Invalid RiskRewardMin: must be between 0.5 and 10");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Trading Conditions |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckTradingConditions()
|
|
{
|
|
// Check if auto trading is enabled
|
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
|
|
return false;
|
|
|
|
// Check if EA trading is enabled
|
|
if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
|
|
return false;
|
|
|
|
// Check if market is open
|
|
if(!g_utils.IsMarketOpen())
|
|
return false;
|
|
|
|
// Check trading hours
|
|
if(InpUseTradeHours)
|
|
{
|
|
MqlDateTime current;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
|
|
if(current.hour < InpStartHour || current.hour >= InpEndHour)
|
|
return false;
|
|
}
|
|
|
|
// Check spread
|
|
double max_spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 3;
|
|
if(g_current_spread > max_spread)
|
|
{
|
|
g_utils.Log(StringFormat("Spread too high: %.1f > %.1f",
|
|
g_current_spread, max_spread), LOG_DEBUG);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Friday Close |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckFridayClose()
|
|
{
|
|
MqlDateTime current;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
|
|
if(current.day_of_week == 5 && current.hour >= InpFridayCloseHour)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close All Positions |
|
|
//+------------------------------------------------------------------+
|
|
void CloseAllPositions(string reason)
|
|
{
|
|
g_utils.Log(StringFormat("Closing all positions: %s", reason), LOG_WARNING);
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket == 0) continue;
|
|
|
|
if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
|
{
|
|
g_trade.CloseTrade(ticket, reason);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check System Health |
|
|
//+------------------------------------------------------------------+
|
|
void CheckSystemHealth()
|
|
{
|
|
// Check connection
|
|
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
|
|
{
|
|
g_utils.Log("Terminal disconnected", LOG_ERROR);
|
|
return;
|
|
}
|
|
|
|
// Check memory usage
|
|
int memory_used = TerminalInfoInteger(TERMINAL_MEMORY_USED);
|
|
int memory_available = TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE);
|
|
|
|
if(memory_available < 100) // Less than 100MB available
|
|
{
|
|
g_utils.Log(StringFormat("Low memory warning: %d MB available", memory_available), LOG_WARNING);
|
|
}
|
|
|
|
// Check CPU usage (simplified)
|
|
if(g_tick_count > 0 && g_utils.GetElapsedTime() > 500)
|
|
{
|
|
g_utils.Log("High CPU usage detected", LOG_WARNING);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log Configuration |
|
|
//+------------------------------------------------------------------+
|
|
void LogConfiguration()
|
|
{
|
|
g_utils.Log("=== ERMT v6.8 Configuration ===", LOG_INFO);
|
|
g_utils.Log(StringFormat("Symbol: %s", _Symbol), LOG_INFO);
|
|
g_utils.Log(StringFormat("Timeframe: %s", g_utils.TimeframeToString(Period())), LOG_INFO);
|
|
g_utils.Log(StringFormat("Magic Number: %d", InpMagicNumber), LOG_INFO);
|
|
g_utils.Log(StringFormat("Risk per trade: %.2f%%", InpMaxRiskPerTrade), LOG_INFO);
|
|
g_utils.Log(StringFormat("Max daily loss: %.2f%%", InpMaxDailyLoss), LOG_INFO);
|
|
g_utils.Log(StringFormat("Max drawdown: %.2f%%", InpMaxDrawdown), LOG_INFO);
|
|
g_utils.Log(StringFormat("Max positions: %d", InpMaxPositions), LOG_INFO);
|
|
g_utils.Log(StringFormat("Sizing method: %d", InpSizingMethod), LOG_INFO);
|
|
g_utils.Log(StringFormat("Manage external: %s", InpManageExternal ? "Yes" : "No"), LOG_INFO);
|
|
g_utils.Log(StringFormat("Starting balance: %.2f", g_starting_balance), LOG_INFO);
|
|
g_utils.Log("================================", LOG_INFO);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log Status |
|
|
//+------------------------------------------------------------------+
|
|
void LogStatus()
|
|
{
|
|
PerformanceMetrics metrics;
|
|
g_trade.GetPerformanceMetrics(metrics);
|
|
|
|
g_utils.Log(StringFormat(
|
|
"Status: Bal=%.2f Eq=%.2f Pos=%d/%d Risk=%.2f%% DD=%.2f%% P/L=%.2f",
|
|
AccountInfoDouble(ACCOUNT_BALANCE),
|
|
AccountInfoDouble(ACCOUNT_EQUITY),
|
|
g_trade.GetOpenTradeCount(),
|
|
InpMaxPositions,
|
|
g_risk.GetCurrentRiskLevel(),
|
|
g_risk.GetCurrentDrawdown(),
|
|
g_session_profit
|
|
), LOG_INFO);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Save Performance Snapshot |
|
|
//+------------------------------------------------------------------+
|
|
void SavePerformanceSnapshot(const PerformanceMetrics &metrics)
|
|
{
|
|
string filename = StringFormat("ERMT_Snapshot_%s.csv",
|
|
TimeToString(TimeCurrent(), TIME_DATE));
|
|
|
|
// Prepare CSV content
|
|
string content = StringFormat(
|
|
"%s,%.2f,%.2f,%.2f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%.2f\n",
|
|
TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES),
|
|
AccountInfoDouble(ACCOUNT_BALANCE),
|
|
AccountInfoDouble(ACCOUNT_EQUITY),
|
|
metrics.total_profit,
|
|
metrics.total_trades,
|
|
metrics.winning_trades,
|
|
metrics.losing_trades,
|
|
metrics.win_rate,
|
|
metrics.profit_factor,
|
|
metrics.max_drawdown,
|
|
metrics.sharpe_ratio,
|
|
g_risk.GetCurrentRiskLevel()
|
|
);
|
|
|
|
// Append to file
|
|
int handle = FileOpen(filename, FILE_READ|FILE_WRITE|FILE_CSV);
|
|
if(handle != INVALID_HANDLE)
|
|
{
|
|
FileSeek(handle, 0, SEEK_END);
|
|
FileWriteString(handle, content);
|
|
FileClose(handle);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Market Condition to String |
|
|
//+------------------------------------------------------------------+
|
|
string MarketConditionToString(ENUM_MARKET_CONDITION condition)
|
|
{
|
|
switch(condition)
|
|
{
|
|
case MARKET_TRENDING_UP: return "Trending Up";
|
|
case MARKET_TRENDING_DOWN: return "Trending Down";
|
|
case MARKET_RANGING: return "Ranging";
|
|
case MARKET_VOLATILE: return "Volatile";
|
|
case MARKET_QUIET: return "Quiet";
|
|
case MARKET_BREAKOUT: return "Breakout";
|
|
case MARKET_REVERSAL: return "Reversal";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Deinitialization Reason Text |
|
|
//+------------------------------------------------------------------+
|
|
string GetDeInitReasonText(int reason)
|
|
{
|
|
switch(reason)
|
|
{
|
|
case REASON_PROGRAM: return "Program terminated";
|
|
case REASON_REMOVE: return "Removed from chart";
|
|
case REASON_RECOMPILE: return "Recompiled";
|
|
case REASON_CHARTCHANGE: return "Chart changed";
|
|
case REASON_CHARTCLOSE: return "Chart closed";
|
|
case REASON_PARAMETERS: return "Parameters changed";
|
|
case REASON_ACCOUNT: return "Account changed";
|
|
case REASON_TEMPLATE: return "Template applied";
|
|
case REASON_INITFAILED: return "Initialization failed";
|
|
case REASON_CLOSE: return "Terminal closed";
|
|
default: return StringFormat("Unknown (%d)", reason);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Cleanup Function |
|
|
//+------------------------------------------------------------------+
|
|
void Cleanup()
|
|
{
|
|
if(g_dashboard != NULL)
|
|
{
|
|
delete g_dashboard;
|
|
g_dashboard = NULL;
|
|
}
|
|
|
|
if(g_trade != NULL)
|
|
{
|
|
delete g_trade;
|
|
g_trade = NULL;
|
|
}
|
|
|
|
if(g_external != NULL)
|
|
{
|
|
delete g_external;
|
|
g_external = NULL;
|
|
}
|
|
|
|
if(g_tech != NULL)
|
|
{
|
|
delete g_tech;
|
|
g_tech = NULL;
|
|
}
|
|
|
|
if(g_risk != NULL)
|
|
{
|
|
delete g_risk;
|
|
g_risk = NULL;
|
|
}
|
|
|
|
if(g_utils != NULL)
|
|
{
|
|
g_utils.CloseExternalLog();
|
|
delete g_utils;
|
|
g_utils = NULL;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+ |