1115 lines
No EOL
39 KiB
MQL5
1115 lines
No EOL
39 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 = clrAqua; // Dashboard Text Color
|
|
input color InpDashboardBgColor = clrBlack; // Dashboard Background 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);
|
|
g_dashboard.m_background_color = InpDashboardBgColor;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+ |