summaryrefslogtreecommitdiff
path: root/services/strategy-engine/strategies/ema_crossover_strategy.py
diff options
context:
space:
mode:
Diffstat (limited to 'services/strategy-engine/strategies/ema_crossover_strategy.py')
-rw-r--r--services/strategy-engine/strategies/ema_crossover_strategy.py24
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