341 lines
12 KiB
Markdown
341 lines
12 KiB
Markdown
|
|
# ERMT PME v1.3 - Integration Fix
|
||
|
|
|
||
|
|
## 🐛 **Problem Identified**
|
||
|
|
|
||
|
|
After implementing v1.3 solutions, partial closures and dynamic ATR breathing room were not functional because:
|
||
|
|
|
||
|
|
1. **Missing Configuration Bridge**: Input parameters (`InpUseDynamicPartials`, `InpUseATRBreathingRoom`, `InpFloorOnlyMode`) were defined but never passed to the ProfitMaximizer module
|
||
|
|
2. **No Partial Execution Logic**: ProfitMaximizer calculated optimal partials but never executed them
|
||
|
|
3. **Empty Configuration Function**: `ConfigureProfitMaximizer()` was essentially empty, not connecting EA inputs to module configuration
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ **Fixes Applied**
|
||
|
|
|
||
|
|
### **Fix 1: Added Configuration Setter Methods**
|
||
|
|
**File**: `Modules_PME/ProfitMaximizer_PME.mqh`
|
||
|
|
**Location**: After `InitializeConfig()` method (lines 221-287)
|
||
|
|
|
||
|
|
**Added Methods**:
|
||
|
|
```mql5
|
||
|
|
void SetFloorOnlyMode(bool enabled)
|
||
|
|
void SetDynamicPartials(bool enabled)
|
||
|
|
void SetATRBreathingRoom(bool enabled)
|
||
|
|
void SetPartialLevel(int level, double trigger, double percent)
|
||
|
|
void SetRunnerConfig(double runner_pct, double trail_mult, double exit_threshold)
|
||
|
|
void SetPhaseMinLock(ENUM_PROFIT_PHASE phase, double min_lock)
|
||
|
|
void SetPhaseLockBehavior(bool maintain_on_retreat, double retreat_multiplier, double breathing_room)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Purpose**: Allow the main EA to configure ProfitMaximizer with user input parameters
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### **Fix 2: Enhanced ConfigureProfitMaximizer()**
|
||
|
|
**File**: `ERMT_PME_1.3.mq5`
|
||
|
|
**Location**: Lines 296-337
|
||
|
|
|
||
|
|
**Before**:
|
||
|
|
```mql5
|
||
|
|
void ConfigureProfitMaximizer()
|
||
|
|
{
|
||
|
|
if(g_profit_max == NULL) return;
|
||
|
|
// Just logging, no actual configuration!
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**After**:
|
||
|
|
```mql5
|
||
|
|
void ConfigureProfitMaximizer()
|
||
|
|
{
|
||
|
|
if(g_profit_max == NULL) return;
|
||
|
|
|
||
|
|
// Pass v1.3 solution flags to ProfitMaximizer
|
||
|
|
g_profit_max.SetFloorOnlyMode(InpFloorOnlyMode);
|
||
|
|
g_profit_max.SetDynamicPartials(InpUseDynamicPartials);
|
||
|
|
g_profit_max.SetATRBreathingRoom(InpUseATRBreathingRoom);
|
||
|
|
|
||
|
|
// Configure optimized partial closure schedule (v1.3)
|
||
|
|
g_profit_max.SetPartialLevel(0, InpPartialTrigger1, InpPartialPercent1);
|
||
|
|
g_profit_max.SetPartialLevel(1, InpPartialTrigger2, InpPartialPercent2);
|
||
|
|
g_profit_max.SetPartialLevel(2, InpPartialTrigger3, InpPartialPercent3);
|
||
|
|
g_profit_max.SetPartialLevel(3, InpPartialTrigger4, InpPartialPercent4);
|
||
|
|
|
||
|
|
// Configure runner settings
|
||
|
|
g_profit_max.SetRunnerConfig(InpRunnerPercentage, InpRunnerTrailMultiplier, 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);
|
||
|
|
|
||
|
|
// Configure phase lock behavior
|
||
|
|
g_profit_max.SetPhaseLockBehavior(InpMaintainLockOnRetreat, InpRetreatLockMultiplier, InpPhaseLockBreathingRoom);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Result**: All user-configured parameters now properly propagate to ProfitMaximizer module
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### **Fix 3: Added ShouldExecutePartial() Method**
|
||
|
|
**File**: `Modules_PME/ProfitMaximizer_PME.mqh`
|
||
|
|
**Location**: Lines 289-343
|
||
|
|
|
||
|
|
**New Method**:
|
||
|
|
```mql5
|
||
|
|
bool ShouldExecutePartial(ulong ticket, double &out_percentage)
|
||
|
|
{
|
||
|
|
// Find tracker for position
|
||
|
|
int index = FindTracker(ticket);
|
||
|
|
if(index < 0) return false;
|
||
|
|
|
||
|
|
// Check if partials are enabled
|
||
|
|
if(!m_config.enabled) return false;
|
||
|
|
|
||
|
|
double current_profit = m_trackers[index].current_profit;
|
||
|
|
int partials_executed = m_trackers[index].partials_executed;
|
||
|
|
|
||
|
|
// Check each partial level in sequence
|
||
|
|
for(int i = 0; i < m_config.partial_levels && i < 10; i++)
|
||
|
|
{
|
||
|
|
if(i < partials_executed) continue; // Skip executed levels
|
||
|
|
|
||
|
|
double trigger = m_config.partial_triggers[i];
|
||
|
|
|
||
|
|
// Check if profit reached this trigger
|
||
|
|
if(current_profit >= trigger)
|
||
|
|
{
|
||
|
|
// Calculate optimal percentage (with dynamic adjustment if enabled)
|
||
|
|
if(m_config.use_dynamic_partials)
|
||
|
|
out_percentage = CalculateOptimalPartial(ticket, current_profit);
|
||
|
|
else
|
||
|
|
out_percentage = m_config.partial_percentages[i];
|
||
|
|
|
||
|
|
// Update partials executed count
|
||
|
|
m_trackers[index].partials_executed = i + 1;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Purpose**: Provides a clean interface for the main EA to check if partial closure should be executed and get the exact percentage to close
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### **Fix 4: Integrated Partial Execution in ApplyPhaseManagement()**
|
||
|
|
**File**: `ERMT_PME_1.3.mq5`
|
||
|
|
**Location**: Lines 394-419 (inserted after phase lock logic)
|
||
|
|
|
||
|
|
**Added Code**:
|
||
|
|
```mql5
|
||
|
|
// === V1.3: Check and execute partial closures ===
|
||
|
|
if(InpPartialEnabled && 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))
|
||
|
|
{
|
||
|
|
g_performance.partial_closes++;
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note**: `PartialClose(ticket, percentage)` accepts 2 parameters only - the ticket and percentage to close. Volume validation is handled internally.
|
||
|
|
|
||
|
|
**Purpose**: Actively executes partial closures when conditions are met, completing the integration
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔄 **Execution Flow (Fixed)**
|
||
|
|
|
||
|
|
### **Before Fix**:
|
||
|
|
```
|
||
|
|
OnInit()
|
||
|
|
→ ConfigureProfitMaximizer() [Did nothing]
|
||
|
|
|
||
|
|
OnTick()
|
||
|
|
→ ApplyPhaseManagement(ticket)
|
||
|
|
→ AnalyzePosition() ✅ Works
|
||
|
|
→ GetPhaseProtectionStop() ✅ Works
|
||
|
|
→ [No partial execution] ❌ Missing!
|
||
|
|
```
|
||
|
|
|
||
|
|
### **After Fix**:
|
||
|
|
```
|
||
|
|
OnInit()
|
||
|
|
→ ConfigureProfitMaximizer()
|
||
|
|
→ SetFloorOnlyMode() ✅ Configured
|
||
|
|
→ SetDynamicPartials() ✅ Configured
|
||
|
|
→ SetATRBreathingRoom() ✅ Configured
|
||
|
|
→ SetPartialLevel() × 4 ✅ Configured
|
||
|
|
→ SetRunnerConfig() ✅ Configured
|
||
|
|
→ SetPhaseMinLock() × 5 ✅ Configured
|
||
|
|
|
||
|
|
OnTick()
|
||
|
|
→ ApplyPhaseManagement(ticket)
|
||
|
|
→ AnalyzePosition() ✅ Tracks profit/phase
|
||
|
|
→ GetPhaseProtectionStop() ✅ Applies phase floors
|
||
|
|
→ ShouldExecutePartial() ✅ Checks triggers
|
||
|
|
→ CalculateOptimalPartial() ✅ Uses dynamic adjustment
|
||
|
|
→ PositionManager.PartialClose() ✅ Executes closure
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 **What Now Works**
|
||
|
|
|
||
|
|
### ✅ **Solution 1: Floor-Only Phase Locks**
|
||
|
|
- **Status**: Fully functional
|
||
|
|
- **Configuration**: `InpFloorOnlyMode = true` → passes to `m_config.floor_only_mode`
|
||
|
|
- **Execution**: `GetPhaseProtectionStop()` uses floor-only logic
|
||
|
|
- **Verification**: Check logs for "Phase floor set at..." messages
|
||
|
|
|
||
|
|
### ✅ **Solution 2: Dynamic Partial Closures**
|
||
|
|
- **Status**: Now functional (was broken)
|
||
|
|
- **Configuration**: `InpUseDynamicPartials = true` → passes to `m_config.use_dynamic_partials`
|
||
|
|
- **Execution**:
|
||
|
|
- Checks profit against triggers: 40/80/150/300 pts
|
||
|
|
- Calls `CalculateOptimalPartial()` with 4-factor adjustment
|
||
|
|
- Executes via `PositionManager.PartialClose()`
|
||
|
|
- **Verification**: Check logs for "Executed X% partial close at Y pts" messages
|
||
|
|
|
||
|
|
### ✅ **Solution 3: ATR Breathing Room**
|
||
|
|
- **Status**: Fully functional
|
||
|
|
- **Configuration**: `InpUseATRBreathingRoom = false` (off by default) → passes to `m_config.use_atr_breathing_room`
|
||
|
|
- **Execution**: `CalculateDynamicBreathingRoom()` uses ATR when enabled
|
||
|
|
- **Verification**: Enable and check logs for "ATR breathing room calculated: X%" messages
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 **Testing Checklist**
|
||
|
|
|
||
|
|
Before deploying to demo/live:
|
||
|
|
|
||
|
|
- [ ] **Compile**: Ensure no compilation errors in MetaEditor
|
||
|
|
- [ ] **Attach to Demo Chart**: Use demo account first
|
||
|
|
- [ ] **Check Initialization Logs**: Should see:
|
||
|
|
```
|
||
|
|
Floor-Only Mode: ENABLED
|
||
|
|
Dynamic Partials: ENABLED
|
||
|
|
ATR Breathing Room: DISABLED
|
||
|
|
Partial Level 0: Trigger=40 pts, Close=25%
|
||
|
|
Partial Level 1: Trigger=80 pts, Close=25%
|
||
|
|
Partial Level 2: Trigger=150 pts, Close=25%
|
||
|
|
Partial Level 3: Trigger=300 pts, Close=15%
|
||
|
|
Runner Config: 10% size, 3.0x trail, 500 exit
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Wait for Position to Reach 40pts**: Should execute first partial (25%)
|
||
|
|
- [ ] **Check Position Volume**: Should reduce by 25%
|
||
|
|
- [ ] **Check Logs**: Should show "Executed 25% partial close at 40 pts"
|
||
|
|
- [ ] **Monitor Dashboard**: "Partials" counter should increment
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 **Configuration Defaults (v1.3)**
|
||
|
|
|
||
|
|
| Parameter | Value | Purpose |
|
||
|
|
|-----------|-------|---------|
|
||
|
|
| `InpFloorOnlyMode` | `true` | Use floor-only phase locks (recommended) |
|
||
|
|
| `InpUseDynamicPartials` | `true` | Enable 4-factor dynamic partial adjustment |
|
||
|
|
| `InpUseATRBreathingRoom` | `false` | Disable ATR breathing room (conservative) |
|
||
|
|
| `InpPartialEnabled` | `true` | Master switch for partial closures |
|
||
|
|
| `InpPartialTrigger1-4` | 40/80/150/300 | Earlier triggers than v1.2 |
|
||
|
|
| `InpPartialPercent1-4` | 25/25/25/15 | Bank 75% by 150pts |
|
||
|
|
| `InpRunnerPercentage` | `10` | Keep 10% as runner (vs 15% in v1.2) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔍 **Diagnostic Commands**
|
||
|
|
|
||
|
|
If partials still don't execute:
|
||
|
|
|
||
|
|
1. **Check if ProfitMaximizer is initialized**:
|
||
|
|
```mql5
|
||
|
|
if(g_profit_max == NULL)
|
||
|
|
Print("ERROR: ProfitMaximizer not initialized");
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Check if partial config is loaded**:
|
||
|
|
```mql5
|
||
|
|
// Add to ConfigureProfitMaximizer():
|
||
|
|
Print("Partial triggers: ", InpPartialTrigger1, "/", InpPartialTrigger2,
|
||
|
|
"/", InpPartialTrigger3, "/", InpPartialTrigger4);
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Check if ShouldExecutePartial() is called**:
|
||
|
|
```mql5
|
||
|
|
// Add debug log in ApplyPhaseManagement():
|
||
|
|
Print("Checking partials for ticket ", ticket, " at profit ", profit_points);
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Check minimum lot size**:
|
||
|
|
```mql5
|
||
|
|
Print("Position volume: ", PositionGetDouble(POSITION_VOLUME));
|
||
|
|
Print("Min volume: ", SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));
|
||
|
|
Print("Calculated partial volume: ", close_volume);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 **Expected Behavior After Fix**
|
||
|
|
|
||
|
|
### **Trade Example: Long EURUSD @ 1.1000**
|
||
|
|
|
||
|
|
| Profit | Event | v1.3 Action (Fixed) |
|
||
|
|
|--------|-------|---------------------|
|
||
|
|
| 40pts | Trigger 1 | ✅ Close 25% (dynamic adjusted 20-30%) |
|
||
|
|
| 50pts | Phase: PROTECTION | ✅ Floor set at 1.1010 (10pt minimum) |
|
||
|
|
| 80pts | Trigger 2 | ✅ Close 25% of remaining (now 56.25% closed total) |
|
||
|
|
| 100pts | Phase: MAXIMIZATION | ✅ Floor set at 1.1050 (50pt minimum) |
|
||
|
|
| 120pts | Peak | ✅ Adaptive trail active (phase-aware width) |
|
||
|
|
| 70pts | Retrace | ✅ Position stays open (above 50pt floor) |
|
||
|
|
| 150pts | Trigger 3 | ✅ Close 25% of remaining (now 75% closed) |
|
||
|
|
| 300pts | Trigger 4 | ✅ Close 15% of remaining (10% runner left) |
|
||
|
|
| 200pts | Phase: RUNNER | ✅ 3.0x ATR trail width for runner |
|
||
|
|
|
||
|
|
**v1.2 Result**: Closed at 70pts (retrace hit tightened lock)
|
||
|
|
**v1.3 Result**: Still open with 10% runner + 75% profit banked
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📞 **Support**
|
||
|
|
|
||
|
|
If issues persist after applying these fixes:
|
||
|
|
|
||
|
|
1. Check Expert log (`/logs/` folder) for error messages
|
||
|
|
2. Verify all input parameters are set correctly
|
||
|
|
3. Ensure position volume is large enough for partials (≥ 4× minimum lot size)
|
||
|
|
4. Test with `InpLogLevel = LOG_DEBUG` for verbose output
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Fix Version**: 1.3.1
|
||
|
|
**Date**: 2025-12-03
|
||
|
|
**Status**: ✅ Partial Closures & Dynamic ATR Now Functional
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**May your partials execute smoothly and your runners run free! 🚀**
|