2026-03-24 23:54:05 +00:00
|
|
|
///+------------------------------------------------------------------+
|
|
|
|
|
//| ERMT_PME.mq5 v1.5 |
|
|
|
|
|
//| Enhanced Risk Management Tool - PME |
|
|
|
|
|
//| Pure Management Edition - No Entry, Exit Only |
|
|
|
|
|
//| |
|
|
|
|
|
//| v1.5 CHANGES (vs v1.4): |
|
|
|
|
|
//| - ATR-based partial triggers (volatility adaptive) |
|
|
|
|
|
//| - Retracement room buffer (prevents whipsaw closes) |
|
|
|
|
|
//| - Safe/Aggressive ATR multiplier profiles |
|
|
|
|
|
//| - Dashboard shows ATR trigger vs current profit |
|
|
|
|
|
//| - Maintains v1.4 phase locks and trailing stops |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
#property copyright "ERMT PME v1.5 - ATR-Adaptive Partials"
|
|
|
|
|
#property link "https://github.com/ERMT"
|
|
|
|
|
#property version "1.5"
|
|
|
|
|
#property description "Professional position management - ATR-adaptive partials with retracement protection"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| SYSTEM CONFIGURATION FLAGS (MUST BE BEFORE INCLUDES) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
// Control which profit protection systems are active
|
|
|
|
|
// Set to false to disable a system entirely (cleaner than commenting out code)
|
|
|
|
|
// IMPORTANT: These must be defined BEFORE including modules
|
|
|
|
|
|
|
|
|
|
#define USE_BREAKEVEN_SYSTEM false // DISABLED - Replaced by phase-based profit locking
|
|
|
|
|
// Reason: Phase locks provide superior protection
|
|
|
|
|
// - Breakeven: 8pts fixed lock at 40pts (20% efficiency)
|
|
|
|
|
// - Phase locks: 10-14pts adaptive at 40pts (35% efficiency)
|
|
|
|
|
// - Conflict: Both trigger at same 40pts level
|
|
|
|
|
|
|
|
|
|
#define USE_PHASE_LOCKING true // ENABLED - Safety floor protection system (v1.3)
|
|
|
|
|
// Floor-only locks: 10→200pts minimum guarantees
|
|
|
|
|
// NO dynamic tightening or breathing room reduction
|
|
|
|
|
// Maintains highest phase achieved as safety net
|
|
|
|
|
|
|
|
|
|
#define USE_PARTIAL_CLOSURES true // ENABLED - Primary profit-taking mechanism
|
|
|
|
|
// Banks 75% profit by 150pts (v1.3: earlier triggers)
|
|
|
|
|
// Creates 10% runner with ultra-wide trail
|
|
|
|
|
// Optimized schedule for faster profit realization
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Include Modules (AFTER configuration flags) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "Modules_PME/DataTypes_PME.mqh"
|
|
|
|
|
#include "Modules_PME/Utilities_PME.mqh"
|
|
|
|
|
#include "Modules_PME/RiskManager_PME.mqh"
|
|
|
|
|
//Original - may work with Galileo FX
|
|
|
|
|
//#include "Modules_PME/TechnicalAnalysis_PME.mqh"
|
|
|
|
|
//#include "Modules_PME/PositionManager_PME.mqh"
|
|
|
|
|
// Fix for premature exits
|
|
|
|
|
#include "Modules_PME/TechnicalAnalysis_PME_Merged.mqh"
|
|
|
|
|
#include "Modules_PME/PositionManager_PME_Complete.mqh"
|
|
|
|
|
#include "Modules_PME/ProfitMaximizer_PME.mqh"
|
|
|
|
|
|
|
|
|
|
enum ENUM_PARTIAL_ENGINE
|
|
|
|
|
{
|
|
|
|
|
PARTIAL_ENGINE_PROFIT_MAX = 0,
|
|
|
|
|
PARTIAL_ENGINE_POSITION_MANAGER = 1
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-26 17:02:22 +00:00
|
|
|
enum ENUM_PARTIAL_MODE
|
|
|
|
|
{
|
|
|
|
|
PARTIAL_MODE_FIXED = 0, // Fixed points only (no ATR)
|
|
|
|
|
PARTIAL_MODE_ATR = 1, // ATR-only (recommended for v1.5)
|
|
|
|
|
PARTIAL_MODE_HYBRID = 2 // ATR preferred with fixed fallback (default; backward-compat)
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-24 23:54:05 +00:00
|
|
|
enum ENUM_STARTUP_PROFILE
|
|
|
|
|
{
|
|
|
|
|
STARTUP_PROFILE_CUSTOM = 0,
|
|
|
|
|
STARTUP_PROFILE_SAFE = 1,
|
|
|
|
|
STARTUP_PROFILE_AGGRESSIVE = 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Input Parameters - v1.4 Streamlined Interface |
|
|
|
|
|
//| |
|
|
|
|
|
//| DESIGN: Minimal, profile-driven configuration |
|
|
|
|
|
//| - One-switch startup profile controls core behavior |
|
|
|
|
|
//| - Only essential parameters exposed |
|
|
|
|
|
//| - All disabled systems removed |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Management Control |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== MANAGEMENT CONTROL ==="
|
|
|
|
|
input int InpMagicFilter = 0; // Magic Filter (0 = manage all trades)
|
|
|
|
|
|
|
|
|
|
input group "=== STARTUP PROFILE ==="
|
|
|
|
|
input ENUM_STARTUP_PROFILE InpStartupProfile = STARTUP_PROFILE_SAFE; // Profile: Safe/Aggressive/Custom
|
|
|
|
|
input string InpProfileGuide = "Safe=conservative ATR partials | Aggressive=earlier ATR partials | Custom=manual values";
|
|
|
|
|
|
|
|
|
|
input group "=== PROFILE NOTE ==="
|
|
|
|
|
input string InpCustomGuide = "Trail/partial inputs below are fully applied only when profile=Custom";
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Phase Configuration |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== PHASE CONFIGURATION ==="
|
|
|
|
|
input double InpPhase1Trigger = 40; // Phase 1: Protection threshold (points)
|
|
|
|
|
input double InpPhase2Trigger = 60; // Phase 2: Accumulation threshold (points)
|
|
|
|
|
input double InpPhase3Trigger = 100; // Phase 3: Maximization threshold (points)
|
|
|
|
|
input double InpPhase4Trigger = 200; // Phase 4: Runner threshold (points)
|
|
|
|
|
input double InpPhase5Trigger = 400; // Phase 5: Extreme threshold (points)
|
|
|
|
|
|
|
|
|
|
input group "=== PHASE SAFETY FLOORS ==="
|
|
|
|
|
input double InpPhase1MinLock = 10; // Protection phase minimum stop (points)
|
|
|
|
|
input double InpPhase2MinLock = 25; // Accumulation phase minimum stop (points)
|
|
|
|
|
input double InpPhase3MinLock = 50; // Maximization phase minimum stop (points)
|
|
|
|
|
input double InpPhase4MinLock = 100; // Runner phase minimum stop (points)
|
|
|
|
|
input double InpPhase5MinLock = 200; // Extreme phase minimum stop (points)
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Risk Protection |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== RISK LIMITS ==="
|
|
|
|
|
input double InpMaxLossPerTrade = 3.0; // Max loss per trade (%)
|
|
|
|
|
input double InpMaxDailyLoss = 10.0; // Max daily loss (%)
|
|
|
|
|
input double InpMaxDrawdown = 50.0; // Max account drawdown (%)
|
|
|
|
|
input double InpMaxCorrelation = 0.80; // Max correlation between positions
|
|
|
|
|
|
|
|
|
|
input group "=== EMERGENCY PROTECTION ==="
|
|
|
|
|
input bool InpApplyEmergencyStops = true; // Apply emergency stops on entry
|
|
|
|
|
input double InpEmergencySLMultiplier = 7.0; // Emergency SL multiplier (ATR)
|
|
|
|
|
input double InpEmergencyTPMultiplier = 4.0; // Emergency TP multiplier (ATR)
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Trailing Stop Strategy |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== TRAILING STOP SETTINGS ==="
|
|
|
|
|
input ENUM_TRAILING_METHOD InpTrailingMethod = TRAIL_ATR; // Method: ATR-based (recommended)
|
|
|
|
|
input double InpTrailStart = 120; // Trail activation threshold (points)
|
|
|
|
|
input double InpTrailDistance = 50; // Trail distance (points)
|
|
|
|
|
input double InpTrailStep = 20; // Trail step/increment (points)
|
|
|
|
|
input bool InpAdaptiveTrailing = true; // Enable phase-aware adaptive trailing
|
|
|
|
|
|
|
|
|
|
input group "=== VOLATILITY ADJUSTMENTS ==="
|
|
|
|
|
input double InpLowVolatilityMultiplier = 0.85; // Low volatility: tighter trails
|
|
|
|
|
input double InpHighVolatilityMultiplier = 1.3; // High volatility: wider trails
|
|
|
|
|
input double InpVolatilityThreshold = 1.2; // Volatility threshold (ATR ratio)
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Partial Close Strategy |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== PARTIAL CLOSURES ==="
|
|
|
|
|
input bool InpPartialEnabled = true; // Enable partial closing
|
|
|
|
|
input ENUM_PARTIAL_ENGINE InpPartialEngine = PARTIAL_ENGINE_PROFIT_MAX; // Engine: ProfitMaximizer/PositionManager
|
2026-03-26 17:02:22 +00:00
|
|
|
input ENUM_PARTIAL_MODE InpPartialMode = PARTIAL_MODE_HYBRID; // Partial mode (v1.5): Fixed/ATR/Hybrid
|
2026-03-24 23:54:05 +00:00
|
|
|
|
|
|
|
|
input group "=== PARTIAL LEVELS - FIXED POINT FALLBACK ==="
|
|
|
|
|
input double InpPartialTrigger1 = 40; // Level 1 trigger (points, used when ATR=false)
|
|
|
|
|
input double InpPartialPercent1 = 25; // Level 1 close amount (%)
|
|
|
|
|
input double InpPartialTrigger2 = 80; // Level 2 trigger (points, used when ATR=false)
|
|
|
|
|
input double InpPartialPercent2 = 25; // Level 2 close amount (%)
|
|
|
|
|
input double InpPartialTrigger3 = 150; // Level 3 trigger (points, used when ATR=false)
|
|
|
|
|
input double InpPartialPercent3 = 25; // Level 3 close amount (%)
|
|
|
|
|
input double InpPartialTrigger4 = 300; // Level 4 trigger (points, used when ATR=false)
|
|
|
|
|
input double InpPartialPercent4 = 15; // Level 4 close amount (%)
|
|
|
|
|
|
|
|
|
|
input group "=== PARTIAL LEVELS - ATR MULTIPLIERS (v1.5) ==="
|
|
|
|
|
input double InpPartialATRMult1 = 2.5; // Partial 1 (multiples of ATR)
|
|
|
|
|
input double InpPartialATRMult2 = 4.0; // Partial 2
|
|
|
|
|
input double InpPartialATRMult3 = 6.0; // Partial 3
|
|
|
|
|
input double InpPartialATRMult4 = 10.0; // Partial 4
|
|
|
|
|
|
|
|
|
|
input group "=== RUNNER MANAGEMENT ==="
|
|
|
|
|
input double InpRunnerPercentage = 10; // Runner size to keep trailing (%)
|
|
|
|
|
input double InpRunnerTrailMultiplier = 3.0; // Runner trail multiplier (wider than standard)
|
|
|
|
|
input double InpRunnerExitThreshold = 500; // Runner forced exit threshold (points)
|
|
|
|
|
|
|
|
|
|
input group "=== RETRACEMENT PROTECTION (v1.5) ==="
|
|
|
|
|
input bool InpRetractionGate = true; // Enable retracement gate (defer partials if retracing too much)
|
|
|
|
|
input double InpMaxRetracementForPartial = 12.0; // Max retracement allowed for partial execution (%)
|
2026-03-25 12:49:17 +00:00
|
|
|
input double InpPartialProfitBuffer = 20.0; // Min profit buffer required to override retracement (% above trigger)
|
2026-03-24 23:54:05 +00:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Phase Management Control |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== PHASE MANAGEMENT ==="
|
|
|
|
|
input bool InpUsePhaseManagement = true; // Enable phase-based management (required for operation)
|
|
|
|
|
input bool InpUseDynamicPartials = true; // Enable dynamic partial adjustment
|
|
|
|
|
input bool InpFloorOnlyMode = true; // Use floors as safety nets only (recommended)
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Display & System Settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "=== DISPLAY SETTINGS ==="
|
|
|
|
|
input bool InpShowDashboard = true; // Show performance dashboard
|
|
|
|
|
input int InpDashboardX = 10; // Dashboard X position
|
|
|
|
|
input int InpDashboardY = 30; // Dashboard Y position
|
|
|
|
|
input int InpUpdateFrequency = 2; // Update frequency (seconds)
|
|
|
|
|
|
|
|
|
|
input group "=== SYSTEM SETTINGS ==="
|
|
|
|
|
input ENUM_LOG_LEVEL InpLogLevel = LOG_INFO; // Log level
|
|
|
|
|
input bool InpSaveReports = true; // Save performance reports
|
|
|
|
|
input bool InpSoundAlerts = true; // Enable sound alerts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Global Variables |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
// Core components
|
|
|
|
|
CUtilities* g_utils = NULL;
|
|
|
|
|
CRiskManager* g_risk = NULL;
|
|
|
|
|
CTechnicalAnalysis* g_tech = NULL;
|
|
|
|
|
CPositionManager* g_manager = NULL;
|
|
|
|
|
CProfitMaximizer* g_profit_max = NULL;
|
|
|
|
|
|
|
|
|
|
// State tracking
|
|
|
|
|
bool g_initialized = false;
|
|
|
|
|
datetime g_last_update = 0;
|
|
|
|
|
datetime g_session_start = 0;
|
|
|
|
|
double g_session_start_balance = 0;
|
|
|
|
|
double g_session_start_equity = 0;
|
|
|
|
|
|
|
|
|
|
// Performance tracking
|
|
|
|
|
int g_positions_detected = 0;
|
|
|
|
|
int g_positions_managed = 0;
|
|
|
|
|
int g_positions_closed = 0;
|
|
|
|
|
double g_total_prevented = 0;
|
|
|
|
|
double g_total_captured = 0;
|
|
|
|
|
|
|
|
|
|
// Alert management
|
|
|
|
|
datetime g_last_alert = 0;
|
|
|
|
|
int g_alert_count = 0;
|
|
|
|
|
|
|
|
|
|
// Dashboard elements (simplified)
|
|
|
|
|
//string g_dashboard_prefix = "PME_" + InpInstanceID + "-";
|
|
|
|
|
string g_dashboard_prefix = "PME_1.1";
|
|
|
|
|
int g_dashboard_objects = 0;
|
|
|
|
|
|
|
|
|
|
// Performance tracking
|
|
|
|
|
struct PerformanceTracker
|
|
|
|
|
{
|
|
|
|
|
int trades_managed;
|
|
|
|
|
double total_profit_captured;
|
|
|
|
|
double total_loss_prevented;
|
|
|
|
|
int runners_created;
|
|
|
|
|
int extreme_profits;
|
|
|
|
|
datetime last_update;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PerformanceTracker g_performance;
|
|
|
|
|
|
|
|
|
|
// Effective runtime settings (can be adjusted by startup profile)
|
|
|
|
|
double g_cfg_trail_start = 0.0;
|
|
|
|
|
double g_cfg_trail_distance = 0.0;
|
|
|
|
|
double g_cfg_trail_step = 0.0;
|
|
|
|
|
double g_cfg_partial_trigger_1 = 0.0;
|
|
|
|
|
double g_cfg_partial_trigger_2 = 0.0;
|
|
|
|
|
double g_cfg_partial_trigger_3 = 0.0;
|
|
|
|
|
double g_cfg_partial_trigger_4 = 0.0;
|
|
|
|
|
double g_cfg_partial_percent_1 = 0.0;
|
|
|
|
|
double g_cfg_partial_percent_2 = 0.0;
|
|
|
|
|
double g_cfg_partial_percent_3 = 0.0;
|
|
|
|
|
double g_cfg_partial_percent_4 = 0.0;
|
|
|
|
|
bool g_cfg_use_atr_partials = false;
|
|
|
|
|
double g_cfg_partial_atr_mult_1 = 0.0;
|
|
|
|
|
double g_cfg_partial_atr_mult_2 = 0.0;
|
|
|
|
|
double g_cfg_partial_atr_mult_3 = 0.0;
|
|
|
|
|
double g_cfg_partial_atr_mult_4 = 0.0;
|
|
|
|
|
bool g_cfg_retracement_gate = false;
|
|
|
|
|
double g_cfg_max_retracement_for_partial = 0.0;
|
|
|
|
|
double g_cfg_partial_profit_buffer = 0.0;
|
|
|
|
|
double g_cfg_major_max_retracement_for_partial = 0.0;
|
|
|
|
|
double g_cfg_major_partial_profit_buffer = 0.0;
|
|
|
|
|
double g_cfg_cross_max_retracement_for_partial = 0.0;
|
|
|
|
|
double g_cfg_cross_partial_profit_buffer = 0.0;
|
|
|
|
|
double g_cfg_runner_percentage = 0.0;
|
|
|
|
|
double g_cfg_runner_trail_multiplier = 0.0;
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Apply startup profile overrides |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void ApplyStartupProfile()
|
|
|
|
|
{
|
|
|
|
|
g_cfg_trail_start = InpTrailStart;
|
|
|
|
|
g_cfg_trail_distance = InpTrailDistance;
|
|
|
|
|
g_cfg_trail_step = InpTrailStep;
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_trigger_1 = InpPartialTrigger1;
|
|
|
|
|
g_cfg_partial_trigger_2 = InpPartialTrigger2;
|
|
|
|
|
g_cfg_partial_trigger_3 = InpPartialTrigger3;
|
|
|
|
|
g_cfg_partial_trigger_4 = InpPartialTrigger4;
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_percent_1 = InpPartialPercent1;
|
|
|
|
|
g_cfg_partial_percent_2 = InpPartialPercent2;
|
|
|
|
|
g_cfg_partial_percent_3 = InpPartialPercent3;
|
|
|
|
|
g_cfg_partial_percent_4 = InpPartialPercent4;
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_atr_mult_1 = InpPartialATRMult1;
|
|
|
|
|
g_cfg_partial_atr_mult_2 = InpPartialATRMult2;
|
|
|
|
|
g_cfg_partial_atr_mult_3 = InpPartialATRMult3;
|
|
|
|
|
g_cfg_partial_atr_mult_4 = InpPartialATRMult4;
|
2026-03-26 17:02:22 +00:00
|
|
|
|
|
|
|
|
// A3: Convert ENUM_PARTIAL_MODE to internal bool for backward-compat
|
|
|
|
|
// HYBRID (default=2) maps to true (ATR-first, matches legacy bool=true)
|
|
|
|
|
// FIXED (0) maps to false (fixed-only)
|
|
|
|
|
// ATR (1) maps to true (ATR-only)
|
|
|
|
|
if(InpPartialMode == PARTIAL_MODE_FIXED)
|
|
|
|
|
g_cfg_use_atr_partials = false;
|
|
|
|
|
else // PARTIAL_MODE_ATR or PARTIAL_MODE_HYBRID (default; preserves bool=true behavior)
|
|
|
|
|
g_cfg_use_atr_partials = true;
|
|
|
|
|
|
2026-03-24 23:54:05 +00:00
|
|
|
g_cfg_retracement_gate = InpRetractionGate;
|
|
|
|
|
g_cfg_max_retracement_for_partial = InpMaxRetracementForPartial;
|
|
|
|
|
g_cfg_partial_profit_buffer = InpPartialProfitBuffer;
|
|
|
|
|
g_cfg_major_max_retracement_for_partial = 13.0;
|
|
|
|
|
g_cfg_major_partial_profit_buffer = 25.0;
|
|
|
|
|
g_cfg_cross_max_retracement_for_partial = 15.0;
|
|
|
|
|
g_cfg_cross_partial_profit_buffer = 40.0;
|
|
|
|
|
|
|
|
|
|
g_cfg_runner_percentage = InpRunnerPercentage;
|
|
|
|
|
g_cfg_runner_trail_multiplier = InpRunnerTrailMultiplier;
|
|
|
|
|
|
|
|
|
|
if(InpStartupProfile == STARTUP_PROFILE_SAFE)
|
|
|
|
|
{
|
|
|
|
|
g_cfg_use_atr_partials = true;
|
|
|
|
|
g_cfg_retracement_gate = true;
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_atr_mult_1 = 2.5;
|
|
|
|
|
g_cfg_partial_atr_mult_2 = 4.0;
|
|
|
|
|
g_cfg_partial_atr_mult_3 = 6.0;
|
|
|
|
|
g_cfg_partial_atr_mult_4 = 10.0;
|
|
|
|
|
g_cfg_major_max_retracement_for_partial = 12.0;
|
|
|
|
|
g_cfg_major_partial_profit_buffer = 30.0;
|
|
|
|
|
g_cfg_cross_max_retracement_for_partial = 14.0;
|
|
|
|
|
g_cfg_cross_partial_profit_buffer = 45.0;
|
|
|
|
|
|
|
|
|
|
g_cfg_trail_start = MathMax(g_cfg_trail_start, 120.0);
|
|
|
|
|
g_cfg_trail_distance = MathMax(g_cfg_trail_distance, 70.0);
|
|
|
|
|
g_cfg_trail_step = MathMax(g_cfg_trail_step, 25.0);
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_trigger_1 = MathMax(g_cfg_partial_trigger_1, 80.0);
|
|
|
|
|
g_cfg_partial_trigger_2 = MathMax(g_cfg_partial_trigger_2, 140.0);
|
|
|
|
|
g_cfg_partial_trigger_3 = MathMax(g_cfg_partial_trigger_3, 240.0);
|
|
|
|
|
g_cfg_partial_trigger_4 = MathMax(g_cfg_partial_trigger_4, 400.0);
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_percent_1 = MathMin(g_cfg_partial_percent_1, 20.0);
|
|
|
|
|
g_cfg_partial_percent_2 = MathMin(g_cfg_partial_percent_2, 20.0);
|
|
|
|
|
g_cfg_partial_percent_3 = MathMin(g_cfg_partial_percent_3, 20.0);
|
|
|
|
|
g_cfg_partial_percent_4 = MathMin(g_cfg_partial_percent_4, 10.0);
|
|
|
|
|
|
|
|
|
|
g_cfg_runner_percentage = MathMax(g_cfg_runner_percentage, 20.0);
|
|
|
|
|
g_cfg_runner_trail_multiplier = MathMax(g_cfg_runner_trail_multiplier, 3.5);
|
|
|
|
|
}
|
|
|
|
|
else if(InpStartupProfile == STARTUP_PROFILE_AGGRESSIVE)
|
|
|
|
|
{
|
|
|
|
|
g_cfg_use_atr_partials = true;
|
|
|
|
|
g_cfg_retracement_gate = true;
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_atr_mult_1 = 2.0;
|
|
|
|
|
g_cfg_partial_atr_mult_2 = 3.5;
|
|
|
|
|
g_cfg_partial_atr_mult_3 = 5.5;
|
|
|
|
|
g_cfg_partial_atr_mult_4 = 9.0;
|
|
|
|
|
g_cfg_major_max_retracement_for_partial = 14.0;
|
|
|
|
|
g_cfg_major_partial_profit_buffer = 20.0;
|
|
|
|
|
g_cfg_cross_max_retracement_for_partial = 16.0;
|
|
|
|
|
g_cfg_cross_partial_profit_buffer = 35.0;
|
|
|
|
|
|
|
|
|
|
g_cfg_trail_start = MathMax(g_cfg_trail_start, 80.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep partial trigger sequence valid after profile adjustments.
|
|
|
|
|
g_cfg_partial_trigger_2 = MathMax(g_cfg_partial_trigger_2, g_cfg_partial_trigger_1 + 1.0);
|
|
|
|
|
g_cfg_partial_trigger_3 = MathMax(g_cfg_partial_trigger_3, g_cfg_partial_trigger_2 + 1.0);
|
|
|
|
|
g_cfg_partial_trigger_4 = MathMax(g_cfg_partial_trigger_4, g_cfg_partial_trigger_3 + 1.0);
|
|
|
|
|
|
|
|
|
|
g_cfg_partial_atr_mult_2 = MathMax(g_cfg_partial_atr_mult_2, g_cfg_partial_atr_mult_1 + 0.1);
|
|
|
|
|
g_cfg_partial_atr_mult_3 = MathMax(g_cfg_partial_atr_mult_3, g_cfg_partial_atr_mult_2 + 0.1);
|
|
|
|
|
g_cfg_partial_atr_mult_4 = MathMax(g_cfg_partial_atr_mult_4, g_cfg_partial_atr_mult_3 + 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Helper: startup profile display name |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
string GetStartupProfileName()
|
|
|
|
|
{
|
|
|
|
|
if(InpStartupProfile == STARTUP_PROFILE_SAFE)
|
|
|
|
|
return "Safe";
|
|
|
|
|
if(InpStartupProfile == STARTUP_PROFILE_AGGRESSIVE)
|
|
|
|
|
return "Aggressive";
|
|
|
|
|
return "Custom";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Configure Profit Maximizer |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void ConfigureProfitMaximizer()
|
|
|
|
|
{
|
|
|
|
|
if(g_profit_max == NULL) return;
|
|
|
|
|
|
|
|
|
|
// === V1.3: Configure ProfitMaximizer with optimized settings ===
|
|
|
|
|
|
|
|
|
|
// Pass v1.3 solution flags to ProfitMaximizer
|
|
|
|
|
g_profit_max.SetFloorOnlyMode(InpFloorOnlyMode);
|
|
|
|
|
g_profit_max.SetDynamicPartials(InpUseDynamicPartials);
|
|
|
|
|
g_profit_max.SetATRBreathingRoom(false); // ATR breathing room disabled in v1.4 streamlined mode
|
|
|
|
|
g_profit_max.SetATRPartialMode(g_cfg_use_atr_partials,
|
|
|
|
|
g_cfg_partial_atr_mult_1,
|
|
|
|
|
g_cfg_partial_atr_mult_2,
|
|
|
|
|
g_cfg_partial_atr_mult_3,
|
|
|
|
|
g_cfg_partial_atr_mult_4);
|
|
|
|
|
g_profit_max.SetRetracementGate(g_cfg_retracement_gate,
|
|
|
|
|
g_cfg_max_retracement_for_partial,
|
|
|
|
|
g_cfg_partial_profit_buffer);
|
|
|
|
|
g_profit_max.SetSymbolClassGatePresets(g_cfg_retracement_gate,
|
|
|
|
|
g_cfg_major_max_retracement_for_partial,
|
|
|
|
|
g_cfg_major_partial_profit_buffer,
|
|
|
|
|
g_cfg_cross_max_retracement_for_partial,
|
|
|
|
|
g_cfg_cross_partial_profit_buffer);
|
|
|
|
|
|
|
|
|
|
// Configure optimized partial closure schedule (v1.3)
|
|
|
|
|
g_profit_max.SetPartialLevel(0, g_cfg_partial_trigger_1, g_cfg_partial_percent_1);
|
|
|
|
|
g_profit_max.SetPartialLevel(1, g_cfg_partial_trigger_2, g_cfg_partial_percent_2);
|
|
|
|
|
g_profit_max.SetPartialLevel(2, g_cfg_partial_trigger_3, g_cfg_partial_percent_3);
|
|
|
|
|
g_profit_max.SetPartialLevel(3, g_cfg_partial_trigger_4, g_cfg_partial_percent_4);
|
|
|
|
|
|
|
|
|
|
// Configure runner settings
|
|
|
|
|
g_profit_max.SetRunnerConfig(g_cfg_runner_percentage, g_cfg_runner_trail_multiplier, InpRunnerExitThreshold);
|
|
|
|
|
|
|
|
|
|
// Configure phase minimum locks
|
|
|
|
|
g_profit_max.SetPhaseMinLock(PHASE_PROTECTION, InpPhase1MinLock);
|
|
|
|
|
g_profit_max.SetPhaseMinLock(PHASE_ACCUMULATION, InpPhase2MinLock);
|
|
|
|
|
g_profit_max.SetPhaseMinLock(PHASE_MAXIMIZATION, InpPhase3MinLock);
|
|
|
|
|
g_profit_max.SetPhaseMinLock(PHASE_RUNNER, InpPhase4MinLock);
|
|
|
|
|
g_profit_max.SetPhaseMinLock(PHASE_EXTREME, InpPhase5MinLock);
|
|
|
|
|
|
|
|
|
|
// Phase locks now operate in floor-only mode (safety floors only, no active management)
|
|
|
|
|
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log("Phase Management Enabled - Using adaptive profit protection", LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Phase Triggers: P1=%.0f P2=%.0f P3=%.0f P4=%.0f P5=%.0f",
|
|
|
|
|
InpPhase1Trigger, InpPhase2Trigger, InpPhase3Trigger,
|
|
|
|
|
InpPhase4Trigger, InpPhase5Trigger), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("V1.3 Solutions: FloorOnly=%s, DynamicPartials=%s, ATRBreathing=%s",
|
|
|
|
|
InpFloorOnlyMode ? "ON" : "OFF",
|
|
|
|
|
InpUseDynamicPartials ? "ON" : "OFF",
|
|
|
|
|
false ? "ON" : "OFF"), LOG_INFO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Apply Phase Management to Position |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void ApplyPhaseManagement(ulong ticket)
|
|
|
|
|
{
|
|
|
|
|
if(!InpUsePhaseManagement || g_profit_max == NULL) return;
|
|
|
|
|
|
|
|
|
|
// Get position info
|
|
|
|
|
if(!PositionSelectByTicket(ticket)) return;
|
|
|
|
|
|
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
|
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
|
|
|
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
|
|
|
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
|
|
|
if(symbol_point <= 0.0)
|
|
|
|
|
symbol_point = _Point;
|
|
|
|
|
|
|
|
|
|
double profit_points = 0.0;
|
|
|
|
|
if(symbol_point > 0.0)
|
|
|
|
|
profit_points = (current_price - entry_price) / symbol_point;
|
|
|
|
|
|
|
|
|
|
// For short positions, profit calculation is inverted
|
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && symbol_point > 0.0)
|
|
|
|
|
profit_points = (entry_price - current_price) / symbol_point;
|
|
|
|
|
|
|
|
|
|
// Analyze position and determine phase
|
|
|
|
|
g_profit_max.AnalyzePosition(ticket, current_price, profit_points);
|
|
|
|
|
|
|
|
|
|
// Get current phase
|
|
|
|
|
ENUM_PROFIT_PHASE phase = g_profit_max.GetCurrentPhase(ticket);
|
|
|
|
|
|
|
|
|
|
// === NEW: Apply phase-based profit lock ===
|
|
|
|
|
double phase_stop_price;
|
|
|
|
|
string lock_reason;
|
|
|
|
|
|
|
|
|
|
if(g_profit_max.GetPhaseProtectionStop(ticket, phase_stop_price, lock_reason))
|
|
|
|
|
{
|
|
|
|
|
// Phase lock suggests a stop update
|
|
|
|
|
if(g_manager != NULL && !g_manager.AdjustStopLoss(ticket, phase_stop_price))
|
|
|
|
|
{
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log(StringFormat("Failed to apply phase lock for #%I64u: %s",
|
|
|
|
|
ticket, lock_reason), LOG_ERROR);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log(StringFormat("Applied phase lock for #%I64u: %s",
|
|
|
|
|
ticket, lock_reason), LOG_INFO);
|
|
|
|
|
|
|
|
|
|
// Sound alert for lock application
|
|
|
|
|
if(InpSoundAlerts) PlaySound("ok.wav");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// === V1.3: Check and execute partial closures ===
|
|
|
|
|
bool use_profit_max_partials = (InpPartialEnabled &&
|
|
|
|
|
InpUsePhaseManagement &&
|
|
|
|
|
InpPartialEngine == PARTIAL_ENGINE_PROFIT_MAX);
|
|
|
|
|
|
|
|
|
|
if(use_profit_max_partials && g_profit_max != NULL && g_manager != NULL)
|
|
|
|
|
{
|
|
|
|
|
double partial_percentage = 0.0;
|
|
|
|
|
|
|
|
|
|
if(g_profit_max.ShouldExecutePartial(ticket, partial_percentage))
|
|
|
|
|
{
|
|
|
|
|
// Execute partial closure (PartialClose handles volume validation internally)
|
|
|
|
|
if(g_manager.PartialClose(ticket, partial_percentage))
|
|
|
|
|
{
|
|
|
|
|
// Partial close counter is tracked internally by PositionManager
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log(StringFormat("Executed %.0f%% partial close for #%I64u at %.0f pts",
|
|
|
|
|
partial_percentage, ticket, profit_points), LOG_INFO);
|
|
|
|
|
|
|
|
|
|
if(InpSoundAlerts) PlaySound("tick.wav");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log(StringFormat("Failed to execute %.0f%% partial close for #%I64u (volume too small or trade error)",
|
|
|
|
|
partial_percentage, ticket), LOG_WARNING);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update performance tracking based on phase
|
|
|
|
|
if(phase == PHASE_RUNNER)
|
|
|
|
|
{
|
|
|
|
|
g_performance.runners_created++;
|
|
|
|
|
}
|
|
|
|
|
else if(phase == PHASE_EXTREME)
|
|
|
|
|
{
|
|
|
|
|
g_performance.extreme_profits++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert initialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
|
|
|
|
{
|
|
|
|
|
Print("==================================================");
|
|
|
|
|
Print(" ERMT PME v1.5 - ATR Adaptive Partials");
|
|
|
|
|
Print(" WHIPSAW-RESISTANT PARTIALS + FLOOR LOCKS");
|
|
|
|
|
Print("==================================================");
|
|
|
|
|
Print("Initializing v1.5 management system...");
|
|
|
|
|
Print("- Phase locks: Safety floors only (no dynamic tightening)");
|
|
|
|
|
Print("- Adaptive trailing: Phase-aware (wider at higher phases)");
|
|
|
|
|
Print("- Volatility logic: ATR-scaled partial triggers");
|
|
|
|
|
Print("- Retracement gate: Defers closes during healthy pullbacks");
|
|
|
|
|
|
|
|
|
|
// Validate inputs
|
|
|
|
|
if(!ValidateInputs())
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Invalid input parameters");
|
|
|
|
|
return INIT_PARAMETERS_INCORRECT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build effective runtime config from selected startup profile.
|
|
|
|
|
ApplyStartupProfile();
|
|
|
|
|
|
|
|
|
|
// Create core components
|
|
|
|
|
g_utils = new CUtilities();
|
|
|
|
|
if(g_utils == NULL)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to create Utilities");
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_risk = new CRiskManager();
|
|
|
|
|
if(g_risk == NULL)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to create RiskManager");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_tech = new CTechnicalAnalysis();
|
|
|
|
|
if(g_tech == NULL)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to create TechnicalAnalysis");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_manager = new CPositionManager();
|
|
|
|
|
if(g_manager == NULL)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to create PositionManager");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize profit maximizer
|
|
|
|
|
g_profit_max = new CProfitMaximizer();
|
|
|
|
|
if(g_profit_max == NULL)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to create ProfitMaximizer");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure profit maximizer with input parameters
|
|
|
|
|
if(InpUsePhaseManagement)
|
|
|
|
|
{
|
|
|
|
|
ConfigureProfitMaximizer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize utilities
|
|
|
|
|
if(!g_utils.Initialize(InpLogLevel, InpSaveReports))
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to initialize Utilities");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize risk manager
|
|
|
|
|
if(!g_risk.Initialize(g_utils, InpMaxLossPerTrade, InpMaxDailyLoss, InpMaxDrawdown))
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to initialize RiskManager");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize technical analysis with retry mechanism
|
|
|
|
|
if(!g_tech.Initialize(g_utils))
|
|
|
|
|
{
|
|
|
|
|
Print("WARNING: Technical Analysis initialization incomplete");
|
|
|
|
|
// Try alternative symbol format
|
|
|
|
|
ChartSetSymbolPeriod(0, _Symbol, PERIOD_CURRENT);
|
|
|
|
|
Sleep(100);
|
|
|
|
|
// Retry initialization
|
|
|
|
|
if(!g_tech.Initialize(g_utils))
|
|
|
|
|
{
|
|
|
|
|
Print("Technical Analysis initialization still incomplete after retry");
|
|
|
|
|
Print("This is normal if chart data is still loading. EA will continue...");
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log("Technical Analysis initialization incomplete - will retry on first tick", LOG_WARNING);
|
|
|
|
|
// Don't fail - EA can work without technical indicators initially
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Print("Technical Analysis initialized successfully on retry");
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
g_utils.Log("Technical Analysis initialized successfully on retry", LOG_INFO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize position manager
|
|
|
|
|
if(!g_manager.Initialize(g_utils, g_risk, g_tech, InpMagicFilter))
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Failed to initialize PositionManager");
|
|
|
|
|
Cleanup();
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure position manager
|
|
|
|
|
ManagementConfig config;
|
|
|
|
|
config.apply_default_sl = InpApplyEmergencyStops;
|
|
|
|
|
config.default_sl_atr = InpEmergencySLMultiplier;
|
|
|
|
|
config.apply_default_tp = InpApplyEmergencyStops;
|
|
|
|
|
config.default_tp_atr = InpEmergencyTPMultiplier;
|
|
|
|
|
config.trailing_method = InpTrailingMethod;
|
|
|
|
|
config.trail_start = g_cfg_trail_start;
|
|
|
|
|
config.trail_distance = g_cfg_trail_distance;
|
|
|
|
|
config.trail_step = g_cfg_trail_step;
|
|
|
|
|
config.adaptive_trailing = InpAdaptiveTrailing;
|
|
|
|
|
bool use_profit_max_partials = (InpPartialEnabled &&
|
|
|
|
|
InpUsePhaseManagement &&
|
|
|
|
|
InpPartialEngine == PARTIAL_ENGINE_PROFIT_MAX);
|
|
|
|
|
config.partial_enabled = (InpPartialEnabled && !use_profit_max_partials);
|
|
|
|
|
config.partial_trigger_1 = g_cfg_partial_trigger_1;
|
|
|
|
|
config.partial_percent_1 = g_cfg_partial_percent_1;
|
|
|
|
|
config.partial_trigger_2 = g_cfg_partial_trigger_2;
|
|
|
|
|
config.partial_percent_2 = g_cfg_partial_percent_2;
|
|
|
|
|
config.partial_trigger_3 = g_cfg_partial_trigger_3;
|
|
|
|
|
config.partial_percent_3 = g_cfg_partial_percent_3;
|
|
|
|
|
config.partial_trigger_4 = g_cfg_partial_trigger_4;
|
|
|
|
|
config.partial_percent_4 = g_cfg_partial_percent_4;
|
|
|
|
|
config.max_loss_per_trade = InpMaxLossPerTrade;
|
|
|
|
|
config.max_daily_loss = InpMaxDailyLoss;
|
|
|
|
|
config.max_correlation = InpMaxCorrelation;
|
|
|
|
|
config.max_drawdown = InpMaxDrawdown;
|
|
|
|
|
|
|
|
|
|
g_manager.SetConfiguration(config);
|
|
|
|
|
|
|
|
|
|
// Initialize session
|
|
|
|
|
g_session_start = TimeCurrent();
|
|
|
|
|
g_session_start_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
|
g_session_start_equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
|
|
|
|
|
|
|
|
// Initialize dashboard
|
|
|
|
|
if(InpShowDashboard)
|
|
|
|
|
{
|
|
|
|
|
InitializeDashboard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Log configuration
|
|
|
|
|
LogConfiguration();
|
|
|
|
|
|
|
|
|
|
// Set timer
|
|
|
|
|
EventSetTimer(InpUpdateFrequency);
|
|
|
|
|
|
|
|
|
|
g_initialized = true;
|
|
|
|
|
|
|
|
|
|
// Initial status
|
|
|
|
|
int initial_positions = g_manager.GetManagedCount();
|
|
|
|
|
Print(StringFormat("PME Ready - Managing %d positions", initial_positions));
|
|
|
|
|
g_utils.Log(StringFormat("PME initialized successfully - %d positions detected",
|
|
|
|
|
initial_positions), LOG_INFO);
|
|
|
|
|
|
|
|
|
|
// Sound alert
|
|
|
|
|
if(InpSoundAlerts) PlaySound("alert.wav");
|
|
|
|
|
|
|
|
|
|
return INIT_SUCCEEDED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert deinitialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnDeinit(const int reason)
|
|
|
|
|
{
|
|
|
|
|
EventKillTimer();
|
|
|
|
|
|
|
|
|
|
string reason_text = GetDeinitReasonText(reason);
|
|
|
|
|
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log(StringFormat("PME Shutdown: %s", reason_text), LOG_INFO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate final report
|
|
|
|
|
if(g_manager != NULL && g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
GenerateFinalReport();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clean up dashboard
|
|
|
|
|
if(InpShowDashboard)
|
|
|
|
|
{
|
|
|
|
|
RemoveDashboard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clean up components
|
|
|
|
|
Cleanup();
|
|
|
|
|
|
|
|
|
|
Comment("");
|
|
|
|
|
Print("==================================================");
|
|
|
|
|
Print(StringFormat("PME Shutdown Complete: %s", reason_text));
|
|
|
|
|
Print("==================================================");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert tick function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTick()
|
|
|
|
|
{
|
|
|
|
|
if(!g_initialized) return;
|
|
|
|
|
|
|
|
|
|
// Check if we should be active
|
|
|
|
|
if(!CheckTradingConditions()) return;
|
|
|
|
|
|
|
|
|
|
// Apply phase-based management if enabled
|
|
|
|
|
if(InpUsePhaseManagement && g_profit_max != NULL)
|
|
|
|
|
{
|
|
|
|
|
int total = PositionsTotal();
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(ticket > 0)
|
|
|
|
|
{
|
|
|
|
|
ApplyPhaseManagement(ticket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Main management cycle (handles standard management)
|
|
|
|
|
g_manager.ManageAllPositions();
|
|
|
|
|
|
|
|
|
|
// Check risk limits
|
|
|
|
|
CheckRiskLimits();
|
|
|
|
|
|
|
|
|
|
// Update dashboard if visible
|
|
|
|
|
if(InpShowDashboard)
|
|
|
|
|
{
|
|
|
|
|
UpdateDashboard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for Friday close
|
|
|
|
|
if(false && IsFridayClose()) // Friday close feature disabled in v1.4
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log("Friday close time - closing all positions", LOG_WARNING);
|
|
|
|
|
g_manager.CloseAllPositions(EXIT_TIME);
|
|
|
|
|
|
|
|
|
|
if(InpSoundAlerts) PlaySound("alert2.wav");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Timer function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTimer()
|
|
|
|
|
{
|
|
|
|
|
if(!g_initialized) return;
|
|
|
|
|
|
|
|
|
|
// Periodic status update
|
|
|
|
|
static int status_counter = 0;
|
|
|
|
|
status_counter++;
|
|
|
|
|
|
|
|
|
|
if(status_counter >= 60 / InpUpdateFrequency) // Every minute
|
|
|
|
|
{
|
|
|
|
|
status_counter = 0;
|
|
|
|
|
LogStatus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save snapshot
|
|
|
|
|
static int snapshot_counter = 0;
|
|
|
|
|
snapshot_counter++;
|
|
|
|
|
|
|
|
|
|
if(snapshot_counter >= 300 / InpUpdateFrequency) // Every 5 minutes
|
|
|
|
|
{
|
|
|
|
|
snapshot_counter = 0;
|
|
|
|
|
if(InpSaveReports)
|
|
|
|
|
{
|
|
|
|
|
SaveSnapshot();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for alerts
|
|
|
|
|
CheckAlerts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Trade function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTrade()
|
|
|
|
|
{
|
|
|
|
|
if(!g_initialized) return;
|
|
|
|
|
|
|
|
|
|
// Update metrics on trade events
|
|
|
|
|
g_positions_closed++;
|
|
|
|
|
|
|
|
|
|
// Log trade event
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log("Trade event detected", LOG_DEBUG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Validate Inputs |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool ValidateInputs()
|
|
|
|
|
{
|
|
|
|
|
// === Risk Limit Validation ===
|
|
|
|
|
if(InpMaxLossPerTrade <= 0 || InpMaxLossPerTrade > 20)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: MaxLossPerTrade must be between 0.1 and 20 (percentage)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpMaxDailyLoss <= 0 || InpMaxDailyLoss > 50)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: MaxDailyLoss must be between 0.1 and 50 (percentage)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpMaxDrawdown <= 0 || InpMaxDrawdown > 100)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: MaxDrawdown must be between 0.1 and 100 (percentage)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpMaxCorrelation < 0 || InpMaxCorrelation > 1.0)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: MaxCorrelation must be between 0 and 1.0");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// === Trailing Stop Validation ===
|
|
|
|
|
if(InpTrailStart < 0 || InpTrailStart > 2000)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: TrailStart must be between 0 and 2000 points");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpTrailDistance < 5 || InpTrailDistance > 1000)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: TrailDistance must be between 5 and 1000 points");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpTrailStep < 1 || InpTrailStep > 500)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: TrailStep must be between 1 and 500 points");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// === Partial Closure Validation ===
|
|
|
|
|
if(InpPartialEnabled)
|
|
|
|
|
{
|
|
|
|
|
// ProfitMax partials need phase management to calculate phase/profit state.
|
|
|
|
|
if(InpPartialEngine == PARTIAL_ENGINE_PROFIT_MAX && !InpUsePhaseManagement)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: PartialEngine=PROFIT_MAX requires InpUsePhaseManagement=true");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that triggers are in ascending order
|
|
|
|
|
if(InpPartialTrigger2 <= InpPartialTrigger1 ||
|
|
|
|
|
InpPartialTrigger3 <= InpPartialTrigger2 ||
|
|
|
|
|
InpPartialTrigger4 <= InpPartialTrigger3)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Partial triggers must be in ascending order (L1 < L2 < L3 < L4)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check percentages are valid
|
|
|
|
|
if(InpPartialPercent1 <= 0 || InpPartialPercent1 > 100 ||
|
|
|
|
|
InpPartialPercent2 <= 0 || InpPartialPercent2 > 100 ||
|
|
|
|
|
InpPartialPercent3 <= 0 || InpPartialPercent3 > 100 ||
|
|
|
|
|
InpPartialPercent4 <= 0 || InpPartialPercent4 > 100)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Partial percentages must be between 0 and 100");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that total closure doesn't exceed 100%
|
|
|
|
|
double total_closure = InpPartialPercent1 + InpPartialPercent2 +
|
|
|
|
|
InpPartialPercent3 + InpPartialPercent4;
|
|
|
|
|
if(total_closure > 95)
|
|
|
|
|
{
|
|
|
|
|
Print(StringFormat("WARNING: Total partial closure is %.1f%% - leaving less than 5%% as runner",
|
|
|
|
|
total_closure));
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 17:02:22 +00:00
|
|
|
if(InpPartialMode == PARTIAL_MODE_ATR || InpPartialMode == PARTIAL_MODE_HYBRID)
|
2026-03-24 23:54:05 +00:00
|
|
|
{
|
|
|
|
|
if(InpPartialATRMult1 <= 0 || InpPartialATRMult2 <= 0 ||
|
|
|
|
|
InpPartialATRMult3 <= 0 || InpPartialATRMult4 <= 0)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: ATR multipliers must all be > 0");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpPartialATRMult2 <= InpPartialATRMult1 ||
|
|
|
|
|
InpPartialATRMult3 <= InpPartialATRMult2 ||
|
|
|
|
|
InpPartialATRMult4 <= InpPartialATRMult3)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: ATR multipliers must be in ascending order (M1 < M2 < M3 < M4)");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpRetractionGate)
|
|
|
|
|
{
|
|
|
|
|
if(InpMaxRetracementForPartial <= 0 || InpMaxRetracementForPartial >= 100)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: MaxRetracementForPartial must be between 0 and 100");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpPartialProfitBuffer < 0)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: PartialProfitBuffer must be >= 0");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === Phase Lock Validation ===
|
|
|
|
|
if(InpUsePhaseManagement)
|
|
|
|
|
{
|
|
|
|
|
// Check that phase locks are in ascending order
|
|
|
|
|
if(InpPhase2MinLock <= InpPhase1MinLock ||
|
|
|
|
|
InpPhase3MinLock <= InpPhase2MinLock ||
|
|
|
|
|
InpPhase4MinLock <= InpPhase3MinLock ||
|
|
|
|
|
InpPhase5MinLock <= InpPhase4MinLock)
|
|
|
|
|
{
|
|
|
|
|
Print("ERROR: Phase minimum locks must be in ascending order");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print("✓ All input parameters validated successfully");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check Trading Conditions |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CheckTradingConditions()
|
|
|
|
|
{
|
|
|
|
|
// Check if terminal is connected
|
|
|
|
|
if(!TerminalInfoInteger(TERMINAL_CONNECTED))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Check if trade is allowed
|
|
|
|
|
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check Risk Limits |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CheckRiskLimits()
|
|
|
|
|
{
|
|
|
|
|
// Check daily loss
|
|
|
|
|
double daily_loss = g_risk.GetDailyLoss();
|
|
|
|
|
if(daily_loss > InpMaxDailyLoss)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log(StringFormat("Daily loss limit reached: %.2f%%", daily_loss), LOG_ERROR);
|
|
|
|
|
g_manager.CloseAllPositions(EXIT_RISK);
|
|
|
|
|
|
|
|
|
|
// Send alerts
|
|
|
|
|
SendAlert("Daily loss limit reached!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check drawdown
|
|
|
|
|
double drawdown = g_risk.GetDrawdown();
|
|
|
|
|
if(drawdown > InpMaxDrawdown)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log(StringFormat("Max drawdown reached: %.2f%%", drawdown), LOG_ERROR);
|
|
|
|
|
g_manager.CloseAllPositions(EXIT_DRAWDOWN);
|
|
|
|
|
|
|
|
|
|
// Send alerts
|
|
|
|
|
SendAlert("Max drawdown reached!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check individual position risks
|
|
|
|
|
double total_risk = g_manager.GetTotalRisk();
|
|
|
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
|
double risk_percent = (total_risk / balance) * 100;
|
|
|
|
|
|
|
|
|
|
if(risk_percent > InpMaxDailyLoss)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log(StringFormat("Total risk too high: %.2f%%", risk_percent), LOG_WARNING);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check if Friday Close |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsFridayClose()
|
|
|
|
|
{
|
|
|
|
|
MqlDateTime current;
|
|
|
|
|
TimeToStruct(TimeCurrent(), current);
|
|
|
|
|
|
|
|
|
|
return false; // Friday close disabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string NormalizeSymbolToPair(const string raw_symbol)
|
|
|
|
|
{
|
|
|
|
|
string upper_symbol = raw_symbol;
|
|
|
|
|
StringToUpper(upper_symbol);
|
|
|
|
|
string pair = "";
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < StringLen(upper_symbol); i++)
|
|
|
|
|
{
|
|
|
|
|
ushort ch = StringGetCharacter(upper_symbol, i);
|
|
|
|
|
if(ch >= 'A' && ch <= 'Z')
|
|
|
|
|
pair += CharToString((uchar)ch);
|
|
|
|
|
|
|
|
|
|
if(StringLen(pair) >= 6)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(StringLen(pair) < 6)
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
return StringSubstr(pair, 0, 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsKnownFXMajorPair(const string pair)
|
|
|
|
|
{
|
|
|
|
|
return (pair == "EURUSD" || pair == "GBPUSD" || pair == "AUDUSD" ||
|
|
|
|
|
pair == "NZDUSD" || pair == "USDJPY" || pair == "USDCHF" ||
|
|
|
|
|
pair == "USDCAD");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsKnownFXCrossPair(const string pair)
|
|
|
|
|
{
|
|
|
|
|
if(StringLen(pair) != 6) return false;
|
|
|
|
|
if(IsKnownFXMajorPair(pair)) return false;
|
|
|
|
|
|
|
|
|
|
string base = StringSubstr(pair, 0, 3);
|
|
|
|
|
string quote = StringSubstr(pair, 3, 3);
|
|
|
|
|
string majors[8] = {"USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD"};
|
|
|
|
|
|
|
|
|
|
bool base_known = false;
|
|
|
|
|
bool quote_known = false;
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
if(base == majors[i]) base_known = true;
|
|
|
|
|
if(quote == majors[i]) quote_known = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (base_known && quote_known);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string DetectSymbolClass(const string symbol)
|
|
|
|
|
{
|
|
|
|
|
string pair = NormalizeSymbolToPair(symbol);
|
|
|
|
|
if(pair == "")
|
|
|
|
|
return "default";
|
|
|
|
|
|
|
|
|
|
if(IsKnownFXMajorPair(pair))
|
|
|
|
|
return "major";
|
|
|
|
|
|
|
|
|
|
if(IsKnownFXCrossPair(pair))
|
|
|
|
|
return "cross";
|
|
|
|
|
|
|
|
|
|
return "default";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double CalculateATRTriggerPoints(string symbol, double atr_multiplier)
|
|
|
|
|
{
|
|
|
|
|
if(g_tech == NULL) return 0.0;
|
|
|
|
|
|
|
|
|
|
string sym = symbol;
|
|
|
|
|
if(sym == "") sym = _Symbol;
|
|
|
|
|
|
|
|
|
|
double point_value = SymbolInfoDouble(sym, SYMBOL_POINT);
|
|
|
|
|
if(point_value <= 0.0) point_value = _Point;
|
|
|
|
|
if(point_value <= 0.0) return 0.0;
|
|
|
|
|
|
|
|
|
|
double atr_price = g_tech.GetATR(sym);
|
|
|
|
|
if(atr_price <= 0.0) return 0.0;
|
|
|
|
|
|
|
|
|
|
return (atr_price / point_value) * atr_multiplier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Initialize Dashboard |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void InitializeDashboard()
|
|
|
|
|
{
|
|
|
|
|
int y = InpDashboardY;
|
|
|
|
|
color text_color = clrWhite;
|
|
|
|
|
color header_color = clrGold;
|
|
|
|
|
|
|
|
|
|
// Compact dashboard focused on live status and effective profile settings.
|
|
|
|
|
CreateRectangle(g_dashboard_prefix + "BG", InpDashboardX - 5, y - 5, 340, 300, clrBlack, STYLE_SOLID, 1);
|
|
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "HEADER", InpDashboardX, y, "ERMT PME v1.5", header_color, 11);
|
|
|
|
|
y += 20;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "PROFILE", InpDashboardX, y, "Profile: " + GetStartupProfileName(), clrAqua, 9);
|
|
|
|
|
y += 16;
|
|
|
|
|
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "LINE1", InpDashboardX, y, "----------------------------", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
|
|
|
|
|
// Status section
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "STATUS", InpDashboardX, y, "STATUS", header_color, 10);
|
|
|
|
|
y += 18;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "ACTIVE", InpDashboardX, y, "Active: 0", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "CLOSED", InpDashboardX, y, "Closed: 0", text_color, 9);
|
|
|
|
|
y += 20;
|
|
|
|
|
|
|
|
|
|
// Risk section
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "RISK", InpDashboardX, y, "RISK", header_color, 10);
|
|
|
|
|
y += 18;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "EXPOSURE", InpDashboardX, y, "Exposure: 0.00", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "DRAWDOWN", InpDashboardX, y, "Drawdown: 0.0%", text_color, 9);
|
|
|
|
|
y += 20;
|
|
|
|
|
|
|
|
|
|
// Execution section
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "MGMT", InpDashboardX, y, "EXECUTION", header_color, 10);
|
|
|
|
|
y += 18;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "TRAILS", InpDashboardX, y, "Trails: 0", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "PARTIALS", InpDashboardX, y, "Partials: 0", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "RUNNERS", InpDashboardX, y, "Runners: 0", text_color, 9);
|
|
|
|
|
y += 20;
|
|
|
|
|
|
|
|
|
|
// Effective config section
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "CFG_HEADER", InpDashboardX, y, "EFFECTIVE CONFIG", header_color, 10);
|
|
|
|
|
y += 18;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "EFF_TRAIL", InpDashboardX, y, "Trail: 0/0/0", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "EFF_PARTIALS", InpDashboardX, y, "Partials: T1 0(0%) T2 0(0%)", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "ATR_STATE", InpDashboardX, y, "ATR: OFF | Gate: OFF", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "SYM_CLASS", InpDashboardX, y, "Symbol Class: default", clrGray, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "EFFICIENCY", InpDashboardX, y, "Efficiency: 0.0%", text_color, 9);
|
|
|
|
|
y += 15;
|
|
|
|
|
CreateLabel(g_dashboard_prefix + "LOCKED_MIN", InpDashboardX, y, "Min Locked: 0 pts", text_color, 9);
|
|
|
|
|
|
|
|
|
|
g_dashboard_objects = 20;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Update Dashboard |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateDashboard()
|
|
|
|
|
{
|
|
|
|
|
SessionMetrics metrics;
|
|
|
|
|
g_manager.GetSessionMetrics(metrics);
|
|
|
|
|
|
|
|
|
|
// Update status
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "ACTIVE",
|
|
|
|
|
StringFormat("Active: %d", g_manager.GetManagedCount()));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "CLOSED",
|
|
|
|
|
StringFormat("Closed: %d", metrics.positions_closed));
|
|
|
|
|
|
|
|
|
|
// Update risk
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "EXPOSURE",
|
|
|
|
|
StringFormat("Exposure: %.2f", g_manager.GetTotalExposure()));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "DRAWDOWN",
|
|
|
|
|
StringFormat("Drawdown: %.1f%%", g_risk.GetDrawdown()));
|
|
|
|
|
|
|
|
|
|
// Update management
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "PROFILE",
|
|
|
|
|
"Profile: " + GetStartupProfileName());
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "TRAILS",
|
|
|
|
|
StringFormat("Trails: %d", metrics.trails_activated));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "PARTIALS",
|
|
|
|
|
StringFormat("Partials: %d", metrics.partial_closes));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "EFFICIENCY",
|
|
|
|
|
StringFormat("Efficiency: %.1f%%", metrics.management_efficiency));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "EFF_TRAIL",
|
|
|
|
|
StringFormat("Trail: %.0f/%.0f/%.0f", g_cfg_trail_start, g_cfg_trail_distance, g_cfg_trail_step));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "EFF_PARTIALS",
|
|
|
|
|
StringFormat("Partials: T1 %.0f(%.0f%%) T2 %.0f(%.0f%%)",
|
|
|
|
|
g_cfg_partial_trigger_1, g_cfg_partial_percent_1,
|
|
|
|
|
g_cfg_partial_trigger_2, g_cfg_partial_percent_2));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "ATR_STATE",
|
2026-03-25 12:49:17 +00:00
|
|
|
StringFormat("ATR:%s %.1f/%.1f/%.1f/%.1f | Gate:%s %.0f%%/%.0f%%",
|
2026-03-24 23:54:05 +00:00
|
|
|
g_cfg_use_atr_partials ? "ON" : "OFF",
|
|
|
|
|
g_cfg_partial_atr_mult_1,
|
|
|
|
|
g_cfg_partial_atr_mult_2,
|
|
|
|
|
g_cfg_partial_atr_mult_3,
|
|
|
|
|
g_cfg_partial_atr_mult_4,
|
|
|
|
|
g_cfg_retracement_gate ? "ON" : "OFF",
|
|
|
|
|
g_cfg_max_retracement_for_partial,
|
|
|
|
|
g_cfg_partial_profit_buffer));
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "SYM_CLASS", "Symbol Class: n/a");
|
|
|
|
|
ObjectSetInteger(0, g_dashboard_prefix + "SYM_CLASS", OBJPROP_COLOR, clrGray);
|
|
|
|
|
|
|
|
|
|
// Update phase distribution
|
|
|
|
|
if(InpUsePhaseManagement && g_profit_max != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Count positions by phase
|
|
|
|
|
int phase_count[6] = {0, 0, 0, 0, 0, 0};
|
|
|
|
|
int total = PositionsTotal();
|
|
|
|
|
ulong first_ticket = 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(ticket > 0)
|
|
|
|
|
{
|
|
|
|
|
if(first_ticket == 0)
|
|
|
|
|
first_ticket = ticket;
|
|
|
|
|
|
|
|
|
|
ENUM_PROFIT_PHASE phase = g_profit_max.GetCurrentPhase(ticket);
|
|
|
|
|
phase_count[phase]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(first_ticket > 0 && PositionSelectByTicket(first_ticket))
|
|
|
|
|
{
|
|
|
|
|
string symbol = PositionGetString(POSITION_SYMBOL);
|
|
|
|
|
if(symbol == "") symbol = _Symbol;
|
|
|
|
|
string symbol_class = DetectSymbolClass(symbol);
|
|
|
|
|
|
|
|
|
|
color symbol_class_color = clrGray;
|
|
|
|
|
if(symbol_class == "major")
|
|
|
|
|
symbol_class_color = clrLime;
|
|
|
|
|
else if(symbol_class == "cross")
|
|
|
|
|
symbol_class_color = clrOrange;
|
|
|
|
|
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "SYM_CLASS",
|
|
|
|
|
StringFormat("Symbol Class: %s (%s)", symbol_class, symbol));
|
|
|
|
|
ObjectSetInteger(0, g_dashboard_prefix + "SYM_CLASS", OBJPROP_COLOR, symbol_class_color);
|
|
|
|
|
|
|
|
|
|
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
|
|
|
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
|
|
|
double symbol_point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
|
|
|
if(symbol_point <= 0.0) symbol_point = _Point;
|
|
|
|
|
|
|
|
|
|
double current_profit_points = 0.0;
|
|
|
|
|
if(symbol_point > 0.0)
|
|
|
|
|
{
|
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
|
|
|
current_profit_points = (current_price - entry_price) / symbol_point;
|
|
|
|
|
else
|
|
|
|
|
current_profit_points = (entry_price - current_price) / symbol_point;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 12:49:17 +00:00
|
|
|
double atr_trigger_l1 = g_cfg_use_atr_partials
|
|
|
|
|
? CalculateATRTriggerPoints(symbol, g_cfg_partial_atr_mult_1)
|
|
|
|
|
: 0.0;
|
|
|
|
|
double trigger_l1 = g_cfg_use_atr_partials ? atr_trigger_l1 : g_cfg_partial_trigger_1;
|
2026-03-24 23:54:05 +00:00
|
|
|
|
|
|
|
|
double protected_profit, peak_profit, retracement_pct;
|
|
|
|
|
g_profit_max.GetProtectionStatus(first_ticket, protected_profit, peak_profit, retracement_pct);
|
|
|
|
|
|
|
|
|
|
string gate_status = "N/A";
|
|
|
|
|
if(g_cfg_retracement_gate)
|
|
|
|
|
{
|
2026-03-25 12:49:17 +00:00
|
|
|
double buffer_pct = (trigger_l1 > 0.0) ? ((current_profit_points - trigger_l1) / trigger_l1 * 100.0) : 0.0;
|
2026-03-24 23:54:05 +00:00
|
|
|
bool gate_pass = (retracement_pct < g_cfg_max_retracement_for_partial ||
|
2026-03-25 12:49:17 +00:00
|
|
|
buffer_pct >= g_cfg_partial_profit_buffer);
|
2026-03-24 23:54:05 +00:00
|
|
|
gate_status = gate_pass ? "EXEC" : "DEFER";
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 12:49:17 +00:00
|
|
|
string trig_label = g_cfg_use_atr_partials
|
|
|
|
|
? StringFormat("A:%.0f F:%.0f", atr_trigger_l1, g_cfg_partial_trigger_1)
|
|
|
|
|
: StringFormat("F:%.0f", g_cfg_partial_trigger_1);
|
2026-03-24 23:54:05 +00:00
|
|
|
UpdateLabel(g_dashboard_prefix + "ATR_STATE",
|
2026-03-25 12:49:17 +00:00
|
|
|
StringFormat("L1 %s | cur %.0f | ret %.1f%% | %s",
|
|
|
|
|
trig_label, current_profit_points, retracement_pct, gate_status));
|
2026-03-24 23:54:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show concise runner count across advanced phases.
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "RUNNERS",
|
|
|
|
|
StringFormat("Runners: %d", phase_count[PHASE_RUNNER] + phase_count[PHASE_EXTREME]));
|
|
|
|
|
|
|
|
|
|
// Update phase lock statistics
|
|
|
|
|
int locked_positions = 0;
|
|
|
|
|
double total_locked_profit = 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(ticket > 0)
|
|
|
|
|
{
|
|
|
|
|
double protected_profit, peak_profit, retracement_pct;
|
|
|
|
|
g_profit_max.GetProtectionStatus(ticket, protected_profit, peak_profit, retracement_pct);
|
|
|
|
|
|
|
|
|
|
if(protected_profit > 0)
|
|
|
|
|
{
|
|
|
|
|
locked_positions++;
|
|
|
|
|
total_locked_profit += protected_profit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateLabel(g_dashboard_prefix + "LOCKED_MIN",
|
|
|
|
|
StringFormat("Min Locked: %.0f pts", total_locked_profit));
|
|
|
|
|
color lock_color = locked_positions > 0 ? clrLime : clrGray;
|
|
|
|
|
ObjectSetInteger(0, g_dashboard_prefix + "LOCKED_MIN", OBJPROP_COLOR, lock_color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update comment if dashboard not shown
|
|
|
|
|
if(!InpShowDashboard)
|
|
|
|
|
{
|
|
|
|
|
Comment(StringFormat(
|
|
|
|
|
"PME v1.5 | %s | Active:%d | P/L:%.2f | Eff:%.1f%% | Trail:%.0f",
|
|
|
|
|
GetStartupProfileName(),
|
|
|
|
|
g_manager.GetManagedCount(),
|
|
|
|
|
g_manager.GetTotalProfit(),
|
|
|
|
|
metrics.management_efficiency,
|
|
|
|
|
g_cfg_trail_start
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Remove Dashboard |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void RemoveDashboard()
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < g_dashboard_objects + 10; i++)
|
|
|
|
|
{
|
|
|
|
|
string name = g_dashboard_prefix + IntegerToString(i);
|
|
|
|
|
ObjectDelete(0, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove named objects
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "BG");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "HEADER");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "LINE1");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "STATUS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "ACTIVE");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "CLOSED");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "RISK");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "EXPOSURE");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "DRAWDOWN");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "MGMT");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "TRAILS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "PARTIALS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "RUNNERS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "CFG_HEADER");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "EFF_TRAIL");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "EFF_PARTIALS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "ATR_STATE");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "SYM_CLASS");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "EFFICIENCY");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "LOCKED_MIN");
|
|
|
|
|
ObjectDelete(0, g_dashboard_prefix + "PROFILE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Create Label |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CreateLabel(string name, int x, int y, string text, color clr, int size)
|
|
|
|
|
{
|
|
|
|
|
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
|
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
|
|
|
|
ObjectSetString(0, name, OBJPROP_FONT, "Consolas");
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Update Label |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateLabel(string name, string text)
|
|
|
|
|
{
|
|
|
|
|
if(ObjectFind(0, name) >= 0)
|
|
|
|
|
{
|
|
|
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Create Rectangle |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CreateRectangle(string name, int x, int y, int width, int height,
|
|
|
|
|
color clr, ENUM_LINE_STYLE style, int line_width)
|
|
|
|
|
{
|
|
|
|
|
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, line_width);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, false);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Log Configuration |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void LogConfiguration()
|
|
|
|
|
{
|
|
|
|
|
if(g_utils == NULL) return;
|
|
|
|
|
|
|
|
|
|
g_utils.Log("=== PME Configuration ===", LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Magic Filter: %d", InpMagicFilter), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Max Loss/Trade: %.2f%%", InpMaxLossPerTrade), 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("Trailing: %s", TrailingMethodToString(InpTrailingMethod)), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Startup Profile: %s",
|
|
|
|
|
InpStartupProfile == STARTUP_PROFILE_SAFE ? "Safe" :
|
|
|
|
|
(InpStartupProfile == STARTUP_PROFILE_AGGRESSIVE ? "Aggressive" : "Custom")), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Effective Trail: start=%.0f distance=%.0f step=%.0f",
|
|
|
|
|
g_cfg_trail_start, g_cfg_trail_distance, g_cfg_trail_step), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Partial Close: %s", InpPartialEnabled ? "Enabled" : "Disabled"), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Partial Engine: %s",
|
|
|
|
|
InpPartialEngine == PARTIAL_ENGINE_PROFIT_MAX ? "ProfitMaximizer" : "PositionManager"), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Effective Partials: T1=%.0f(%.0f%%) T2=%.0f(%.0f%%) T3=%.0f(%.0f%%) T4=%.0f(%.0f%%)",
|
|
|
|
|
g_cfg_partial_trigger_1, g_cfg_partial_percent_1,
|
|
|
|
|
g_cfg_partial_trigger_2, g_cfg_partial_percent_2,
|
|
|
|
|
g_cfg_partial_trigger_3, g_cfg_partial_percent_3,
|
|
|
|
|
g_cfg_partial_trigger_4, g_cfg_partial_percent_4), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("ATR Partials: %s | Mults=%.1f/%.1f/%.1f/%.1f",
|
|
|
|
|
g_cfg_use_atr_partials ? "ON" : "OFF",
|
|
|
|
|
g_cfg_partial_atr_mult_1, g_cfg_partial_atr_mult_2,
|
|
|
|
|
g_cfg_partial_atr_mult_3, g_cfg_partial_atr_mult_4), LOG_INFO);
|
2026-03-25 12:49:17 +00:00
|
|
|
g_utils.Log(StringFormat("Retracement Gate: %s | MaxRetr=%.1f%% | Buffer=%.1f%% above trigger",
|
2026-03-24 23:54:05 +00:00
|
|
|
g_cfg_retracement_gate ? "ON" : "OFF",
|
|
|
|
|
g_cfg_max_retracement_for_partial,
|
|
|
|
|
g_cfg_partial_profit_buffer), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Symbol Class Gate Matrix: Major=%.1f%%/%.1f pts | Cross=%.1f%%/%.1f pts",
|
|
|
|
|
g_cfg_major_max_retracement_for_partial,
|
|
|
|
|
g_cfg_major_partial_profit_buffer,
|
|
|
|
|
g_cfg_cross_max_retracement_for_partial,
|
|
|
|
|
g_cfg_cross_partial_profit_buffer), LOG_INFO);
|
|
|
|
|
g_utils.Log(StringFormat("Start Balance: %.2f", g_session_start_balance), LOG_INFO);
|
|
|
|
|
g_utils.Log("========================", LOG_INFO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Log Status |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void LogStatus()
|
|
|
|
|
{
|
|
|
|
|
if(g_utils == NULL || g_manager == NULL) return;
|
|
|
|
|
|
|
|
|
|
SessionMetrics metrics;
|
|
|
|
|
g_manager.GetSessionMetrics(metrics);
|
|
|
|
|
|
|
|
|
|
g_utils.Log(StringFormat(
|
|
|
|
|
"Status: Active=%d | Closed=%d | BE=%d | Trails=%d | Saved=%.2f | Captured=%.2f",
|
|
|
|
|
g_manager.GetManagedCount(),
|
|
|
|
|
metrics.positions_closed,
|
|
|
|
|
metrics.breakevens_applied,
|
|
|
|
|
metrics.trails_activated,
|
|
|
|
|
metrics.total_prevented_loss,
|
|
|
|
|
metrics.total_captured_profit
|
|
|
|
|
), LOG_INFO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Save Snapshot |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void SaveSnapshot()
|
|
|
|
|
{
|
|
|
|
|
if(g_utils == NULL || g_manager == NULL) return;
|
|
|
|
|
|
|
|
|
|
SessionMetrics metrics;
|
|
|
|
|
g_manager.GetSessionMetrics(metrics);
|
|
|
|
|
|
|
|
|
|
// Use filename-safe date format
|
|
|
|
|
MqlDateTime dt;
|
|
|
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
|
|
|
string filename = StringFormat("PME_Snapshot_%04d%02d%02d.csv",
|
|
|
|
|
dt.year, dt.mon, dt.day);
|
|
|
|
|
|
|
|
|
|
// CSV content with safe time format
|
|
|
|
|
string time_str = StringFormat("%04d-%02d-%02d %02d:%02d",
|
|
|
|
|
dt.year, dt.mon, dt.day, dt.hour, dt.min);
|
|
|
|
|
|
|
|
|
|
string content = StringFormat(
|
|
|
|
|
"%s,%d,%d,%d,%d,%d,%.2f,%.2f,%.2f,%.2f\n",
|
|
|
|
|
time_str,
|
|
|
|
|
g_manager.GetManagedCount(),
|
|
|
|
|
metrics.positions_closed,
|
|
|
|
|
metrics.breakevens_applied,
|
|
|
|
|
metrics.trails_activated,
|
|
|
|
|
metrics.partial_closes,
|
|
|
|
|
metrics.total_prevented_loss,
|
|
|
|
|
metrics.total_captured_profit,
|
|
|
|
|
metrics.management_efficiency,
|
|
|
|
|
AccountInfoDouble(ACCOUNT_BALANCE)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
g_utils.SaveToFile(filename, content, true); // Append mode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Generate Final Report |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void GenerateFinalReport()
|
|
|
|
|
{
|
|
|
|
|
if(g_utils == NULL || g_manager == NULL) return;
|
|
|
|
|
|
|
|
|
|
SessionMetrics metrics;
|
|
|
|
|
g_manager.GetSessionMetrics(metrics);
|
|
|
|
|
|
|
|
|
|
double session_duration = (TimeCurrent() - g_session_start) / 3600.0; // Hours
|
|
|
|
|
double final_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
|
double session_profit = final_balance - g_session_start_balance;
|
|
|
|
|
|
|
|
|
|
string report = "\n";
|
|
|
|
|
report += "==================================================\n";
|
|
|
|
|
report += " ERMT PME - Final Session Report\n";
|
|
|
|
|
report += "==================================================\n";
|
|
|
|
|
report += StringFormat("Session Duration: %.1f hours\n", session_duration);
|
|
|
|
|
report += StringFormat("Starting Balance: $%.2f\n", g_session_start_balance);
|
|
|
|
|
report += StringFormat("Final Balance: $%.2f\n", final_balance);
|
|
|
|
|
report += StringFormat("Session P/L: $%.2f (%.2f%%)\n",
|
|
|
|
|
session_profit,
|
|
|
|
|
(session_profit / g_session_start_balance) * 100);
|
|
|
|
|
report += "\n";
|
|
|
|
|
report += "--- Management Statistics ---\n";
|
|
|
|
|
report += StringFormat("Positions Managed: %d\n", metrics.total_managed);
|
|
|
|
|
report += StringFormat("Positions Closed: %d\n", metrics.positions_closed);
|
|
|
|
|
report += StringFormat("Emergency Closes: %d\n", metrics.emergency_closes);
|
|
|
|
|
report += "\n";
|
|
|
|
|
report += "--- Actions Taken ---\n";
|
|
|
|
|
report += StringFormat("Stops Added: %d\n", metrics.stops_added);
|
|
|
|
|
report += StringFormat("Stops Adjusted: %d\n", metrics.stops_adjusted);
|
|
|
|
|
report += StringFormat("Breakevens Applied: %d\n", metrics.breakevens_applied);
|
|
|
|
|
report += StringFormat("Trails Activated: %d\n", metrics.trails_activated);
|
|
|
|
|
report += StringFormat("Partial Closes: %d\n", metrics.partial_closes);
|
|
|
|
|
report += StringFormat("Total Actions: %d\n", metrics.total_actions);
|
|
|
|
|
report += "\n";
|
|
|
|
|
report += "--- Performance Metrics ---\n";
|
|
|
|
|
report += StringFormat("Loss Prevented: $%.2f\n", metrics.total_prevented_loss);
|
|
|
|
|
report += StringFormat("Profit Captured: $%.2f\n", metrics.total_captured_profit);
|
|
|
|
|
report += StringFormat("Total Value Added: $%.2f\n",
|
|
|
|
|
metrics.total_prevented_loss + metrics.total_captured_profit);
|
|
|
|
|
report += StringFormat("Management Efficiency: %.2f%%\n", metrics.management_efficiency);
|
|
|
|
|
report += StringFormat("Success Rate: %.2f%%\n", metrics.success_rate);
|
|
|
|
|
report += "\n";
|
|
|
|
|
report += "--- Risk Metrics ---\n";
|
|
|
|
|
report += StringFormat("Risk Reduced: $%.2f\n", metrics.total_risk_reduced);
|
|
|
|
|
report += StringFormat("Avg Risk Reduction: %.2f%%\n", metrics.average_risk_reduction);
|
|
|
|
|
report += StringFormat("Max Single Risk: $%.2f\n", metrics.max_single_risk);
|
|
|
|
|
report += StringFormat("Final Exposure: $%.2f\n", metrics.current_exposure);
|
|
|
|
|
report += "==================================================\n";
|
|
|
|
|
|
|
|
|
|
g_utils.Log(report, LOG_INFO);
|
|
|
|
|
|
|
|
|
|
if(InpSaveReports)
|
|
|
|
|
{
|
|
|
|
|
string filename = StringFormat("PME_FinalReport_%s.txt",
|
|
|
|
|
TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES));
|
|
|
|
|
g_utils.SaveToFile(filename, report, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check Alerts |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CheckAlerts()
|
|
|
|
|
{
|
|
|
|
|
if(TimeCurrent() - g_last_alert < 60) return; // Max 1 alert per minute
|
|
|
|
|
|
|
|
|
|
// Check for positions without stops
|
|
|
|
|
SessionMetrics metrics;
|
|
|
|
|
g_manager.GetSessionMetrics(metrics);
|
|
|
|
|
|
|
|
|
|
// Alert conditions
|
|
|
|
|
bool alert_needed = false;
|
|
|
|
|
string alert_message = "";
|
|
|
|
|
|
|
|
|
|
// Check drawdown
|
|
|
|
|
double drawdown = g_risk.GetDrawdown();
|
|
|
|
|
if(drawdown > InpMaxDrawdown * 0.8) // 80% of max
|
|
|
|
|
{
|
|
|
|
|
alert_needed = true;
|
|
|
|
|
alert_message = StringFormat("Warning: Drawdown at %.1f%%", drawdown);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check daily loss
|
|
|
|
|
double daily_loss = g_risk.GetDailyLoss();
|
|
|
|
|
if(daily_loss > InpMaxDailyLoss * 0.8) // 80% of max
|
|
|
|
|
{
|
|
|
|
|
alert_needed = true;
|
|
|
|
|
alert_message = StringFormat("Warning: Daily loss at %.1f%%", daily_loss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(alert_needed)
|
|
|
|
|
{
|
|
|
|
|
SendAlert(alert_message);
|
|
|
|
|
g_last_alert = TimeCurrent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Send Alert |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void SendAlert(string message)
|
|
|
|
|
{
|
|
|
|
|
string full_message = StringFormat("PME Alert: %s", message);
|
|
|
|
|
|
|
|
|
|
// Terminal alert
|
|
|
|
|
Alert(full_message);
|
|
|
|
|
|
|
|
|
|
// Sound alert
|
|
|
|
|
if(InpSoundAlerts)
|
|
|
|
|
{
|
|
|
|
|
PlaySound("alert2.wav");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Log
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
g_utils.Log(full_message, LOG_WARNING);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_alert_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| 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 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void Cleanup()
|
|
|
|
|
{
|
|
|
|
|
if(g_manager != NULL)
|
|
|
|
|
{
|
|
|
|
|
delete g_manager;
|
|
|
|
|
g_manager = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(g_tech != NULL)
|
|
|
|
|
{
|
|
|
|
|
delete g_tech;
|
|
|
|
|
g_tech = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(g_risk != NULL)
|
|
|
|
|
{
|
|
|
|
|
delete g_risk;
|
|
|
|
|
g_risk = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(g_profit_max != NULL)
|
|
|
|
|
{
|
|
|
|
|
delete g_profit_max;
|
|
|
|
|
g_profit_max = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(g_utils != NULL)
|
|
|
|
|
{
|
|
|
|
|
delete g_utils;
|
|
|
|
|
g_utils = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|