854 lines
No EOL
32 KiB
MQL5
854 lines
No EOL
32 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Risk Management Overlay EA v6.7 |
|
|
//| Modular Institutional Grade System |
|
|
//| Main Coordinator File - Fixed |
|
|
//| Compilation Errors Resolved |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Institutional Risk Management System v6.7"
|
|
#property version "6.70"
|
|
#property strict
|
|
#property description "Modular risk management system with advanced monitoring"
|
|
|
|
//--- Include base MQL5 classes first
|
|
#include <Object.mqh>
|
|
#include <Trade/Trade.mqh>
|
|
|
|
//--- Include system modules
|
|
#include "Modules/DataTypes.mqh"
|
|
#include "Modules/Utilities.mqh"
|
|
#include "Modules/RiskManager.mqh"
|
|
#include "Modules/TradeManager.mqh"
|
|
#include "Modules/TechnicalAnalysis.mqh"
|
|
#include "Modules/EntrySystem.mqh"
|
|
#include "Modules/Dashboard.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)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| GLOBAL OBJECTS |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//--- Module instances
|
|
CRiskManager *RiskMgr;
|
|
CTradeManager *TradeMgr;
|
|
CEntrySystem *EntrySys;
|
|
CTechnicalAnalysis *TechAnalysis;
|
|
CDashboard *Dashboard;
|
|
CUtilities *Utils;
|
|
|
|
//--- System state
|
|
SystemState g_SystemState;
|
|
PerformanceMetrics g_Performance;
|
|
//MarketConditions g_MarketConditions;
|
|
|
|
//--- Trade tracking
|
|
ManagedTrade g_ManagedTrades[];
|
|
int g_TotalTrades = 0;
|
|
|
|
//--- Timing variables
|
|
datetime g_LastUpdateTime = 0;
|
|
datetime g_LastTradeTime = 0;
|
|
datetime g_LastReportTime = 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 CDashboard();
|
|
Utils = new CUtilities();
|
|
|
|
//--- 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...");
|
|
|
|
// Wait for sufficient bars
|
|
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);
|
|
}
|
|
|
|
// Validate ATR is working
|
|
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);
|
|
|
|
//--- Load existing positions
|
|
LoadExistingPositions();
|
|
|
|
//--- Create dashboard
|
|
if(ShowDashboard)
|
|
{
|
|
(*Dashboard).Create(DashboardX, DashboardY, DashboardColor, ProfitColor, LossColor);
|
|
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
|
|
}
|
|
|
|
//--- Set timer for periodic updates
|
|
EventSetTimer(UpdateFrequency);
|
|
|
|
//--- Log initialization
|
|
(*Utils).Log(LOG_INFO, StringFormat("Risk Management Overlay v6.7 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)
|
|
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
|
|
|
|
//--- Cleanup modules
|
|
if(Dashboard != NULL && ShowDashboard)
|
|
(*Dashboard).Destroy();
|
|
|
|
//--- Delete module instances
|
|
delete RiskMgr;
|
|
delete TradeMgr;
|
|
delete EntrySys;
|
|
delete TechAnalysis;
|
|
delete Dashboard;
|
|
delete Utils;
|
|
|
|
//--- Log shutdown
|
|
Print("Risk Management Overlay shutdown: ", 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
|
|
if(ManageExternalTrades)
|
|
CheckForNewExternalTrades();
|
|
|
|
//--- 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();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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();
|
|
|
|
for(int i = 0; i < total; i++)
|
|
{
|
|
string symbol = PositionGetSymbol(i);
|
|
if(symbol != _Symbol)
|
|
continue;
|
|
|
|
if(PositionSelect(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)
|
|
{
|
|
ManagedTrade trade;
|
|
if(CreateManagedTrade(trade, PositionGetInteger(POSITION_TICKET)))
|
|
{
|
|
AddManagedTrade(trade);
|
|
(*Utils).Log(LOG_INFO, StringFormat("Loaded position #%d into management",
|
|
trade.ticket));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update market conditions - ENHANCED VERSION |
|
|
//+------------------------------------------------------------------+
|
|
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]);
|
|
RemoveManagedTrade(i);
|
|
continue;
|
|
}
|
|
|
|
//--- Update trade information
|
|
UpdateTradeInfo(g_ManagedTrades[i]);
|
|
|
|
//--- Apply risk management - PROPERLY CALLING MODULE METHOD
|
|
(*RiskMgr).ValidateAndAdjustRisk(g_ManagedTrades[i], MaxRiskPercent);
|
|
|
|
//--- Apply trade management
|
|
(*TradeMgr).ManageTrade(g_ManagedTrades[i], g_MarketConditions);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check for new external trades |
|
|
//+------------------------------------------------------------------+
|
|
void CheckForNewExternalTrades()
|
|
{
|
|
int total = PositionsTotal();
|
|
|
|
for(int i = 0; i < total; i++)
|
|
{
|
|
string symbol = PositionGetSymbol(i);
|
|
if(symbol != _Symbol)
|
|
continue;
|
|
|
|
if(PositionSelect(symbol))
|
|
{
|
|
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
|
ulong magic = PositionGetInteger(POSITION_MAGIC);
|
|
|
|
//--- Skip if already managed or wrong magic
|
|
if(IsTradeManaged(ticket))
|
|
continue;
|
|
|
|
if(magic == TradingMagic)
|
|
continue; // Our own trade
|
|
|
|
if(MagicNumberFilter != 0 && magic != MagicNumberFilter)
|
|
continue;
|
|
|
|
//--- Create managed trade entry
|
|
ManagedTrade trade;
|
|
if(CreateManagedTrade(trade, ticket))
|
|
{
|
|
//--- Apply risk rules to external trade
|
|
if(ForceRiskRules)
|
|
{
|
|
bool keep_trade = (*RiskMgr).EnforceRiskRules(trade, DefaultExternalRisk, CloseExcessiveRisk);
|
|
if(!keep_trade && CloseExcessiveRisk)
|
|
{
|
|
// Close the excessive risk trade
|
|
(*TradeMgr).ClosePosition(ticket);
|
|
(*Utils).Log(LOG_WARNING, StringFormat("Closed excessive risk external trade #%d", ticket));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
AddManagedTrade(trade);
|
|
(*Utils).Log(LOG_INFO, StringFormat("Added external trade #%d to management", ticket));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check entry signals - ENHANCED VERSION WITH DEBUGGING |
|
|
//+------------------------------------------------------------------+
|
|
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;
|
|
}
|
|
|
|
//--- Debug market conditions BEFORE signal check
|
|
if(EnableLogging)
|
|
{
|
|
Print("=== Market Conditions Debug ===");
|
|
Print("Market Volatility (ATR): ", g_MarketConditions.volatility);
|
|
Print("Market Trend Strength: ", g_MarketConditions.trend_strength);
|
|
Print("Market Condition: ", EnumToString(g_MarketConditions.condition));
|
|
}
|
|
|
|
// Force update market conditions if ATR is invalid
|
|
if(g_MarketConditions.volatility <= 0)
|
|
{
|
|
Print("WARNING: Market volatility is 0, forcing update");
|
|
UpdateMarketConditions();
|
|
Print("After update - Market Volatility: ", g_MarketConditions.volatility);
|
|
}
|
|
|
|
//--- Get entry signal
|
|
EntrySignal signal = (*EntrySys).CheckSignal(g_MarketConditions);
|
|
|
|
if(EnableLogging && signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
(*Utils).Log(LOG_DEBUG, StringFormat("Signal found: Type=%d, Confidence=%.1f, Comment=%s",
|
|
signal.signal_type, signal.confidence, signal.comment));
|
|
}
|
|
|
|
if(signal.signal_type != SIGNAL_NONE)
|
|
{
|
|
// Debug logging
|
|
if(EnableLogging)
|
|
{
|
|
Print("=== Signal Debug Info ===");
|
|
Print("Signal Type: ", signal.comment);
|
|
Print("Market Volatility (ATR): ", g_MarketConditions.volatility);
|
|
Print("Stop Distance: ", signal.stop_loss_distance, " (",
|
|
signal.stop_loss_distance / _Point, " points)");
|
|
Print("TP Distance: ", signal.take_profit_distance, " (",
|
|
signal.take_profit_distance / _Point, " points)");
|
|
Print("Current Spread: ", SymbolInfoInteger(_Symbol, SYMBOL_SPREAD), " points");
|
|
Print("Min Stop Level: ", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL), " points");
|
|
Print("======================");
|
|
}
|
|
|
|
// Additional safety check
|
|
if(signal.stop_loss_distance <= 0)
|
|
{
|
|
(*Utils).Log(LOG_ERROR, StringFormat("Zero stop distance detected! Signal: %s, ATR: %.5f",
|
|
signal.comment, g_MarketConditions.volatility));
|
|
|
|
// Emergency fallback
|
|
double emergency_stop = 50 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 50 points
|
|
signal.stop_loss_distance = emergency_stop;
|
|
signal.take_profit_distance = emergency_stop * 2; // 2:1 RR
|
|
|
|
(*Utils).Log(LOG_WARNING, StringFormat("Applied emergency stop: %.5f", emergency_stop));
|
|
}
|
|
|
|
//--- Calculate position size using RiskManager
|
|
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;
|
|
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));
|
|
}
|
|
}
|
|
else if(EnableLogging)
|
|
{
|
|
(*Utils).Log(LOG_WARNING, "Failed to open position - check logs for details");
|
|
}
|
|
}
|
|
else if(EnableLogging && lotSize <= 0)
|
|
{
|
|
(*Utils).Log(LOG_WARNING, "Invalid lot size calculated - risk limits may be exceeded");
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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();
|
|
}
|
|
|
|
//--- Update risk manager performance metrics
|
|
if(g_Performance.total_trades > 0)
|
|
{
|
|
double avg_win = (g_Performance.winning_trades > 0) ?
|
|
g_Performance.gross_profit / g_Performance.winning_trades : 0;
|
|
double avg_loss = (g_Performance.losing_trades > 0) ?
|
|
g_Performance.gross_loss / g_Performance.losing_trades : 0;
|
|
|
|
(*RiskMgr).UpdatePerformanceMetrics(g_Performance.winning_trades,
|
|
g_Performance.losing_trades,
|
|
avg_win, avg_loss);
|
|
}
|
|
|
|
//--- Generate periodic reports
|
|
if(TimeCurrent() - g_LastReportTime > 3600) // Hourly
|
|
{
|
|
(*Utils).GenerateReport(g_Performance, g_ManagedTrades);
|
|
g_LastReportTime = TimeCurrent();
|
|
}
|
|
|
|
//--- Check for news events
|
|
if((*Utils).IsNewsTime())
|
|
{
|
|
g_MarketConditions.news_event = true;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Create managed trade structure |
|
|
//+------------------------------------------------------------------+
|
|
bool CreateManagedTrade(ManagedTrade &trade, ulong ticket)
|
|
{
|
|
if(!PositionSelectByTicket(ticket))
|
|
return false;
|
|
|
|
trade.ticket = ticket;
|
|
trade.symbol = PositionGetString(POSITION_SYMBOL);
|
|
trade.type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
trade.open_time = (datetime)PositionGetInteger(POSITION_TIME);
|
|
trade.open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
trade.volume = PositionGetDouble(POSITION_VOLUME);
|
|
trade.current_volume = trade.volume;
|
|
trade.sl = PositionGetDouble(POSITION_SL);
|
|
trade.tp = PositionGetDouble(POSITION_TP);
|
|
trade.magic = PositionGetInteger(POSITION_MAGIC);
|
|
trade.profit = PositionGetDouble(POSITION_PROFIT);
|
|
trade.commission = 0; // Will be calculated from deal history if needed
|
|
trade.swap = PositionGetDouble(POSITION_SWAP);
|
|
|
|
//--- Initialize management fields
|
|
trade.be_activated = false;
|
|
trade.trailing_activated = false;
|
|
trade.partial_count = 0;
|
|
trade.risk_amount = (*RiskMgr).CalculateTradeRisk(trade); // PROPERLY CALLING MODULE METHOD
|
|
trade.max_profit = 0;
|
|
trade.max_drawdown = 0;
|
|
trade.management_start = TimeCurrent();
|
|
|
|
//--- Set entry reason
|
|
if(trade.magic == TradingMagic)
|
|
trade.entry_reason = "EA Signal";
|
|
else
|
|
trade.entry_reason = "External Trade";
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Add trade to managed array |
|
|
//+------------------------------------------------------------------+
|
|
void AddManagedTrade(ManagedTrade &trade)
|
|
{
|
|
int size = ArraySize(g_ManagedTrades);
|
|
ArrayResize(g_ManagedTrades, size + 1);
|
|
g_ManagedTrades[size] = trade;
|
|
g_TotalTrades++;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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 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;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Reset daily metrics |
|
|
//+------------------------------------------------------------------+
|
|
void ResetDailyMetrics()
|
|
{
|
|
g_Performance.daily_profit = 0;
|
|
g_Performance.daily_trades = 0;
|
|
g_Performance.daily_volume = 0;
|
|
|
|
//--- Reset risk manager daily metrics
|
|
(*RiskMgr).ResetDailyMetrics();
|
|
|
|
(*Utils).Log(LOG_INFO, "Daily metrics reset");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function for periodic updates |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
//--- Update dashboard if needed
|
|
if(ShowDashboard)
|
|
{
|
|
(*Dashboard).Update(g_Performance, g_ManagedTrades, g_MarketConditions);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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));
|
|
}
|
|
}
|
|
} |