fedbf1c81e43ab0eebdeabe0f11b4948b8c620d7
## 증상
30분봉 일반 숏 신호가 한 캔들(20:30)에 발화한 뒤 30초 ~ 수 분 간격으로
계속 같은 시간(20:30) 으로 반복 알림이 텔레그램에 도착.
## 원인
Streamlit 은 사용자가 페이지를 새로고침하거나 컨트롤을 조작할 때마다
스크립트 전체를 위에서부터 재실행한다. 이 과정에서 모듈 최상단의 mutable
state 들이 매번 재할당된다:
_last_alert = {... 0 ...} # 쿨다운 타이머 0 으로 리셋
_last_fired_candle = {... None ...} # per-candle dedup 상태 None 리셋
_long_entry = None # 진입 추적 None 리셋
_alert_started = False # 스레드 가드 False 리셋
_alert_lock = threading.Lock() # 새 락 객체 생성 (기존 락 무시)
결과적으로 매 rerun 마다:
1. 새 알림 스레드가 추가로 spawn → 동일 polling 을 중복 수행
2. dedup 상태가 None 으로 리셋되어 이미 알린 캔들도 새로 알림 처리
3. 새 락 객체로 기존 스레드와 동기화 깨짐
streamlit.log 에 "[일일리포트] 스레드 기동" 메시지가 50회 가까이 찍힌 것이
1번 원인의 직접 증거.
## 수정
모듈 최상단에 globals() 검사 가드 추가:
if "_alert_state_initialized" not in globals():
_last_alert = {...}
_last_fired_candle = {...}
_long_entry = None
_short_entry = None
_alert_state_initialized = True
같은 패턴으로 _alert_thread_state_initialized 와 _last_report_date 도 보호.
첫 실행 시에만 초기화되고, 이후 rerun 에서는 globals() 에 이미 키가 존재
하므로 if 블록을 건너뛴다 -> 이전 값이 그대로 유지된다.
## 검증
페이지 5회 hit 후 streamlit.log 의 "기동" 로그 카운트:
이전: 50+ (rerun 마다 추가)
이후: 1 (단 한 번만)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
Python
70.9%
TypeScript
27.1%
Dockerfile
1%
Shell
0.5%
CSS
0.3%
Other
0.2%