Forming candle 진입 신호 안정성 요구 — 2 polls 연속 True 만 발사

## 문제
forming candle 동안 close 변동에 따라 신호 컬럼이 1폴링 동안 True →
다음 폴링 False 로 깜빡이는 케이스 다수. 1회 True 만으로 진입 알림
발사되어 곧바로 [취소 알림] 도착하는 패턴 반복. 사용자 22:04
5m/15m 취소 알림 폭주 사례.

## 수정
신호별 (interval, sig) 키로 연속 True polling 카운트 추적.
- forming candle 의 신호는 count >= 2 (= 60s 안정 유지) 일 때만 발사
- closed candle 의 신호는 1회로 즉시 발사 (data 확정이라 깜빡 X)
- 신호 False 로 바뀌면 count 리셋 (연속성 보장)
- per-candle dedup 와 cooldown 은 그대로 위에 적용

## 효과
- forming 깜빡 1회는 더 이상 알림 발사 X → false alert + 취소 알림 동반
  감소
- 진짜 신호는 2폴링 (60s) 동안 안정 유지하므로 통과 → latency 증가
  최대 30s
- 닫힌 캔들 알림은 latency 변화 없음

## 추적 state (alert_state.signal_seen_count)
{(interval, sig): {"candle_time": ts, "count": int}}
새 candle 진입 시 자동 리셋. False 시 카운트 0 으로 리셋.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ILSEON-RYU
2026-05-05 22:12:43 +09:00
parent 3538b0324d
commit 1ecd6241f2
2 changed files with 19 additions and 0 deletions
+5
View File
@@ -33,6 +33,11 @@ pending_groups = []
# skip. interval 별로 각각 한 번씩 sync.
synced_intervals = set()
# (interval, sig) 별 forming candle 의 연속 True polling 카운트.
# 진입 신호가 forming 중 1회 깜빡으로 발사되는 false alert 차단용.
# 항목 형식: {(interval, sig): {"candle_time": ts, "count": int}}
signal_seen_count = {}
alert_symbol = "BTCUSDT"
alert_interval = "5m" # UI 표시용; 알림 스레드는 multi-TF 모니터링이라 무시
alert_lock = threading.Lock()
+14
View File
@@ -149,7 +149,12 @@ def check_and_alert(df, symbol, interval):
if sig not in recent.columns:
continue
triggered = recent[recent[sig].fillna(False)]
seen_key = (interval, sig)
prev_seen = alert_state.signal_seen_count.get(seen_key)
if triggered.empty:
# 신호 사라짐 → 카운터 리셋 (다음 True 시점부터 다시 1회 카운트)
if prev_seen:
alert_state.signal_seen_count[seen_key] = {"candle_time": prev_seen["candle_time"], "count": 0}
continue
candle_time = triggered.iloc[-1]["open_time"]
state_key = (interval, key)
@@ -157,6 +162,15 @@ def check_and_alert(df, symbol, interval):
continue
if now - alert_state.last_alert.get(state_key, 0) <= ALERT_COOLDOWN:
continue
# 연속 True polling 카운트 갱신
if prev_seen is None or prev_seen["candle_time"] != candle_time:
alert_state.signal_seen_count[seen_key] = {"candle_time": candle_time, "count": 1}
else:
alert_state.signal_seen_count[seen_key] = {"candle_time": candle_time, "count": prev_seen["count"] + 1}
count = alert_state.signal_seen_count[seen_key]["count"]
# forming candle 만 안정성 (2 polls) 요구. 닫힌 캔들은 즉시 발사 (data 확정).
if candle_time == forming_ct and count < 2:
continue
eligible.append({
"sig": sig, "key": key, "sub_label": sub_label,
"direction": direction, "candle_time": candle_time, "row": triggered.iloc[-1],