알림 신호 누락 버그 수정 (3건 묶음)

## 문제
대시보드 차트에는 진입 신호가 정상 표기되지만 텔레그램 알림이 전혀 발송되지
않거나 일부 신호 종류가 영구적으로 누락되는 현상.

## 변경 사항

1. 알림 스레드 캔들 50 -> 200
   - MA99(99 SMA) 가 50 캔들에서는 항상 NaN 이라 bull_ma / bear_ma 가
     False 가 되고, strong_long_signal / strong_short_signal 이 영원히
     발화하지 않던 문제. UI 와 동일한 200 캔들로 맞춰 신호 일관성 확보.
   - OI 도 50 -> 200 으로 정렬해 lookback 보강.

2. 알림 스레드에 fundingRate fetch + merge 추가
   - 기존 스레드 df 에 fundingRate 컬럼이 없어 short_caution_signal
     ('fundingRate' in df.columns 분기) 가 항상 False 였음.
   - UI 와 동일한 패턴(get_funding_rate -> floor('1h') merge -> ffill)
     으로 fundingRate 합류, short_caution_signal 정상 발화.

3. check_and_alert 1캔들 -> 3캔들 체크
   - df.iloc[-1] (현재 형성 중 캔들)만 보던 로직을 df.tail(3) 으로 확장.
   - 30s 폴링 사이 닫혀버린 캔들의 신호 누락 방지. cooldown 은 그대로
     유지되므로 중복 알림은 발생하지 않음.

## 부가
- streamlit.log / streamlit.err.log 를 .gitignore 에 추가
  (런타임 산출물 — 6.9MB까지 커지는 상황 발생).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ILSEON-RYU
2026-05-01 09:55:22 +09:00
parent 790d4f1c83
commit 10944ae907
2 changed files with 17 additions and 4 deletions
+3
View File
@@ -0,0 +1,3 @@
.claude/settings.local.json
streamlit.log
streamlit.err.log
+14 -4
View File
@@ -66,7 +66,7 @@ def send_telegram(message: str):
def check_and_alert(df, symbol, interval):
now = time.time()
last = df.iloc[-1]
recent = df.tail(3)
for sig, key, msg in [
("strong_long_signal", "strong_long", f"🟢 강한 롱 진입 신호\n{symbol} {interval}"),
("strong_short_signal", "strong_short", f"🔴 강한 숏 진입 신호\n{symbol} {interval}"),
@@ -76,7 +76,7 @@ def check_and_alert(df, symbol, interval):
("vol_short_signal", "vol_short", f"🔽 볼륨급등 숏 신호\n{symbol} {interval}"),
("short_caution_signal","short_caution",f"⚠️ 숏 진입 주의 신호\n{symbol} {interval}"),
]:
if last.get(sig, False) and now - _last_alert[key] > ALERT_COOLDOWN:
if sig in recent.columns and recent[sig].fillna(False).any() and now - _last_alert[key] > ALERT_COOLDOWN:
send_telegram(msg)
_last_alert[key] = now
@@ -542,10 +542,10 @@ def _alert_loop():
with _alert_lock:
symbol = _alert_symbol
interval = _alert_interval
df = get_klines(symbol, interval, 50)
df = get_klines(symbol, interval, 200)
oi_period = interval if interval in ["5m","15m","30m","1h","4h","12h","1d","3d","1M"] else "5m"
try:
oi = get_open_interest_history(symbol, oi_period, 50)
oi = get_open_interest_history(symbol, oi_period, 200)
if not oi.empty:
oi_m = oi[["timestamp","sumOpenInterest"]].rename(columns={"timestamp":"open_time"})
df["open_time_r"] = df["open_time"].dt.floor(_to_floor_freq(oi_period))
@@ -554,6 +554,16 @@ def _alert_loop():
df = df.drop(columns=["open_time_r","open_time_oi"], errors="ignore")
df["sumOpenInterest"] = df["sumOpenInterest"].ffill()
except: pass
try:
fr = get_funding_rate(symbol, 200)
if not fr.empty:
fr_m = fr[["fundingTime","fundingRate"]].rename(columns={"fundingTime":"open_time"})
fr_m["open_time"] = fr_m["open_time"].dt.floor(_to_floor_freq("1h"))
df["open_time_r2"] = df["open_time"].dt.floor(_to_floor_freq("1h"))
df = df.merge(fr_m, left_on="open_time_r2", right_on="open_time", how="left", suffixes=("","_fr"))
df = df.drop(columns=["open_time_r2","open_time_fr"], errors="ignore")
df["fundingRate"] = df["fundingRate"].ffill().fillna(0)
except: pass
df = compute_indicators(df, interval)
check_and_alert(df, symbol, interval)
except Exception as e: