from __future__ import annotations from dataclasses import dataclass import math import numpy as np import pandas as pd from config import CFG # zakładam, że masz te funkcje gdzieś u siebie: # from indicators import ema, rsi, atr, macd, adx_val from indicators import ema, rsi, atr, macd, adx_val Signal = str # "BUY" | "SELL" | "NONE" @dataclass class Decision: signal: Signal sl: float | None tp: float | None rpu: float # risk per unit (odległość od SL) def evaluate_signal(df: pd.DataFrame) -> Decision: """ Warunki (poluzowane / opcjonalne): - (opcjonalnie) kierunek EMA200 (trend filter), - ADX >= CFG.adx_min, - ATR >= CFG.atr_min_frac_price * price, - RSI nieprzeciążone (BUY < rsi_buy_max, SELL > rsi_sell_min), - (opcjonalnie) MACD zgodny z kierunkiem. Wyjście: SL = k_ATR, TP = RR * R (R = dystans do SL). """ if df is None or len(df) < CFG.history_min_bars: return Decision("NONE", None, None, 0.0) # Bezpieczne pobranie kolumn cols = {c.lower(): c for c in df.columns} C = pd.to_numeric(df[cols.get("close","Close")], errors="coerce").to_numpy(float) H = pd.to_numeric(df[cols.get("high","High")], errors="coerce").to_numpy(float) L = pd.to_numeric(df[cols.get("low","Low")], errors="coerce").to_numpy(float) if len(C) == 0 or np.isnan(C[-1]): return Decision("NONE", None, None, 0.0) # wskaźniki ema200 = ema(C, 200) rsi14 = rsi(C, CFG.rsi_len) atr14 = atr(H, L, C, 14) macd_line, macd_sig, macd_hist = macd(C, 12, 26, 9) adx14, pdi, mdi = adx_val(H, L, C, 14) c = float(C[-1]) a = float(atr14[-1]) if math.isfinite(atr14[-1]) else 0.0 if not (math.isfinite(c) and math.isfinite(a)) or a <= 0: return Decision("NONE", None, None, 0.0) # filtry bazowe adx_ok = float(adx14[-1]) >= CFG.adx_min atr_ok = (a / max(1e-9, c)) >= CFG.atr_min_frac_price # trend (opcjonalny) trend_ok_buy = True if not CFG.require_trend else (c > float(ema200[-1])) trend_ok_sell = True if not CFG.require_trend else (c < float(ema200[-1])) # RSI „nieprzeciążone” rsi_val = float(rsi14[-1]) rsi_ok_buy = rsi_val <= CFG.rsi_buy_max rsi_ok_sell = rsi_val >= CFG.rsi_sell_min # MACD (opcjonalny) if CFG.use_macd_filter: macd_buy = (macd_line[-1] > macd_sig[-1] and macd_hist[-1] > 0) macd_sell = (macd_line[-1] < macd_sig[-1] and macd_hist[-1] < 0) else: macd_buy = macd_sell = True signal: Signal = "NONE" sl = tp = None rpu = 0.0 if adx_ok and atr_ok: if trend_ok_buy and rsi_ok_buy and macd_buy: signal = "BUY" sl = c - CFG.sl_atr_mult * a risk = c - sl tp = c + CFG.tp_rr * risk rpu = max(1e-9, risk) elif CFG.allow_short and trend_ok_sell and rsi_ok_sell and macd_sell: signal = "SELL" sl = c + CFG.sl_atr_mult * a risk = sl - c tp = c - CFG.tp_rr * risk rpu = max(1e-9, risk) return Decision(signal, sl, tp, rpu)