Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3846abf
휴장일 달력 교정: 9/28은 거래일, 2027 설·추석 날짜 수정
easygap Sep 23, 2026
be9d2c8
일일 사이클: 결측 보충 복구, 휴장일 매매 차단, 실측 커버리지로 판정
easygap Sep 23, 2026
7442f23
배치율 감시를 실제로 울리게 하고, 소액 계좌 유휴 현금은 파킹 ETF로
easygap Sep 23, 2026
eb66ac4
지수 자료가 멈춰도 조용히 넘어가지 않게: 신선도 검사·대용 지수·헬스 표시
easygap Sep 23, 2026
d38e5a7
주문 단계를 계획과 같은 시가로 판정하고, 거부 사유를 남긴다
easygap Sep 23, 2026
e5be4f4
리포트 지표: 입금을 흡수한 스냅샷 기준으로 중화하고 주간 기준일을 바로잡음
easygap Sep 23, 2026
559abba
테스트가 운영 파일을 건드리지 않게 격리
easygap Sep 23, 2026
7c542bc
평가: 규칙·설계 변경 이후 창을 따로 재고, 승격 판정의 NaN을 막음
easygap Sep 23, 2026
286ab00
대시보드: 거래일 기준 경보, 스냅샷 시점 원금, 경보 채널 실패 기록
easygap Sep 23, 2026
348b5cc
관찰 트랙 오버레이 연구를 운영 조건으로 재계산 — 9/17 권장 철회
easygap Sep 23, 2026
e80900c
DB 시작 시 모델과 실제 스키마를 대조하고 안전한 어긋남은 고침
easygap Sep 23, 2026
0dc3cde
평가: paper 트랙의 주문·사이클 오류 이벤트를 참고 지표로 보여 줌
easygap Sep 23, 2026
5561ca9
운영 원칙 우선순위 표 상태 갱신 (P1 완료, P3 도구 추가, 배치율 감시 교정 기록)
easygap Sep 23, 2026
b5572a5
시가 노출 테스트가 실행 시각(장 마감 진입 차단 시간대)에 따라 실패하지 않게
easygap Sep 23, 2026
5f83293
문구 다듬기
easygap Sep 23, 2026
367d5ff
신호 점수: MACD·이동평균 점수가 지표가 생기기 전 구간을 잘못 채점하던 문제 수정
easygap Sep 23, 2026
83c1ffb
돌파 전략: 청산 기준을 진입 때 뚫은 가격으로 고정
easygap Sep 23, 2026
6b0dad1
평균회귀: 52주 필터를 백테스트에도 적용
easygap Sep 23, 2026
0feaa3a
추세 추종: trend_ma_period 설정이 실제로 반영되게
easygap Sep 23, 2026
41b6596
지수 자료를 전략마다 한 번만 받고, 회전 전략 시장 필터가 첫날 상태로 굳던 문제 수정
easygap Sep 23, 2026
d999062
시장 국면 필터: 켜면 매수가 전부 막히던 조회 기간 문제 수정
easygap Sep 23, 2026
97c75ca
앙상블: 모드 전환을 날짜마다 그 전 60일 신호로만 판단
easygap Sep 23, 2026
f79ebca
블랙스완 감지기: 백테스트와 모의투자의 동작 차이를 주석으로 남김
easygap Sep 23, 2026
c8b6383
문서: 지운 전략 설정(추세 추종 손절 배수, buy_threshold_offset) 설명 정리
easygap Sep 23, 2026
3edcb0d
백테스트 지표: 빠지던 청산 사유를 통계에 넣고 칼마 부호 수정
easygap Sep 23, 2026
6e20b3e
백테스트 리포트: 슬리피지 설정 키와 국면별 월수익률·MDD 계산 수정
easygap Sep 23, 2026
4b2b4ec
단일 종목 백테스트: 같은 시가 갭다운 가짜 왕복 제거, 최소 보유 기간 규칙을 실전과 맞춤
easygap Sep 23, 2026
ef0efee
포트폴리오 백테스트: 시가 주문을 종가 청산보다 먼저 처리하고, 자료가 빈 날은 마지막 종가로 평가
easygap Sep 23, 2026
0f525f7
검증 벤치마크(상위 50종목 동일비중): 늦게 상장한 종목이 기간 전체를 자르지 않게
easygap Sep 23, 2026
4dab61e
워크포워드·OOS 검증: 앞 구간으로 지표를 예열한 뒤 평가 구간만 잰다
easygap Sep 23, 2026
519a0a3
샤프 기준 표기를 '금리 3%'로 통일하고, 운영 지표와의 차이 방향을 바로잡음
easygap Sep 23, 2026
1d3262d
KIS 접근 토큰과 사용량 통계를 프로세스 안에서 공유
easygap Sep 23, 2026
7d20c50
서킷 브레이커: 알림은 락 밖에서 보내고, 조회 응답의 한도·토큰 오류는 장애로 세지 않음
easygap Sep 23, 2026
3a4483c
스케줄러 헬스체크가 정상 DB에서도 10분마다 'DB 연결 실패'를 내던 문제 수정
easygap Sep 23, 2026
54b41f3
스케줄러는 바스켓을 거래하지 않는다 — 바스켓 실행은 일일 CLI 하나로
easygap Sep 23, 2026
9cded59
evidence 날짜와 경과일을 KRX 거래일 기준으로
easygap Sep 23, 2026
9a9e10d
스케줄러 장마감을 15:35 이후 언제든 한 번 실행하고, 리포트 일간 수익률을 실제로 계산
easygap Sep 23, 2026
b084c2f
live 전환 게이트의 자료 점검을 보유 종목·지수의 최근 봉 기준으로
easygap Sep 23, 2026
73870a1
재스캔 후보에도 신호 시각을 남겨 30분 지난 후보는 버리게
easygap Sep 23, 2026
ff54ea3
스케줄러 휴장일 자동 갱신: 프로젝트 기준 경로를 쓰고, pykrx를 못 쓰면 건너뜀
easygap Sep 23, 2026
251aceb
웹소켓: 한 프레임에 여러 건이 오면 모두 전달하고, 런타임에 연결돼 있지 않다고 문서에 명시
easygap Sep 23, 2026
697d484
문서: 바스켓 실행 경로가 일일 CLI 하나라는 점을 반영
easygap Sep 23, 2026
60106aa
평가: paper 오류 집계가 metrics 없는 결과에서 KeyError를 내던 문제, 바뀐 문구에 맞춰 테스트 수정
easygap Sep 23, 2026
cfbfc7d
계좌 낙폭 가드는 자기 낙폭 규칙이 있는 바스켓 주문만 넘기고, 가격 없이 잰 요약의 음수 MDD를 막음
easygap Sep 23, 2026
c1fa5b5
대시보드: 검증 결과를 처음 불러오는 동안 '불러오지 못했습니다'가 뜨던 문제
easygap Sep 23, 2026
f7be7cc
위험 조절 설명: 069500으로 대신 판단한 날을 '비중 확대 보류'로 쓰지 않게
easygap Sep 23, 2026
7ce65f0
일일 카드: 일간 수익률을 계산하지 못한 날은 0.00% 대신 '—'로
easygap Sep 23, 2026
56ec837
결측 보충은 paper 트랙에서만
easygap Sep 23, 2026
ba45568
나중에 채운 추정 기록은 낙폭 고점으로 쓰지 않음
easygap Sep 23, 2026
5378c2e
대시보드 테스트: 검증 결과를 불러오는 중·실패·완료 상태의 안내 문구 확인
easygap Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions api/circuit_breaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,25 @@ def on_success(self):
self._half_open_probe_in_flight = False
self._half_open_probe_started_at = 0.0

def release_probe(self):
"""HALF_OPEN probe가 확정적 응답(400/401/403/429 등)을 받았을 때 호출.

서버가 응답했으니 장애는 아니지만 요청이 성공한 것도 아니다. 상태는
HALF_OPEN으로 두고(닫지 않는다) probe 점유만 풀어, 다음 호출이
recovery_timeout(60초)을 기다리지 않고 곧바로 다시 probe하게 한다.
풀지 않으면 그동안 손절 SELL을 포함한 모든 KIS 요청이 이유 없이 막힌다.
"""
with self._lock:
if self.state == CircuitState.HALF_OPEN and self._half_open_probe_in_flight:
self._half_open_probe_in_flight = False
self._half_open_probe_started_at = 0.0
logger.info(
"Circuit Breaker HALF_OPEN probe 해제 — 서버가 확정 응답, 상태는 HALF_OPEN 유지"
)

def on_failure(self):
"""요청 실패(50x, 타임아웃 등) 시 호출"""
alert_failure_count = None
with self._lock:
self.last_failure_time = time.monotonic()
self._half_open_probe_in_flight = False
Expand All @@ -91,24 +108,29 @@ def on_failure(self):
# HALF_OPEN 상태에서 또 실패하면 다시 OPEN으로 회귀
self.state = CircuitState.OPEN
logger.warning("Circuit Breaker 복구 실패: 다시 OPEN 상태로 전환")

elif self.state == CircuitState.CLOSED:
self.failure_count += 1
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
logger.error("🚨 Circuit Breaker 발동! 상태 전환: CLOSED -> OPEN ({}회 연속 실패)", self.failure_count)

# 여기서 디스코드나 긴급 알림 연결 가능 (외부에서 구독)
self._trigger_alert()
alert_failure_count = self.failure_count

# 알림은 Discord·SMTP 네트워크 I/O(수 초 이상)라 락을 놓은 뒤 보낸다. 락 안에서
# 보내면 그동안 can_request/on_success를 부르는 모든 스레드(주문 제출·손절
# SELL 포함)가 알림 전송이 끝날 때까지 멈춘다. 발동 판정은 위 락 안에서 끝났다.
if alert_failure_count is not None:
self._trigger_alert(alert_failure_count)

def _trigger_alert(self):
def _trigger_alert(self, failure_count: int | None = None):
"""서킷 브레이커 오픈 시 알림 — 치명적 이벤트이므로 모든 채널 동시 발송."""
count = self.failure_count if failure_count is None else failure_count
try:
from core.notifier import Notifier
notifier = Notifier()
notifier.send_message(
f"🚨 **서킷 브레이커 발동 (API 차단)**\n"
f"연속 {self.failure_count}회 API 요청 실패로 인해 모든 통신을 {self.recovery_timeout}초간 차단합니다.\n"
f"연속 {count}회 API 요청 실패로 인해 모든 통신을 {self.recovery_timeout}초간 차단합니다.\n"
f"서버 다운 또는 장애가 의심됩니다.",
critical=True,
)
Expand Down
Loading
Loading