summaryrefslogtreecommitdiff
path: root/services/strategy-engine/strategies/macd_strategy.py
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 18:44:20 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-04-01 18:44:20 +0900
commitcb55c81dbc43df83ef4d5b717fe22b4d04a93d2e (patch)
tree26ef6f6a89233fa8cf74ea6467b07f1158d75ff1 /services/strategy-engine/strategies/macd_strategy.py
parent0b0aace94fa633cd7a90c95ee89658167a8afd35 (diff)
feat(strategy): apply filters, conviction scoring, and ATR stops to all strategies
Diffstat (limited to 'services/strategy-engine/strategies/macd_strategy.py')
-rw-r--r--services/strategy-engine/strategies/macd_strategy.py26
1 files changed, 25 insertions, 1 deletions
diff --git a/services/strategy-engine/strategies/macd_strategy.py b/services/strategy-engine/strategies/macd_strategy.py
index bf30ed3..67c5e44 100644
--- a/services/strategy-engine/strategies/macd_strategy.py
+++ b/services/strategy-engine/strategies/macd_strategy.py
@@ -43,11 +43,30 @@ class MacdStrategy(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_histogram = None
+ def _macd_conviction(self, histogram_value: float, price: float) -> float:
+ """Map histogram magnitude to conviction (0.1-1.0).
+
+ Normalize by price to make it scale-independent.
+ """
+ if price == 0:
+ return 0.5
+ normalized = abs(histogram_value) / price * 1000 # scale to reasonable range
+ return min(1.0, max(0.1, normalized))
+
def on_candle(self, candle: Candle) -> Signal | None:
+ self._update_filter_data(candle)
self._closes.append(float(candle.close))
if len(self._closes) < self.warmup_period:
@@ -65,6 +84,7 @@ class MacdStrategy(BaseStrategy):
signal = None
if self._prev_histogram is not None:
+ conviction = self._macd_conviction(current_histogram, float(candle.close))
# Bullish crossover: histogram crosses from negative to positive
if self._prev_histogram <= 0 and current_histogram > 0:
signal = Signal(
@@ -73,6 +93,7 @@ class MacdStrategy(BaseStrategy):
side=OrderSide.BUY,
price=candle.close,
quantity=self._quantity,
+ conviction=conviction,
reason=f"MACD bullish crossover: histogram {self._prev_histogram:.6f} -> {current_histogram:.6f}",
)
# Bearish crossover: histogram crosses from positive to negative
@@ -83,8 +104,11 @@ class MacdStrategy(BaseStrategy):
side=OrderSide.SELL,
price=candle.close,
quantity=self._quantity,
+ conviction=conviction,
reason=f"MACD bearish crossover: histogram {self._prev_histogram:.6f} -> {current_histogram:.6f}",
)
self._prev_histogram = current_histogram
- return signal
+ if signal is not None:
+ return self._apply_filters(signal)
+ return None