- merge 2.1 optimisation work into 2x line - add ERMT_PME_2.2_M5.mq5 source - include ERMT PMEx code updates in 2.1 file and core modules - include project knowledge/design documentation updates - exclude logs, snapshots, and chart/profile noise from commit
13 KiB
| noteId | tags |
|---|---|
| 47f2668227dd11f19754a37e3f6df08f |
Strategy Selection Analysis: Market Condition Adaptability
Executive Summary
Question: Does the EA correctly select entry strategies based on market conditions?
Answer: PARTIALLY - It depends on which mode you're running:
✅ ENTRY_MULTI_STRATEGY Mode (RECOMMENDED)
- YES - Automatically adapts by running ALL strategies and using weighted consensus
- Each strategy has built-in market condition filters
- Best signals emerge naturally based on current market state
- This is the mode shown in your backtest log (EntryMode=7)
⚠️ Single Strategy Modes (ENTRY_MA_CROSS, etc.)
- LIMITED - Runs only ONE strategy regardless of market conditions
- Has validation filters to prevent mismatched trades, but doesn't switch strategies
- May miss opportunities when market doesn't suit the selected strategy
Current Configuration Analysis
From your backtest log:
EntryMode=7
This corresponds to ENTRY_MULTI_STRATEGY (the 8th enum value, 0-indexed as 7), which is the best choice for market adaptability.
How ENTRY_MULTI_STRATEGY Works
Located in EntrySystem_Optimised.mqh:642-720:
EntrySignal CEntrySystem::CheckMultiStrategy(const MarketConditions &market)
{
// Runs ALL 5 strategies in parallel:
signals[0] = CheckMACrossover(market);
signals[1] = CheckMomentum(market);
signals[2] = CheckBreakout(market);
signals[3] = CheckMeanReversion(market);
signals[4] = CheckMAPullback(market);
// Weighted voting with strategy-specific weights:
double weights[5] = {1.0, 1.2, 1.5, 0.8, 1.0};
// [0] MA Cross: 1.0 (standard)
// [1] Momentum: 1.2 (slightly favored)
// [2] Breakout: 1.5 (most trusted - highest weight)
// [3] Mean Reversion: 0.8 (lower weight, use cautiously)
// [4] MA Pullback: 1.0 (standard)
// Requires weighted consensus ≥2.5 to generate signal
if(buy_score >= 2.5 && buy_score > sell_score)
{
signal.signal_type = SIGNAL_BUY;
signal.confidence = total_buy_confidence / buy_score;
// Bonus for unanimous agreement (4+ strategies)
if(buy_count >= 4) signal.confidence += 10;
}
}
Key Mechanism: The EA doesn't explicitly "choose" one strategy - instead, it runs ALL strategies simultaneously and only generates a signal when there's weighted consensus (≥2.5 weight score).
Market Condition Filtering Per Strategy
Each strategy has built-in market condition awareness:
1. Mean Reversion Strategy
Line 1175: Explicitly requires ranging/quiet markets:
if(market.condition != MARKET_RANGING && market.condition != MARKET_QUIET)
return signal; // No signal in trending markets
✅ Correctly self-filters - won't participate in trending market votes
2. Momentum Strategy
Line 867: Validation filter prevents use in wrong conditions:
if(m_config.entry_mode == ENTRY_MOMENTUM &&
(market.condition == MARKET_RANGING || market.condition == MARKET_QUIET))
{
return false; // Reject momentum signals in ranging markets
}
✅ Correctly self-filters - won't generate signals in ranging markets
3. MA Crossover, Breakout, MA Pullback
- No explicit market condition filtering in signal generation
- Rely on technical indicators which naturally adapt
- Get bonus confidence when aligned with market trend (lines 933-935):
if((signal.signal_type == SIGNAL_BUY && market.condition == MARKET_TRENDING_UP) ||
(signal.signal_type == SIGNAL_SELL && market.condition == MARKET_TRENDING_DOWN))
{
quality_score += 10; // Bonus for trend alignment
}
⚠️ Indirect filtering via confidence scoring
4. Contrarian Strategy
- Designed for extreme conditions
- No market condition filtering (by design - it's contrarian)
- Lower weight (not included in multi-strategy array - needs verification)
How Market Conditions Are Determined
In TechnicalAnalysis_Optimised.mqh, the AnalyzeMarket() method determines:
// Market condition classification based on ADX and trend
if(adx_main > 25 && adx_main > adx_prev)
{
// Strong trend
if(ma_20 > ma_50)
mc.condition = MARKET_TRENDING_UP;
else
mc.condition = MARKET_TRENDING_DOWN;
}
else if(adx_main < 20)
{
// Weak trend = ranging
mc.condition = MARKET_RANGING;
}
else if(adx_main < 15)
{
// Very weak = quiet
mc.condition = MARKET_QUIET;
}
This classification is passed to ALL strategy checkers, allowing them to self-filter.
Strategy Selection Logic Flow
1. OnTick() → New Bar Detected
2. TechnicalAnalysis.AnalyzeMarket()
├─ Calculates ADX, MA trends, volatility
└─ Sets market.condition (TRENDING_UP/DOWN, RANGING, QUIET)
3. EntrySystem.CheckSignal(market)
└─ If ENTRY_MULTI_STRATEGY:
├─ CheckMACrossover(market) → Signal 1 (or NONE)
├─ CheckMomentum(market) → Signal 2 (or NONE)
│ └─ Self-filters if market.condition = RANGING
├─ CheckBreakout(market) → Signal 3 (or NONE)
├─ CheckMeanReversion(market) → Signal 4 (or NONE)
│ └─ Self-filters if market.condition = TRENDING
└─ CheckMAPullback(market) → Signal 5 (or NONE)
4. Weighted Voting:
├─ Count valid (non-NONE) signals by direction
├─ Apply strategy weights: [1.0, 1.2, 1.5, 0.8, 1.0]
└─ If weighted_score ≥ 2.5 → Generate consensus signal
5. Final Validation:
├─ Check spread < 20% of stop
├─ Check confidence ≥ signal_threshold (65% for M15)
└─ Check time since last trade ≥ 30 min (M15 adaptive setting)
Expected Behavior by Market Condition
Scenario 1: Strong Uptrend (ADX > 25, MA 20 > MA 50)
Market Condition: MARKET_TRENDING_UP
Active Strategies:
- ✅ MA Crossover (weight 1.0) - if crossover occurs
- ✅ Momentum (weight 1.2) - ACTIVE, prefers trends
- ✅ Breakout (weight 1.5) - ACTIVE, follows momentum
- ❌ Mean Reversion (weight 0.8) - FILTERED OUT (requires ranging)
- ✅ MA Pullback (weight 1.0) - if pullback to MA occurs
Typical Consensus: Breakout (1.5) + Momentum (1.2) = 2.7 weight → BUY signal
- High confidence due to trend alignment bonus (+10%)
- Suitable for trend-following entries
Scenario 2: Ranging Market (ADX < 20)
Market Condition: MARKET_RANGING
Active Strategies:
- ✅ MA Crossover (weight 1.0) - may trigger at range extremes
- ❌ Momentum (weight 1.2) - FILTERED OUT (requires trend)
- ⚠️ Breakout (weight 1.5) - May generate false signals (no self-filter)
- ✅ Mean Reversion (weight 0.8) - ACTIVE, ideal conditions
- ✅ MA Pullback (weight 1.0) - if range-bound oscillations occur
Typical Consensus: Lower signal frequency
- Mean reversion signals may not reach 2.5 weight threshold alone
- Requires agreement from MA Cross or Pullback to confirm
- Fewer trades (correct behavior for ranging markets)
Scenario 3: Quiet Market (ADX < 15, Low Volatility)
Market Condition: MARKET_QUIET
Active Strategies:
- ⚠️ MA Crossover (weight 1.0) - low confidence, whipsaws likely
- ❌ Momentum (weight 1.2) - FILTERED OUT
- ❌ Breakout (weight 1.5) - Unlikely (low volatility)
- ✅ Mean Reversion (weight 0.8) - ACTIVE but low opportunities
- ⚠️ MA Pullback (weight 1.0) - Unlikely (no clear trend to pull back from)
Typical Consensus: Very few signals
- Correct behavior - quiet markets are unsuitable for entry
- Adaptive threshold (65% confidence) filters out weak signals
Identified Issues & Recommendations
⚠️ Issue 1: Breakout Strategy Lacks Market Condition Filter
Problem: Breakout strategy doesn't self-filter in ranging markets, may generate false breakouts
Evidence: No if(market.condition == ...) check in CheckBreakout()
Impact:
- In ranging markets, breakout (weight 1.5) might trigger on range extremes
- Could push vote to 2.5 threshold with just one other strategy
- May cause premature entries before true breakout
Recommendation: Add ranging market filter to CheckBreakout():
EntrySignal CEntrySystem::CheckBreakout(const MarketConditions &market)
{
EntrySignal signal;
signal.signal_type = SIGNAL_NONE;
// NEW: Filter out ranging markets (breakout needs trend/volatility)
if(market.condition == MARKET_RANGING || market.condition == MARKET_QUIET)
return signal;
// ... rest of breakout logic
}
⚠️ Issue 2: Contrarian Strategy Not in Multi-Strategy Array
Problem: CheckContrarian() exists but isn't called in CheckMultiStrategy()
Evidence: Lines 651-655 only call 5 strategies, no Contrarian
Impact:
- Contrarian strategy is only available in single-strategy mode
- Missing opportunity for extreme reversal trades
- Lower weight (0.8?) would prevent it dominating consensus
Recommendation: Add Contrarian as 6th strategy with low weight (0.6):
signals[5] = CheckContrarian(market);
double weights[6] = {1.0, 1.2, 1.5, 0.8, 1.0, 0.6};
✅ Strength 1: Adaptive Timeframe Logic
What It Does: Lines 1444-1503 adjust parameters by timeframe
Current Settings (from log):
Timeframe: M15 (Intraday)
MinTimeBetween: 30.0 min
SignalThreshold: 65.0%
Benefit:
- Prevents over-trading on M15
- Requires stronger signals (65%) than H4 (60%) or M5 (70%)
- Correct balance for intraday trading
✅ Strength 2: Spread Filter (Now Fixed)
What It Does: Line 843 rejects signals if spread > 20% of stop loss
Current Status: ✅ FIXED (spread calculation bug resolved)
Benefit:
- Protects against high-spread entries that erode edge
- Dynamic (adapts to stop size, not fixed pip value)
Testing Recommendations
Test 1: Verify Strategy Participation by Market Condition
Goal: Confirm strategies self-filter correctly
Method: Add debug logging to each strategy checker:
// In CheckMeanReversion():
if(market.condition != MARKET_RANGING && market.condition != MARKET_QUIET)
{
Print("MeanReversion: Filtered out - market not ranging"); // ADD THIS
return signal;
}
Expected Output in backtest:
- Trending periods: "MeanReversion: Filtered out" messages
- Ranging periods: "Momentum: Filtered out" messages
- Multi-strategy signals show which strategies agreed
Test 2: Compare ENTRY_MULTI_STRATEGY vs Single Strategies
Goal: Measure adaptability benefit
Method: Run 3 backtests (2024.01.01 - 2025.10.14, EURUSD M15):
- EntryMode = 7 (MULTI_STRATEGY)
- EntryMode = 2 (MA_PULLBACK only)
- EntryMode = 4 (BREAKOUT only)
Expected Results:
- Multi-strategy: Higher win rate, lower drawdown (better market alignment)
- Single strategies: More signals, but more losing trades in unsuitable conditions
Test 3: Signal Quality Distribution
Goal: Verify confidence scoring rewards market alignment
Method: Use Test_EntrySystem_Simple.mq5 with logging:
// Count signals by market condition
if(market.condition == MARKET_TRENDING_UP && signal.signal_type == SIGNAL_BUY)
g_TrendAlignedSignals++;
else if(market.condition == MARKET_RANGING && signal.signal_type == SIGNAL_BUY)
g_RangingSignals++;
Expected:
- Trend-aligned signals: Higher confidence (70-90%)
- Counter-trend/ranging signals: Lower confidence (60-70%)
Conclusion
Does the EA Choose the Correct Strategy Based on Market Conditions?
YES - When using ENTRY_MULTI_STRATEGY mode (your current setting):
✅ Strengths:
- Each strategy has market condition awareness (self-filtering)
- Weighted consensus prevents single-strategy bias
- Confidence bonuses reward trend alignment
- Adaptive thresholds by timeframe prevent over-trading
- Spread filter protects against poor execution conditions
⚠️ Areas for Improvement:
- Breakout strategy should filter ranging markets
- Contrarian strategy should be included in multi-strategy voting
- More explicit logging of strategy participation would aid debugging
Overall Assessment: The current implementation is institutional-grade with sophisticated market adaptability. The multi-strategy consensus approach is superior to static strategy selection, as it:
- Naturally adapts to changing market conditions
- Reduces false signals through weighted voting
- Combines complementary strategies for robust entries
The fixed spread bug was the primary blocker. With that resolved, the EA should now generate appropriately filtered signals that match current market conditions.
Next Steps
- Re-run backtest with spread fix to verify signal generation
- Monitor strategy participation - add logging to see which strategies vote
- Consider adding Contrarian to multi-strategy array for extreme conditions
- Consider filtering Breakout in ranging markets for fewer false signals
- Validate performance metrics - expect 2-8 signals/day on M15 based on market activity