372 lines
13 KiB
Markdown
372 lines
13 KiB
Markdown
|
|
# 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](EntrySystem_Optimised.mqh#L642-L720):
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
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:
|
||
|
|
```cpp
|
||
|
|
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:
|
||
|
|
```cpp
|
||
|
|
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):
|
||
|
|
```cpp
|
||
|
|
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](TechnicalAnalysis_Optimised.mqh), the `AnalyzeMarket()` method determines:
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
// 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():
|
||
|
|
```cpp
|
||
|
|
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):
|
||
|
|
```cpp
|
||
|
|
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:
|
||
|
|
```cpp
|
||
|
|
// 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):
|
||
|
|
1. EntryMode = 7 (MULTI_STRATEGY)
|
||
|
|
2. EntryMode = 2 (MA_PULLBACK only)
|
||
|
|
3. 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:
|
||
|
|
```cpp
|
||
|
|
// 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**:
|
||
|
|
1. Each strategy has market condition awareness (self-filtering)
|
||
|
|
2. Weighted consensus prevents single-strategy bias
|
||
|
|
3. Confidence bonuses reward trend alignment
|
||
|
|
4. Adaptive thresholds by timeframe prevent over-trading
|
||
|
|
5. Spread filter protects against poor execution conditions
|
||
|
|
|
||
|
|
⚠️ **Areas for Improvement**:
|
||
|
|
1. Breakout strategy should filter ranging markets
|
||
|
|
2. Contrarian strategy should be included in multi-strategy voting
|
||
|
|
3. 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
|
||
|
|
|
||
|
|
1. **Re-run backtest** with spread fix to verify signal generation
|
||
|
|
2. **Monitor strategy participation** - add logging to see which strategies vote
|
||
|
|
3. **Consider adding Contrarian** to multi-strategy array for extreme conditions
|
||
|
|
4. **Consider filtering Breakout** in ranging markets for fewer false signals
|
||
|
|
5. **Validate performance metrics** - expect 2-8 signals/day on M15 based on market activity
|