diff options
Diffstat (limited to 'services/strategy-engine/strategies/ema_crossover_strategy.py')
| -rw-r--r-- | services/strategy-engine/strategies/ema_crossover_strategy.py | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/services/strategy-engine/strategies/ema_crossover_strategy.py b/services/strategy-engine/strategies/ema_crossover_strategy.py index bc36f36..a812eff 100644 --- a/services/strategy-engine/strategies/ema_crossover_strategy.py +++ b/services/strategy-engine/strategies/ema_crossover_strategy.py @@ -39,11 +39,28 @@ class EmaCrossoverStrategy(BaseStrategy): if self._quantity <= 0: raise ValueError(f"Quantity must be positive, got {self._quantity}") + self._init_filters( + require_trend=True, + adx_threshold=float(params.get("adx_threshold", 25.0)), + min_volume_ratio=float(params.get("min_volume_ratio", 0.5)), + atr_stop_multiplier=float(params.get("atr_stop_multiplier", 2.0)), + atr_tp_multiplier=float(params.get("atr_tp_multiplier", 3.0)), + ) + def reset(self) -> None: self._closes.clear() self._prev_short_above = None + def _ema_conviction(self, short_ema: float, long_ema: float, price: float) -> float: + """Map EMA gap to conviction (0.1-1.0). Larger gap = stronger crossover.""" + if price == 0: + return 0.5 + gap_pct = abs(short_ema - long_ema) / price + # Scale: 0% gap -> 0.1, 1%+ gap -> ~1.0 + return min(1.0, max(0.1, gap_pct * 100)) + def on_candle(self, candle: Candle) -> Signal | None: + self._update_filter_data(candle) self._closes.append(float(candle.close)) if len(self._closes) < self._long_period: @@ -57,6 +74,7 @@ class EmaCrossoverStrategy(BaseStrategy): signal = None if self._prev_short_above is not None: + conviction = self._ema_conviction(short_ema, long_ema, float(candle.close)) if not self._prev_short_above and short_above: signal = Signal( strategy=self.name, @@ -64,6 +82,7 @@ class EmaCrossoverStrategy(BaseStrategy): side=OrderSide.BUY, price=candle.close, quantity=self._quantity, + conviction=conviction, reason=f"Golden Cross: short EMA ({short_ema:.2f}) crossed above long EMA ({long_ema:.2f})", ) elif self._prev_short_above and not short_above: @@ -73,8 +92,11 @@ class EmaCrossoverStrategy(BaseStrategy): side=OrderSide.SELL, price=candle.close, quantity=self._quantity, + conviction=conviction, reason=f"Death Cross: short EMA ({short_ema:.2f}) crossed below long EMA ({long_ema:.2f})", ) self._prev_short_above = short_above - return signal + if signal is not None: + return self._apply_filters(signal) + return None |
