7dcd9591dc
## 배경
이전 커밋 fedbf1c 에서 globals() 검사 가드로 rerun 시 상태 초기화를 막으려
했으나 동작하지 않음. streamlit.log 의 "[일일리포트] 스레드 기동" 메시지가
restart 후에도 12 회 이상 누적된 것이 증거.
## 원인
Streamlit 은 rerun 시 메인 스크립트를 새 namespace 에서 exec() 한다.
이 namespace 는 매번 새로 만들어지므로 globals() 안에는 이전 run 의
변수가 존재하지 않음 -> 가드가 항상 True 분기를 타고 mutable state 가
매번 초기화됨. 그 결과:
- 알림 dedup 상태 (last_fired_candle) 가 None 으로 리셋
- 진입 추적 (long_entry / short_entry) 가 None 으로 리셋
- 스레드 기동 가드 (alert_started) 가 False 로 리셋 -> 새 스레드 spawn
- threading.Lock() 도 매번 새로 생성되어 동기화 깨짐
이로 인해 같은 캔들 (예: 30분봉 20:30 일반 숏) 알림이 텔레그램으로 30초
간격 반복 발사되는 증상 발생.
## 수정
mutable state 를 별도 alert_state.py 모듈로 분리. 메인 스크립트는
"import alert_state" 만 실행하는데, 이 import 는 sys.modules 캐싱
덕분에 첫 실행 후 노옵 -> alert_state 모듈은 process lifetime 동안
같은 객체이며 그 attribute 들은 보존된다. 메인 스크립트 namespace 가
매번 새로 만들어져도 alert_state 의 state 는 영향받지 않음.
상태 항목:
- last_alert (signal type 별 마지막 발사 시각, 쿨다운용)
- last_fired_candle (signal type 별 마지막 발사 캔들 open_time, dedup)
- long_entry / short_entry (진입 추적)
- alert_lock, alert_symbol, alert_interval (스레드 동기화 + UI -> 스레드)
- alert_started, daily_report_started (스레드 1회 기동 가드)
- last_report_date (자정 통과 감지용)
## 검증
import 동작 확인 (별도 process 에서):
- 같은 모듈 객체 (s1 is s2)
- 같은 dict 객체 (s1.last_fired_candle is s2.last_fired_candle)
- attribute set/get 양쪽에 반영
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
35 lines
1014 B
Python
35 lines
1014 B
Python
"""
|
|
Streamlit 의 매 rerun 마다 메인 스크립트는 새 namespace 에서 재실행되어
|
|
모듈 최상단의 mutable state 가 모두 초기화된다 (`globals()` 가드도 우회됨).
|
|
|
|
이 파일은 별도 모듈로 단 한 번만 import 되므로 (Python 의 sys.modules 캐싱)
|
|
state 가 process lifetime 동안 보존된다. 알림 dedup, 진입 추적, 스레드
|
|
기동 가드 등 다중 rerun 환경에서 살아남아야 하는 모든 mutable 상태는
|
|
여기에 둔다.
|
|
"""
|
|
import threading
|
|
|
|
last_alert = {
|
|
"strong_long": 0, "strong_short": 0,
|
|
"long": 0, "short": 0,
|
|
"vol_long": 0, "vol_short": 0,
|
|
"short_caution": 0,
|
|
}
|
|
|
|
last_fired_candle = {
|
|
"strong_long": None, "strong_short": None,
|
|
"long": None, "short": None,
|
|
"vol_long": None, "vol_short": None,
|
|
"short_caution": None,
|
|
}
|
|
|
|
long_entry = None
|
|
short_entry = None
|
|
|
|
alert_symbol = "BTCUSDT"
|
|
alert_interval = "5m"
|
|
alert_lock = threading.Lock()
|
|
alert_started = False
|
|
daily_report_started = False
|
|
last_report_date = None
|