Add indicator ablation framework + apply ablation-driven cleanup
Framework: - TechnicalConfig.disabled_indicators skips named indicators in engine.analyze - Optimizer supports disable_indicator override for parallel ablation sweeps - main.py --ablate runs leave-one-out across all 10 directional indicators Cleanup applied (validated across 2024 trend / 2023 chop / 2026 recent): - Drop pivot by default (cumulative +9.5% to disable) - Drop candlestick by default (+3.4% cumulative) - Regime-gate ichimoku: 1.3x in trending, 0.2x in ranging (was a chop villain) - Trim RSI weight 1.0 -> 0.3 (cumulative noise across regimes) Result: +10.60% on 2024 trend (+33.24% -> +43.84%, Sharpe 2.17 -> 2.77), +15.71% on 2023 chop (-18.27% -> -2.56%). Recent regime still adverse.
This commit is contained in:
@@ -83,18 +83,20 @@ class TechnicalEngine:
|
||||
)
|
||||
|
||||
# ── Compute all indicators ─────────────────────────────────
|
||||
self._compute_ema_signals(df, result)
|
||||
self._compute_rsi(df, result)
|
||||
self._compute_macd(df, result)
|
||||
self._compute_bollinger(df, result)
|
||||
self._compute_stochastic(df, result)
|
||||
self._compute_adx(df, result)
|
||||
self._compute_atr(df, result)
|
||||
self._compute_ichimoku(df, result)
|
||||
self._detect_divergences(df, result)
|
||||
self._detect_candlestick_patterns(df, result)
|
||||
self._compute_support_resistance(df, result)
|
||||
self._compute_volume_analysis(df, result)
|
||||
disabled = set(self.config.disabled_indicators or [])
|
||||
|
||||
if "ema_trend" not in disabled: self._compute_ema_signals(df, result)
|
||||
if "rsi" not in disabled: self._compute_rsi(df, result)
|
||||
if "macd" not in disabled: self._compute_macd(df, result)
|
||||
if "bollinger" not in disabled: self._compute_bollinger(df, result)
|
||||
if "stochastic" not in disabled: self._compute_stochastic(df, result)
|
||||
if "adx" not in disabled: self._compute_adx(df, result)
|
||||
if "atr" not in disabled: self._compute_atr(df, result)
|
||||
if "ichimoku" not in disabled: self._compute_ichimoku(df, result)
|
||||
if "divergence" not in disabled: self._detect_divergences(df, result)
|
||||
if "candlestick" not in disabled: self._detect_candlestick_patterns(df, result)
|
||||
if "pivot" not in disabled: self._compute_support_resistance(df, result)
|
||||
if "volume" not in disabled: self._compute_volume_analysis(df, result)
|
||||
|
||||
# ── Classify market regime ─────────────────────────────────
|
||||
self._classify_regime(df, result)
|
||||
@@ -215,7 +217,7 @@ class TechnicalEngine:
|
||||
|
||||
result.indicators["rsi"] = IndicatorSignal(
|
||||
name="RSI", value=rsi_val, signal=signal,
|
||||
confidence=conf, description=desc, weight=1.0,
|
||||
confidence=conf, description=desc, weight=0.3,
|
||||
)
|
||||
|
||||
# Attach RSI series for divergence detection (dynamic attribute, filtered by _prefix)
|
||||
@@ -847,10 +849,12 @@ class TechnicalEngine:
|
||||
total_weight = 0.0
|
||||
total_confidence = 0.0
|
||||
|
||||
# Regime-gate ichimoku: trend hero (-23% to disable in 2024 trend) but
|
||||
# chop villain (+18% to disable in 2023 chop & 2026 recent).
|
||||
regime_weights = {
|
||||
"trending_up": {"ema_trend": 1.5, "adx": 1.3, "macd": 1.2, "rsi": 0.7, "bollinger": 0.6},
|
||||
"trending_down": {"ema_trend": 1.5, "adx": 1.3, "macd": 1.2, "rsi": 0.7, "bollinger": 0.6},
|
||||
"ranging": {"rsi": 1.4, "stochastic": 1.3, "bollinger": 1.4, "ema_trend": 0.6, "adx": 0.5},
|
||||
"trending_up": {"ema_trend": 1.5, "adx": 1.3, "macd": 1.2, "ichimoku": 1.3, "rsi": 0.7, "bollinger": 0.6},
|
||||
"trending_down": {"ema_trend": 1.5, "adx": 1.3, "macd": 1.2, "ichimoku": 1.3, "rsi": 0.7, "bollinger": 0.6},
|
||||
"ranging": {"rsi": 1.4, "stochastic": 1.3, "bollinger": 1.4, "ema_trend": 0.6, "adx": 0.5, "ichimoku": 0.2},
|
||||
}
|
||||
|
||||
active_regime_weights = regime_weights.get(result.market_regime, {})
|
||||
|
||||
Reference in New Issue
Block a user