늦은 진입 차단 — BB position + 3봉 모멘텀 필터 추가 (long/short/strong 전체)

## 문제
30분봉 19:30 short 신호 발화: 가격이 80.5k -> 78.2k 까지 -2.8% 추락 한
직후 79k 부근 반등 시점에 short 진입 신호. 이미 다 떨어진 후 바닥에서
숏 거는 격 -> 즉시 반등에 stop 맞음. 사용자: "이거 청산이야 늦어".

같은 패턴 long 도 발생: 30분봉 18:30 long 신호 (close 79.8k) 후 80.5k
까지만 짧게 오른 뒤 78.2k 까지 추락. 진입 시점이 이미 충분히 오른
후라 결과적으로 손실.

## 수정
모든 진입 신호(long_signal, short_signal, strong_long_signal,
strong_short_signal) 에 두 가지 추가 필터:

1. BB position (close 가 BB 범위의 어느 위치인지, 0=하단 1=상단):
   - long: 0.5 < bb_pos < 0.7  (중간선 위, 상단 70% 미만)
   - short: 0.3 < bb_pos < 0.5  (중간선 아래, 하단 30% 위)
   * 이미 한 끝까지 가버린 후의 늦은 진입 차단

2. 3봉 모멘텀 (close vs close 3봉 전):
   - long: 최근 3봉 동안 +0.5% 미만 상승
   - short: 최근 3봉 동안 -0.5% 미만 하락
   * 이미 큰 폭으로 움직인 후의 추격 진입 차단

## 검증
30분봉 19:30 (이전 strong_short + short 동시 발화):
- bb_pos = 0.149 (< 0.3) -> short 차단
- 3봉 모멘텀 = -0.85% (< -0.5%) -> short 차단
- 결과: 모든 short 신호 False ✓

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 19:52:34 +09:00
parent 255054683a
commit 56159a0695
+11 -4
View File
@@ -299,8 +299,15 @@ def compute_signals(df, interval="5m"):
df["bear_ma"] = (
(df["close"] < df["MA7"]) & (df["MA7"] < df["MA25"])
)
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["BB_upper"])
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["BB_lower"])
# BB position: 0 = BB_lower, 0.5 = BB_mid, 1 = BB_upper.
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)
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)
@@ -324,8 +331,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["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["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["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)