1m/3m 알림 제외 + 진입 신호 필터를 close vs open 방향성 검증으로 교체

## 변경 요약
1. ALERT_TIMEFRAMES 에서 1m / 3m 제거 -> [5m, 15m, 30m, 1h] 만 모니터링.
   1m / 3m 의 진입 알림 / 취소 알림 / 손절가 알림 모두 차단.
2. 진입 신호 (long/short/strong_long/strong_short) 의 늦은 진입 차단 필터를
   BB position + 3봉 모멘텀 -> 현재 캔들 자체의 close vs open 방향성으로 교체.
   - long_signal / strong_long_signal: close > open (이번 캔들이 양봉)
   - short_signal / strong_short_signal: close < open (이번 캔들이 음봉)
   이전 BB position 필터는 breakdown / breakup 캔들에서 추세 진입을 막아버리는
   부작용이 있었음 (예: 15m 19:00 -1.4% 거대 빨간 캔들에 short 마커 안 뜸).
   close vs open 검증은 "이번 캔들 자체가 신호 방향과 일치" 만 요구해, 진행
   중인 추세는 잡고, 반등 / 반락 캔들의 늦은 진입은 차단.

## 변경 안 한 것
- 다단계 ROI 알림 (-5/-10/-15%) 은 사용자 의도가 별개 (1m/3m 볼륨 변화를
  선행 지표로 활용) 라 이번 커밋에선 미적용. 추후 별도 작업.
- vol_long / vol_short 신호 정의는 그대로.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ILSEON-RYU
2026-05-04 20:03:39 +09:00
parent 56159a0695
commit 2e916d48cb
+9 -10
View File
@@ -299,15 +299,14 @@ def compute_signals(df, interval="5m"):
df["bear_ma"] = (
(df["close"] < df["MA7"]) & (df["MA7"] < df["MA25"])
)
# BB position: 0 = BB_lower, 0.5 = BB_mid, 1 = BB_upper.
# BB position 보존 (디버그 / 차트 hover 용도) — 신호 정의에는 미사용.
bb_range = (df["BB_upper"] - df["BB_lower"]).replace(0, float("nan"))
df["bb_pos"] = (df["close"] - df["BB_lower"]) / bb_range
# 최근 3봉 모멘텀: 이미 큰 폭으로 움직인 후의 늦은 진입 차단.
# 롱: 최근 3봉 동안 이미 +0.5% 이상 오른 상태면 차단 (이미 늦음).
# 숏: 최근 3봉 동안 이미 -0.5% 이상 떨어진 상태면 차단.
recent_change_pct = (df["close"] - df["close"].shift(3)) / df["close"].shift(3) * 100
df["long_signal"] = df["bull_ma_2"] & (df["RSI"] < 60) & (df["MACD_hist"] > df["MACD_hist"].shift(1)) & (df["bb_pos"] > 0.5) & (df["bb_pos"] < 0.7) & (recent_change_pct < 0.5)
df["short_signal"] = df["bear_ma_2"] & (df["RSI"] > 35) & (df["MACD_hist"] < df["MACD_hist"].shift(1)) & (df["bb_pos"] < 0.5) & (df["bb_pos"] > 0.3) & (recent_change_pct > -0.5)
# 현재 캔들 자체의 방향이 신호 방향과 일치해야 발사.
# 늦은 진입 (반등 중인 녹색 캔들에 short 등) 차단 + 현재 진행 중인 breakdown
# (빨간 거대 캔들에 short) 은 통과 시킴.
df["long_signal"] = df["bull_ma_2"] & (df["RSI"] < 60) & (df["MACD_hist"] > df["MACD_hist"].shift(1)) & (df["close"] > df["BB_mid"]) & (df["close"] > df["open"])
df["short_signal"] = df["bear_ma_2"] & (df["RSI"] > 35) & (df["MACD_hist"] < df["MACD_hist"].shift(1)) & (df["close"] < df["BB_mid"]) & (df["close"] < df["open"])
df["long_signal"] = df["long_signal"] & (df["long_signal"].rolling(5, min_periods=1).sum().shift(1).fillna(0) == 0)
df["short_signal"] = df["short_signal"] & (df["short_signal"].rolling(5, min_periods=1).sum().shift(1).fillna(0) == 0)
@@ -331,8 +330,8 @@ def compute_signals(df, interval="5m"):
df["fr_long_favor"] = df["taker_buy_vol"].rolling(3).mean() > df["taker_sell_vol"].rolling(3).mean()
df["fr_short_favor"] = df["taker_sell_vol"].rolling(3).mean() > df["taker_buy_vol"].rolling(3).mean()
df["strong_long_signal"] = df["bull_ma_2"] & (df["RSI"] < 65) & (df["MACD_hist"] > df["MACD_hist"].shift(1)) & df["oi_up_2"] & df["taker_buy_2"] & df["fr_long_favor"] & (df["bb_pos"] > 0.5) & (df["bb_pos"] < 0.7) & (recent_change_pct < 0.5)
df["strong_short_signal"] = df["bear_ma_2"] & (df["RSI"] > 35) & (df["MACD_hist"] < df["MACD_hist"].shift(1)) & df["oi_down_2"] & df["taker_sell_2"] & df["fr_short_favor"] & (df["bb_pos"] < 0.5) & (df["bb_pos"] > 0.3) & (recent_change_pct > -0.5)
df["strong_long_signal"] = df["bull_ma_2"] & (df["RSI"] < 65) & (df["MACD_hist"] > df["MACD_hist"].shift(1)) & df["oi_up_2"] & df["taker_buy_2"] & df["fr_long_favor"] & (df["close"] > df["open"])
df["strong_short_signal"] = df["bear_ma_2"] & (df["RSI"] > 35) & (df["MACD_hist"] < df["MACD_hist"].shift(1)) & df["oi_down_2"] & df["taker_sell_2"] & df["fr_short_favor"] & (df["close"] < df["open"])
df["strong_long_signal"] = df["strong_long_signal"] & (df["strong_long_signal"].rolling(10, min_periods=1).sum().shift(1).fillna(0) == 0)
df["strong_short_signal"] = df["strong_short_signal"] & (df["strong_short_signal"].rolling(10, min_periods=1).sum().shift(1).fillna(0) == 0)
@@ -700,7 +699,7 @@ def _build_signal_df(symbol, interval, klines_limit=200):
df = compute_indicators(df, interval)
return df
ALERT_TIMEFRAMES = ["1m", "3m", "5m", "15m", "30m", "1h"]
ALERT_TIMEFRAMES = ["5m", "15m", "30m", "1h"]
def _alert_loop():
while True: