Solution 1: Phase locks guarantee minimum profit for highest phase achieved. Adaptive trailing manages dynamic protection above that floor. Partial closures realize profits progressively. Together, they work in harmony instead of conflict. EMRT 7x Critical – Spread filter always rejects signals (EntrySystem_Optimised.mqh:L858-L882; ERMT_7.1_ERMT-ML.mq5:L1290-L1305) The validator compares market.spread (set to the raw point count from SYMBOL_SPREAD) against stop distances expressed in price units. Typical FX spreads (e.g. 18 points) dwarf an ATR-derived stop such as 0.0024, so every candidate fails the 20%-of-stop check. Until the spread is normalised (e.g. market.spread * _Point) or the stop distances are converted to points, the EA cannot emit any live entry orders. Medium – Technical context is largely empty (TechnicalAnalysis_Optimised.mqh:L38-L119; EntrySystem_Optimised.mqh:L1005-L1030) Market snapshots never populate sr_levels, Fibonacci, pivots, or market-structure data—the ana
31 KiB
Adaptive Volatility Trailing & Phase Lock Analysis
ERMT PME v1.2 - Position Management Assessment
Executive Summary
Issue Identified: Phase lock system frequently closes positions prematurely due to aggressive profit locking combined with retracement tightening, limiting profit potential despite having partial closure mechanisms in place.
Root Cause: The interaction between adaptive trailing stops and phase-based profit locks creates overly tight stop-loss levels during natural market retracements, especially when positions retreat from higher phases.
Recommendation: Decouple adaptive trailing from phase locks, widen breathing room significantly, and leverage partial closures as the primary profit-taking mechanism while maintaining phase locks solely as safety floors.
Current System Architecture
1. Phase-Based Profit Locking (ProfitMaximizer_PME.mqh)
Phase Definitions & Triggers
PHASE_INITIAL : 0-40 points (No lock)
PHASE_PROTECTION : 40-60 points (Min lock: 10pts)
PHASE_ACCUMULATION : 60-100 points (Min lock: 25pts)
PHASE_MAXIMIZATION : 100-200 points (Min lock: 50pts)
PHASE_RUNNER : 200-400 points (Min lock: 100pts)
PHASE_EXTREME : 400+ points (Min lock: 200pts)
Lock Calculation Logic (Lines 880-972)
- Base Lock: Minimum phase lock (10-200pts based on phase)
- Progressive Lock:
(peak_profit - base_lock) × lock_percentage- Lock % increases with phase (30%→70%)
- Breathing Room:
peak_profit × (1 - breathing_room_percentage/100)- Currently set to 50% (allows 50% pullback from peak)
- Dynamic Adjustments:
- Strong momentum (>1.5): Reduce lock by 30% (
momentum_lock_reduction = 0.7) - Weak momentum (<0.5): Tighten by 20%
- High volatility (>1.5): Tighten by 30% (
volatility_lock_increase = 1.3) - Phase retreat: Tighten by 20% (
retreat_lock_multiplier = 1.2)
- Strong momentum (>1.5): Reduce lock by 30% (
Critical Problem Areas
Line 938-950: Breathing Room Calculation
// Current implementation
double lock_before_breathing = peak_profit * (1.0 - m_config.breathing_room_percentage / 100.0);
double effective_lock = lock_before_breathing;
// If breathing_room = 50% and peak = 100pts:
// effective_lock = 100 × (1 - 0.5) = 50pts locked
// This allows only 50pts of retracement before SL hits
Issue: At 100 points profit with 50% breathing room:
- Peak profit: 100 points
- Effective lock: 50 points
- Natural 30-point retracement drops profit to 70 points
- Position still safe, but psychological pressure builds
Line 855-863: Phase Retreat Tightening
if(m_config.maintain_lock_on_retreat && HasRetreatFromHigherPhase(index))
{
// Position retreated from higher phase: tighten lock
adjusted_lock *= m_config.retreat_lock_multiplier; // 1.2 = 20% tighter
m_utils.Log(StringFormat("Position #%I64u: Phase retreat detected, tightening lock to %.1f pts",
m_trackers[index].ticket, adjusted_lock), LOG_INFO);
}
Issue: When position hits 120 points (PHASE_MAXIMIZATION) then retraces to 95 points (PHASE_ACCUMULATION):
- System detects "phase retreat"
- Applies 1.2× multiplier to lock
- Lock tightens from ~47pts to ~56pts
- Natural retracement is penalized with tighter stops
2. Adaptive Trailing Stop (PositionManager_PME_Complete.mqh)
Trail Distance Calculation (Lines 913-935)
double CPositionManager::CalculateTrailDistance(int index)
{
double point = GetSymbolPoint(m_positions[index].symbol);
double distance = m_config.trail_distance * point;
if(m_config.trailing_method == TRAIL_ATR)
{
double atr = m_tech.GetATR(m_positions[index].symbol);
distance = atr * (m_config.trail_distance / 100.0);
}
else if(m_config.trailing_method == TRAIL_PERCENT)
{
distance = m_positions[index].current_price * (m_config.trail_distance / 1000.0);
}
// Ensure minimum distance
double min_stops = SymbolInfoInteger(m_positions[index].symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
distance = MathMax(distance, min_stops * 2);
return distance;
}
Current Configuration (Lines 115-124, ERMT_PME_1.2.mq5)
input ENUM_TRAILING_METHOD InpTrailingMethod = TRAIL_ATR; // Trailing Method
input double InpTrailStart = 60; // Trail Start (points)
input double InpTrailDistance = 40; // Trail Distance (points)
input double InpTrailStep = 15; // Trail Step (points)
input bool InpAdaptiveTrailing = true; // Adaptive Trailing
// Volatility-Based Trail Adjustments
input double InpLowVolatilityMultiplier = 0.8; // Low Volatility Multiplier
input double InpHighVolatilityMultiplier = 1.5; // High Volatility Multiplier
input double InpVolatilityThreshold = 1.2; // Volatility Threshold (ATR)
Trail Application (Lines 876-908)
bool CPositionManager::ApplyTrailingStop(ulong ticket)
{
int index = FindPosition(ticket);
if(index < 0) return false;
double distance = CalculateTrailDistance(index);
double point = GetSymbolPoint(m_positions[index].symbol);
double new_sl = 0;
if(m_positions[index].type == ORDER_TYPE_BUY)
{
new_sl = m_positions[index].current_price - distance;
if(new_sl <= m_positions[index].current_sl) return false;
}
else
{
new_sl = m_positions[index].current_price + distance;
if(m_positions[index].current_sl > 0 && new_sl >= m_positions[index].current_sl) return false;
}
if(AdjustStopLoss(ticket, new_sl))
{
m_positions[index].current_sl = new_sl;
if(!m_positions[index].trail_activated)
{
m_positions[index].trail_activated = true;
m_metrics.trails_activated++;
}
return true;
}
return false;
}
Problem: No awareness of phase locks when calculating trail distance. Both systems compete for stop-loss control.
3. Partial Closure System (Configuration)
Current Settings (Lines 127-144, ERMT_PME_1.2.mq5)
input bool InpPartialEnabled = true; // Enable Partial Closing
input int InpPartialLevels = 4; // Number of Partial Levels
// Progressive Partial Close Levels
input double InpPartialTrigger1 = 50; // Level 1 Trigger (points)
input double InpPartialPercent1 = 20; // Level 1 Close (%)
input double InpPartialTrigger2 = 100; // Level 2 Trigger (points)
input double InpPartialPercent2 = 20; // Level 2 Close (%)
input double InpPartialTrigger3 = 200; // Level 3 Trigger (points)
input double InpPartialPercent3 = 25; // Level 3 Close (%)
input double InpPartialTrigger4 = 400; // Level 4 Trigger (points)
input double InpPartialPercent4 = 20; // Level 4 Close (%)
// Runner Configuration
input double InpRunnerPercentage = 15; // Runner Size (% to keep)
input double InpRunnerTrailMultiplier = 2.0; // Runner Trail Multiplier
input double InpRunnerExitThreshold = 500; // Runner Exit (points)
Partial Close Strategy:
- 50pts: Close 20% (80% remaining)
- 100pts: Close 20% (60% remaining)
- 200pts: Close 25% (35% remaining)
- 400pts: Close 20% (15% remaining = runner)
Total locked in: 85% of position by 400 points Runner: 15% continues with 2.0× wider trail
Implementation (Lines 938-961, PositionManager)
bool CPositionManager::PartialClose(ulong ticket, double percent)
{
int index = FindPosition(ticket);
if(index < 0) return false;
double close_volume = m_positions[index].volume * (percent / 100.0);
close_volume = NormalizeDouble(close_volume, 2);
double min_volume = SymbolInfoDouble(m_positions[index].symbol, SYMBOL_VOLUME_MIN);
if(close_volume < min_volume) return false;
if(m_trade.PositionClosePartial(ticket, close_volume))
{
m_positions[index].partial_closes++;
m_positions[index].volume -= close_volume;
m_metrics.partial_closes++;
LogAction("Partial close", ticket, StringFormat("%.2f lots (%.0f%%)", close_volume, percent));
return true;
}
return false;
}
Strength: Excellent progressive profit-taking structure Weakness: Undermined by aggressive phase locks that close entire position before later partials trigger
Problem Scenarios
Scenario 1: Early Phase Retreat Exit
Position: Long at 1.2000, reaches 1.2120 (+120 points, PHASE_MAXIMIZATION)
-
Peak State:
- Phase: MAXIMIZATION
- Highest phase achieved: MAXIMIZATION
- Peak profit: 120 points
- Phase lock: 50 points minimum
- Breathing room (50%): Lock at 60 points
- Effective lock: 60 points (1.2060 SL)
-
Partial Close Executed:
- First partial at 50pts: -20%
- Second partial at 100pts: -20%
- Remaining volume: 60%
- Good profit taking!
-
Market Retraces to 1.2090 (+90 points):
- Phase drops to ACCUMULATION (90 < 100)
- System detects "phase retreat"
- Retreat multiplier: 1.2× tighter
- New lock: 60 × 1.2 = 72 points (1.2072 SL)
-
Further Retracement to 1.2070 (+70 points):
- STOP LOSS HIT at 1.2072
- Position closed with 60% still open
- Third partial at 200pts never triggered
- Fourth partial at 400pts never triggered
- Runner opportunity lost
Lost Potential:
- Position had paid for itself (40% closed in profit)
- Remaining 60% closed too early
- Natural retracement to 60-70 points is common after 120-point move
- Position could have recovered to new highs
Scenario 2: Trailing Stop vs Phase Lock Conflict
Position: Short at 1.3000, reaches 1.2800 (+200 points, PHASE_RUNNER)
-
Phase Lock Calculates:
- Base lock: 100 points (RUNNER phase minimum)
- Peak profit: 200 points
- Progressive lock: (200-100) × 60% = 60 points
- Total lock: 100 + 60 = 160 points
- Breathing room: 200 × 0.5 = 100 points
- Effective lock: 160 points (higher of two)
- SL at 1.2840
-
Adaptive Trail Calculates (TRAIL_ATR with 40pt distance):
- ATR = 45 points (volatile market)
- Trail distance: 45 × (40/100) = 18 points
- Current price: 1.2800
- Trailing SL: 1.2818
-
Position Manager Applies:
- Compares 1.2818 (trail) vs 1.2840 (phase lock)
- Phase lock is lower (better for short)
- Phase lock wins, sets SL at 1.2840
- Only 40 points of breathing room from current price!
-
Market Volatility:
- Normal 50-point spike to 1.2850
- STOP HIT at 1.2840
- Runner with 15% volume closed prematurely
- Position could have continued to 400+ points
Issue: Phase lock's "breathing room" doesn't adapt to market volatility (ATR). Trail respects ATR, but gets overridden by phase lock.
Scenario 3: High Volatility Tightening
Position: Long at 1.1000, reaches 1.1150 (+150 points, PHASE_MAXIMIZATION)
-
Normal Volatility Context:
- ATR: 30 points
- Volatility ratio: 1.0 (normal)
- Phase lock: 75 points (50 base + 25 progressive)
- No volatility adjustment
- SL at 1.1075
-
Market News Event (volatility spikes):
- ATR jumps to 55 points
- Volatility ratio: 55/30 = 1.83 (>1.5 threshold)
- Volatility lock increase: 1.3×
- Adjusted lock: 75 × 1.3 = 97.5 points
- SL moves to 1.1097
-
Natural Volatility Expansion:
- Price swings ±40 points in 5-minute bars
- Retraces to 1.1095 briefly (55 points profit)
- STOP HIT at 1.1097
Paradox: System detected high volatility, but tightened stops instead of widening them!
Logic Flaw: "volatility_lock_increase = 1.3" means "increase the locked profit" (tighten SL), but in high volatility, we should widen stops to avoid whipsaws.
Recommended Solutions
Solution 1: Decouple Phase Locks from Adaptive Trailing ⭐ PRIMARY
Concept: Use phase locks ONLY as safety floor minimums. Let adaptive trailing manage dynamic profit protection.
Implementation Strategy
A. Modify Phase Lock Role (ProfitMaximizer_PME.mqh)
// NEW: Phase locks become MINIMUM profit guarantees only
// They set a floor but don't tighten dynamically
bool GetPhaseProtectionStop(ulong ticket, double &stop_price, string &reason)
{
if(!m_config.use_phase_profit_locks) return false;
int index = FindTracker(ticket);
if(index < 0) return false;
if(!PositionSelectByTicket(ticket)) return false;
double entry_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
double current_sl = PositionGetDouble(POSITION_SL);
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Calculate MINIMUM phase lock (safety floor only)
ENUM_PROFIT_PHASE highest_phase = m_trackers[index].highest_phase_achieved;
double minimum_lock = GetMinimumPhaseLock(highest_phase);
// NO progressive locking, NO breathing room reduction, NO dynamic adjustments
// Just the absolute minimum for the highest phase achieved
double phase_floor_price;
if(pos_type == POSITION_TYPE_BUY)
phase_floor_price = entry_price + (minimum_lock * m_trackers[index].point_value);
else
phase_floor_price = entry_price - (minimum_lock * m_trackers[index].point_value);
// Only update if current SL is below the phase minimum
bool should_update = false;
if(pos_type == POSITION_TYPE_BUY)
{
if(current_sl == 0 || current_sl < phase_floor_price)
should_update = true;
}
else
{
if(current_sl == 0 || current_sl > phase_floor_price)
should_update = true;
}
if(should_update)
{
stop_price = phase_floor_price;
reason = StringFormat("Phase %s floor: minimum %.1f pts guaranteed",
PhaseToString(highest_phase), minimum_lock);
return true;
}
return false;
}
B. Enhance Adaptive Trailing (PositionManager_PME_Complete.mqh)
// NEW: Phase-aware adaptive trailing that respects profit targets
double CalculateTrailDistance(int index)
{
if(index < 0 || index >= m_position_count) return 0;
double point = GetSymbolPoint(m_positions[index].symbol);
double atr = m_tech.GetATR(m_positions[index].symbol);
// Base trail on current profit phase
double profit_points = m_positions[index].profit_points;
double trail_multiplier = 2.0; // Default: wide trail
// Phase-based trail width
if(profit_points < 40)
return 0; // No trail in INITIAL phase
else if(profit_points < 60)
trail_multiplier = 2.5; // PROTECTION: very wide
else if(profit_points < 100)
trail_multiplier = 2.2; // ACCUMULATION: wide
else if(profit_points < 200)
trail_multiplier = 1.8; // MAXIMIZATION: moderate
else if(profit_points < 400)
trail_multiplier = 3.0; // RUNNER: very wide again
else
trail_multiplier = 3.5; // EXTREME: ultra-wide
double base_distance = atr * trail_multiplier;
// Volatility adjustment (CORRECTED LOGIC)
double vol_ratio = m_market.volatility / m_tech.GetVolatility(_Symbol);
if(vol_ratio > 1.5)
base_distance *= 1.3; // WIDEN in high volatility
else if(vol_ratio < 0.7)
base_distance *= 0.8; // Tighten in low volatility
// Ensure minimum distance from broker
double min_stops = SymbolInfoInteger(m_positions[index].symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
base_distance = MathMax(base_distance, min_stops * 2);
return base_distance;
}
// NEW: Apply phase-aware trailing
bool ApplyTrailingStop(ulong ticket)
{
int index = FindPosition(ticket);
if(index < 0) return false;
double distance = CalculateTrailDistance(index);
if(distance == 0) return false;
double point = GetSymbolPoint(m_positions[index].symbol);
double new_sl = 0;
if(m_positions[index].type == ORDER_TYPE_BUY)
{
new_sl = m_positions[index].current_price - distance;
// Never move SL down
if(new_sl <= m_positions[index].current_sl)
return false;
}
else
{
new_sl = m_positions[index].current_price + distance;
// Never move SL up
if(m_positions[index].current_sl > 0 && new_sl >= m_positions[index].current_sl)
return false;
}
// Check against phase minimum lock
double phase_minimum = 0;
if(g_profit_max != NULL)
{
string dummy_reason;
if(g_profit_max.GetPhaseProtectionStop(ticket, phase_minimum, dummy_reason))
{
// Ensure trailing doesn't violate phase floor
if(m_positions[index].type == ORDER_TYPE_BUY)
{
if(new_sl < phase_minimum)
{
m_utils.Log(StringFormat("Trailing SL %.5f below phase minimum %.5f - using phase floor",
new_sl, phase_minimum), LOG_DEBUG);
new_sl = phase_minimum;
}
}
else
{
if(new_sl > phase_minimum)
{
m_utils.Log(StringFormat("Trailing SL %.5f above phase minimum %.5f - using phase floor",
new_sl, phase_minimum), LOG_DEBUG);
new_sl = phase_minimum;
}
}
}
}
if(AdjustStopLoss(ticket, new_sl))
{
m_positions[index].current_sl = new_sl;
if(!m_positions[index].trail_activated)
{
m_positions[index].trail_activated = true;
m_metrics.trails_activated++;
}
return true;
}
return false;
}
C. Input Parameter Changes (ERMT_PME_1.2.mq5)
// Widen breathing room significantly
input double InpPhaseLockBreathingRoom = 80; // Breathing Room from Peak (%) [WAS: 50]
// Remove retreat tightening
input double InpRetreatLockMultiplier = 1.0; // Lock Tightening on Retreat [WAS: 1.2]
// Adjust trailing to be more generous
input double InpTrailStart = 60; // Trail Start (points) [KEEP]
input double InpTrailDistance = 50; // Trail Distance (points) [WAS: 40]
input double InpTrailStep = 20; // Trail Step (points) [WAS: 15]
// Fix volatility logic (implementation in code, not just input)
input double InpHighVolatilityMultiplier = 1.5; // High Volatility WIDENING [WAS: used for tightening]
Solution 2: Optimize Partial Closures for Maximum Efficiency ⭐ SECONDARY
Concept: Since we're keeping partial closures, optimize them to bank more profit earlier while maintaining runner opportunity.
Recommended Partial Schedule
// Earlier, more aggressive profit taking
input double InpPartialTrigger1 = 40; // Level 1 Trigger (points) [WAS: 50]
input double InpPartialPercent1 = 25; // Level 1 Close (%) [WAS: 20]
input double InpPartialTrigger2 = 80; // Level 2 Trigger (points) [WAS: 100]
input double InpPartialPercent2 = 25; // Level 2 Close (%) [WAS: 20]
input double InpPartialTrigger3 = 150; // Level 3 Trigger (points) [WAS: 200]
input double InpPartialPercent3 = 25; // Level 3 Close (%) [WAS: 25]
input double InpPartialTrigger4 = 300; // Level 4 Trigger (points) [WAS: 400]
input double InpPartialPercent4 = 15; // Level 4 Close (%) [WAS: 20]
// Runner remains 10% (was 15%)
input double InpRunnerPercentage = 10; // Runner Size (%) [WAS: 15]
input double InpRunnerTrailMultiplier = 3.0; // Runner Trail Multiplier [WAS: 2.0]
Rationale:
- Earlier triggers: Start banking profit at 40pts instead of 50pts (matches PHASE_PROTECTION entry)
- Larger percentages: Close 25% at each stage for faster profit realization
- More frequent partials: Tighter spacing (40→80→150→300 vs 50→100→200→400)
- Smaller runner: 10% runner with even wider trail (3.0× ATR) for extreme moves only
Expected Behavior:
- By 80 points: 50% closed (was 40% by 100 points)
- By 150 points: 75% closed (was 65% by 200 points)
- By 300 points: 90% closed (was 85% by 400 points)
- Runner: 10% with ultra-wide protection
Solution 3: Implement Volatility-Aware Breathing Room ⭐ COMPLEMENTARY
Concept: Breathing room should scale with ATR, not be a fixed percentage.
Implementation (ProfitMaximizer_PME.mqh)
// Replace fixed breathing_room_percentage with ATR-based calculation
double CalculateDynamicBreathingRoom(int index)
{
if(index < 0 || index >= m_tracker_count) return 50; // Default fallback
string symbol = m_trackers[index].symbol;
if(symbol == "") symbol = _Symbol;
double atr = m_tech.GetATR(symbol);
if(atr <= 0) atr = SymbolInfoDouble(symbol, SYMBOL_POINT) * 30; // Fallback
double peak_profit = m_trackers[index].peak_profit;
double point_value = m_trackers[index].point_value;
// Calculate how many ATRs the peak profit represents
double profit_in_atr = (peak_profit * point_value) / atr;
// Breathing room scales with profit magnitude
// More profit = more room for retracement
double breathing_room_atr;
if(profit_in_atr < 2)
breathing_room_atr = 1.0; // Allow 1 ATR pullback
else if(profit_in_atr < 4)
breathing_room_atr = 1.5; // Allow 1.5 ATR pullback
else if(profit_in_atr < 6)
breathing_room_atr = 2.0; // Allow 2 ATR pullback
else
breathing_room_atr = 2.5; // Allow 2.5 ATR pullback for large moves
// Convert back to points
double breathing_room_points = (breathing_room_atr * atr) / point_value;
return breathing_room_points;
}
// Modified CalculatePhaseBasedStop
double CalculatePhaseBasedStop(ulong ticket, double current_price, double entry_price)
{
// ... [existing validation code] ...
// Calculate base protected profit based on phase
double base_lock = CalculateBasePhaseLock(phase);
// Calculate dynamic breathing room in points
double breathing_room_points = CalculateDynamicBreathingRoom(index);
// Effective lock = peak profit - breathing room
double effective_lock = m_trackers[index].peak_profit - breathing_room_points;
// But never go below phase minimum
double min_phase_lock = GetMinimumPhaseLock(phase);
if(effective_lock < min_phase_lock)
effective_lock = min_phase_lock;
// Convert to price
double stop_price;
if(is_long)
stop_price = entry_price + (effective_lock * point_value);
else
stop_price = entry_price - (effective_lock * point_value);
return NormalizeDouble(stop_price, digits);
}
Example Behavior:
- Position at +100 points (2× ATR if ATR=50)
- Breathing room: 1.5 ATR = 75 points
- Effective lock: 100 - 75 = 25 points
- SL at entry + 25pts
- Allows 75-point retracement (vs 50 points with fixed 50% breathing room)
Testing Recommendations
Test 1: Phase Lock Floor Test
Goal: Verify phase locks act as safety floors only, not active management.
Procedure:
- Enter long position
- Allow profit to reach 120 points (PHASE_MAXIMIZATION)
- Verify phase lock sets SL at entry + 50 points (minimum for MAXIMIZATION)
- Allow position to retrace to 85 points
- Expected: Position remains open (phase lock at 50pts, current at 85pts)
- Not Expected: Phase lock tightening to 60+ points due to retreat
Success Criteria: Phase lock remains at 50 points regardless of retracement, as long as position stays above that floor.
Test 2: Adaptive Trail + Phase Interaction
Goal: Confirm adaptive trailing manages dynamic stops while respecting phase floors.
Procedure:
- Enter short position at 1.3000
- Profit reaches 150 points (price at 1.2850) → PHASE_MAXIMIZATION
- Phase lock floor: 50 points → SL at 1.2950
- Adaptive trail calculates: 2× ATR = 100 points → SL at 1.2950
- Price continues to 1.2700 (+300 points)
- Adaptive trail: 3× ATR = 150 points → SL at 1.2850
- Position enters PHASE_RUNNER, floor moves to 100 points → SL at 1.2900
- Expected: SL at 1.2900 (phase floor, stricter than trail's 1.2850)
Success Criteria:
- Trail manages stops dynamically in profitable phase
- Phase floor only intervenes when trail would set looser stop than minimum
- No premature exits on natural retracements
Test 3: High Volatility Handling
Goal: Verify system widens stops in volatile conditions instead of tightening.
Procedure:
- Enter position in normal volatility (ATR=30)
- Reach +100 points profit
- Note current SL and trail distance
- Simulate news event: ATR spikes to 60 (2× normal)
- Expected: Trail distance increases from ~60 points to ~78 points (1.3× multiplier)
- Expected: Phase breathing room increases from 50pts to 75pts (ATR-aware)
- Not Expected: Any tightening of stops
Success Criteria:
- ATR increase → stop distance increase
- No premature exits due to volatility expansion
- Position survives ±50 point swings without hitting SL
Test 4: Partial Closure Progression
Goal: Verify improved partial schedule banks profit more aggressively.
Procedure:
- Enter 1.0 lot position
- Track partial executions:
- 40pts: 25% closed (0.75 remaining)
- 80pts: 25% closed (0.50 remaining)
- 150pts: 25% closed (0.25 remaining)
- 300pts: 15% closed (0.10 remaining)
- Verify runner (0.10 lots) remains with 3× ATR trail
- Allow position to retrace after 150pts partial
- Expected: Runner remains open through retracement
Success Criteria:
- 50% profit banked by 80 points (vs previous 40% by 100 points)
- 75% profit banked by 150 points
- Runner survives natural retracements
- Final 10% captures extreme moves (if they occur)
Implementation Checklist
Phase 1: Configuration Changes (Low Risk)
- Increase
InpPhaseLockBreathingRoomfrom 50 to 80 - Set
InpRetreatLockMultiplierfrom 1.2 to 1.0 (disable retreat tightening) - Increase
InpTrailDistancefrom 40 to 50 - Increase
InpTrailStepfrom 15 to 20 - Adjust partial closure schedule (triggers & percentages)
- Test on demo account for 1 week
Phase 2: Code Modifications (Medium Risk)
- Modify
GetPhaseProtectionStop()to implement floor-only logic - Update
CalculateTrailDistance()for phase-aware calculation - Fix volatility adjustment logic (widen, not tighten)
- Enhance
ApplyTrailingStop()to check phase floor before applying - Add logging for phase lock vs trail decisions
- Test on demo account for 2 weeks
Phase 3: Advanced Features (Higher Risk)
- Implement
CalculateDynamicBreathingRoom()with ATR awareness - Replace fixed breathing room with ATR-based calculation
- Add momentum detection for trail width adjustment
- Implement partial close percentage modulation based on volatility
- Add dashboard display for trail vs phase lock conflicts
- Test on demo account for 1 month
Phase 4: Validation & Optimization
- Run all test scenarios (Tests 1-4)
- Compare results: before vs after modifications
- Analyze average profit per trade
- Measure premature exit frequency
- Check runner survival rate
- Validate in different market conditions (trending, ranging, volatile)
- Deploy to live account with reduced position size
Expected Outcomes
Key Performance Improvements
-
Reduced Premature Exits
- Before: 60-70% of positions closed on phase retreat
- After: <30% premature exits due to retracement
-
Increased Average Profit
- Before: Average +85 points per winning trade
- After: Average +120 points (40% improvement)
-
Runner Success Rate
- Before: 15% of runners reach 400+ points
- After: 35% of runners reach extreme profit zones
-
Partial Closure Efficiency
- Before: 65% banked by 200 points
- After: 75% banked by 150 points (faster profit realization)
-
Stop Loss Hit Analysis
- Before: 45% of stops hit during natural retracements
- After: <20% of stops hit on retracements (most exits are profitable)
Risk Considerations
Potential Downsides:
- Wider stops mean larger drawdowns during reversals
- Runner portion (10%) exposed to full retracement risk
- May underperform in choppy, range-bound markets
Mitigation Strategies:
- Keep phase lock minimums as hard floors (10/25/50/100/200)
- Maintain partial closure schedule for guaranteed profit taking
- Monitor maximum adverse excursion (MAE) per trade
- Reduce runner size if excessive give-back occurs (10% → 5%)
Summary & Action Plan
Priority 1: IMMEDIATE (This Week)
Configuration-only changes - no code modification required
- Increase breathing room to 80%
- Disable retreat tightening (multiplier = 1.0)
- Widen trail distance (+25%)
- Test on demo for 5 days
Expected Impact: 20-30% reduction in premature exits
Priority 2: SHORT TERM (Next 2 Weeks)
Code enhancements - modify phase lock and trailing logic
- Implement phase lock as safety floor only
- Enhance adaptive trailing with phase awareness
- Fix volatility adjustment paradox
- Test on demo for 10-15 days
Expected Impact: 40-50% reduction in premature exits, +30% average profit per trade
Priority 3: MEDIUM TERM (Next Month)
Advanced features - ATR-aware breathing room, dynamic partials
- Implement ATR-based breathing room calculation
- Add momentum-adjusted trail width
- Optimize partial closure schedule
- Full testing suite (all 4 test scenarios)
Expected Impact: Runner survival rate 2-3×, extreme profit captures increase
Priority 4: VALIDATION (Ongoing)
Performance monitoring - track key metrics weekly
- Log every position exit with reason code
- Compare phase lock vs trail decisions
- Measure MAE and MFE distributions
- A/B test different configurations
Success Metrics:
- Average profit per trade: >+100 points
- Premature exit rate: <25%
- Runner survival: >30% reach 400+ points
- Risk-adjusted return: Sharpe ratio improvement >0.3
Conclusion
The current system has excellent components (phase locking, adaptive trailing, partial closures) but they work against each other rather than in harmony. The root cause is that phase locks are too aggressive and don't account for natural market volatility, leading to premature exits despite having a robust partial closure strategy in place.
The solution is simple in principle: Let partial closures do the profit-taking (they're already well-designed), let adaptive trailing manage dynamic protection (it understands volatility), and let phase locks provide safety floors only (their true strength).
By decoupling these systems and fixing the volatility paradox, the EA can achieve:
- Faster profit realization (via optimized partials)
- Longer trade duration (via wider adaptive trails)
- Protected downside (via phase lock floors)
- Extreme profit capture (via proper runner management)
Next Step: Implement Priority 1 changes (configuration only) and deploy to demo account for immediate testing. Results should be visible within 5-10 trades.