본문 바로가기
Project Archive/Quant & Auto Trading

[자동매매] 실전운영 16일차 - 표면 +22,550원, 그 뒤에 가려진 봇 hang과 F5 도입

by 병헤는 밤 2026. 4. 28.
반응형

4월 28일 매매 일지 -- 표면 +22,550원, 그 뒤에 가려진 봇 hang과 F5 도입

화요일. KOSPI/KOSDAQ 모두 BULL(BOTH_BULL) 환경에서 청산 3건·매수 7건. 순손익 +22,550원(PF 2.02, 승률 33.3%) 으로 표면적으론 흑자 마감했지만, 실제로는 09:00~10:16 봇이 70분간 hang으로 정지했다가 재시작 후 매수 6건이 몰린, 시스템 안정성 측면에서 가장 위험했던 날. pension 유니버스 빌드에서 발견한 pykrx hang을 timeout 패치로 잡았고, 14:10 대우건설 매수를 계기로 안전 필터 F5(당일 고가 -5% 낙폭)를 추가했다.

시장 환경

지수 현재가 (09:00) 20일선 판정
KOSPI (KODEX 200) 100,240 90,782 BULL
KOSDAQ (KODEX 코스닥150) 20,670 19,161 BULL

3팩터 모두 BULL → ALL_BULL 100% 예산 스케일 적용 가능 환경. 그러나 pension은 09:00~09:06 매수 윈도우에 봇이 hang 상태였기에 사실상 비가동.

🚨 시스템 경보 -- 09:00~10:16 봇 hang

오전 발생한 사건:

  • 09:00:43 키움 웹소켓 로그인 응답 타임아웃 → reader 태스크 예외 처리 중 _reconnect()도 같은 RuntimeError를 던져 reader가 영구 사망. 실시간 시세 피드 단절.
  • 09:00:45 pension _analyze_stock에서 K필터 통과 1,753종목 sem=8 동시 조회 진입 직후 hang. 이후 70분간 어떤 로그도 추가되지 않음. VB 사이클·청산 감시까지 모두 정지.
  • 10:16 사용자 발견 → 패치 3종 적용 후 재시작.

근본 원인은 pykrx의 webio.Get/Post.readrequests.Session.get/post에 timeout을 안 넘긴다는 것. KRX 서버가 응답하지 않으면 OS TCP 기본값(수 시간)까지 블록된다. asyncio.wait_for로 future를 취소해도 underlying executor 스레드와 connection은 그대로 누수되어, 누적되면 워커 풀이 고갈된다.

적용한 패치 3종

  1. core/logger.py -- install_pykrx_timeout(15s)를 모듈 로드 시 자동 호출. webio.Get.read/Post.read를 monkey-patch해 모든 KRX HTTP 호출에 socket timeout 강제. 봇·tools/·market_data 일괄 보호.
  2. strategy/bunt_pension.py -- _analyze_stock의 pykrx 호출에 asyncio.wait_for 10s/5s 이중 안전망.
  3. core/kiwoom_realtime.py -- _reader_loop가 reconnect 실패에도 죽지 않도록 1→30s exponential backoff 재시도.

드라이런 시뮬(1,753종목 sem=8 동일 패턴)에서 패치 전 49분+ hang → 패치 후 124.8초 완주, 96.7% 성공, 57건 ReadTimeout 정상 해제로 검증.

청산 내역 (3건)

시각 종목 진입가 청산가 PnL 사유 보유 K
10:30:34 삼성전자 (005930) 228,500 225,000 -21,000 (-1.53%) TIME_STOP D+5 0.48
14:04:27 HD현대에너지솔루션 (322000) 203,625 203,500 -1,000 (-0.06%) BREAKEVEN_STOP D+0 0.60
14:37:11 대한전선 (001440) 44,500 45,850 +44,550 (+3.03%) TRAILING_STOP D+4 0.45

청산 손익 합계: +22,550원 (1승 2패, 승률 33.3%, PF 2.02, 평균 +0.48%)

  • 삼성전자 TIME_STOP -- 4/23 진입 후 5영업일 보유, +1% 미만 횡보로 시간 손절. 진입가 대비 -1.53%로 확대 손절(ATR×2.0)이나 본전 손절상향(BE)이 발동하지 않은 상태에서 시간 룰이 깨끗하게 작동. 자본 회수 측면에서 정상 동작.
  • HD현대에너지솔루션 BREAKEVEN_STOP -- 10:18 진입 후 일시 +3% 이상 상승해 BE 손절상향이 잡혔으나, 14:04에 진입가까지 회귀해 본전 컷. -0.06% 손실은 호가 스프레드 수준. 당일 진입·당일 청산은 변동성 회수 시그널이 약했다는 뜻.
  • 대한전선 TRAILING_STOP -- 4/24 진입 후 +5% 트레일링 진입, 4영업일 만에 -2% 컷. 단일 종목이 오늘 PnL을 사실상 단독으로 결정한 셈(승률 33.3%인데 PF가 2.02인 비대칭 분포의 전형).

매수 내역 (7건)

시각 종목 진입가 수량 금액 K 비고
10:18:16 삼성E&A (028050) 56,800 28 1,590,400 0.56 재시작 직후 첫 진입
10:18:18 현대차 (005380) 562,000 2 1,124,000 0.57 대형주
10:18:19 HD현대에너지솔루션 (322000) 203,625 8 1,629,000 0.60 당일 BE 청산
10:22:25 NAVER (035420) 222,000 8 1,776,000 0.65 K 최고치(노이즈 큼)
11:45:29 삼성전기 (009150) 836,000 1 836,000 0.53 단가↑로 1주만
14:10:35 대우건설 (047040) 37,950 50 1,897,500 0.55 ⚠️ F5 트리거 케이스
14:43:20 대한전선 (001440) 46,000 38 1,748,000 0.45 🔁 6분 만에 재진입

총 매수 금액 약 10,600,000원. 봇 재시작(10:16) 직후 4분 안에 4건이 몰렸는데, 이는 hang 동안 누적된 돌파 신호가 한 번에 뚫린 결과. 대형주(현대차·삼성전기) 진입은 PER_SLOT_CAP=200만 동적 분배 + per-slot 예산 150만 환경에서 1주 단위로 가능하게 된 것이 시각적으로 확인됨.

🔭 특이 분석

1. 대한전선 6분 재진입

14:37 TRAILING_STOP으로 +3.03% 익절(D+4) → 14:43 재돌파로 즉시 재진입. TRAILING_STOP 사유의 자동 복귀 쿨다운이 0일이라 가능한 흐름이고, 진입가가 청산가(45,850)보다 +0.33% 높은 수준으로 추세가 유지되고 있다는 시그널. 동일 종목이 같은 날 두 번 등장하는 케이스로 K값(0.45 → 0.45)·VB trigger(44,766)·진입 사이즈(50주 → 38주)는 슬롯당 잔액 차이가 반영된 결과.

2. 14:10 대우건설 -- F5 도입의 직접 동기

대우건설(047040)은 09:00 시가 33,850원에서 09:45 장중 고가 40,350원까지 +19% 갭상승 후 종일 하락. 14:10 시점 37,950원은 당일 고점 대비 -5.95% 자리에서의 매수.

봇 로그 추적 결과 F4(현재가/20일고가 1.05x 상한)는 09:00~14:06까지 1.05~1.08x 범위에서 계속 차단했으나, 가격이 지속 하락하며 ratio가 줄어 14:06에 1.047x로 자연 통과. 즉 F1~F4 모두 일봉/누적 데이터 기반이라, 장중 고점 후 종일 빠지는 종목은 가격이 빠질수록 오히려 통과 쉬워지는 역설이 있다.

대응: MAX_INTRADAY_DRAWDOWN = 0.05 상수를 추가하고 F5(당일 고가 대비 -5% 낙폭이면 차단)를 F4 다음에 삽입(strategy/volatility_breakout.py·main.py). 시간대 cutoff 단축(예: 14:50→14:00)도 검토했으나 14:10 케이스에 fit한 후행 선택이고 다른 시간대 패턴은 못 잡으므로 over-fit으로 간주해 기각. 설계 원칙은 case-avoidance가 아니라 mechanism-block.

보유 종목 현황 (10종목, 슬롯 10/12)

종목 진입가 진입일 D+ K 비고
HD한국조선해양 (009540) 473,000 04/24 D+4 0.61 방산·조선
두산에너빌리티 (034020) 126,900 04/24 D+4 0.54 에너지·발전
현대로템 (064350) 247,000 04/24 D+4 0.55 방산
알테오젠 (196170) 385,000 04/27 D+1 0.50 바이오
삼성E&A (028050) 56,800 04/28 D+0 0.56 건설·엔지니어링
현대차 (005380) 562,000 04/28 D+0 0.57 자동차
NAVER (035420) 222,000 04/28 D+0 0.65 IT
삼성전기 (009150) 836,000 04/28 D+0 0.53 IT 부품
대우건설 (047040) 37,950 04/28 D+0 0.55 건설 (F5 트리거 case)
대한전선 (001440) 46,000 04/28 D+0 0.45 전기·케이블 (재진입)

섹터 분포는 방산·조선·에너지·자동차·건설·IT가 고르게 섞여 분산이 어느 정도 자연 발생. 04/24 방산·중공업 묶음(HD한국조선해양·두산에너빌리티·현대로템) 3건이 동일 시기 진입한 점은 섹터 쿼터(현재 비활성)의 재검토 여지로 남겨두던 부분.

📊 인사이트

  • PF 2.02·승률 33.3%는 전형적인 비대칭 분포로, 대한전선 단일 +44,550이 결과를 단독 결정. 시스템 신호가 약한 날엔 한 종목의 트레일링 폭이 PnL을 좌우한다는 점이 다시 확인됨.
  • K구간 성과(daily_summary insight): K≤0.5 평균 +0.75% (2건) vs K>0.5 평균 -0.06% (1건). 표본 적지만 노이즈가 큰 환경(K가 높음)에서 진입한 HD현대에너지솔루션이 즉시 BE로 빠진 흐름과 일치.
  • 평균 보유일 약 3.1일(5/0.16/4.17). TIME_STOP 한 건이 평균을 끌어올렸고, BE 즉시 컷이 오늘 한 건 발생.
  • 주간 누계(4/21~4/28 6영업일): 27건 청산, 21승, 순손익 +657,298원. 4/22 -139,350 한 번을 제외하면 5/6일 흑자. PF가 누적 안정 구간에 진입.

🐛 미해결 숙제

  • pension universe pre-build 분리 -- 오늘 hang의 직접 원인은 pension의 09:00 즉시 빌드. 하지만 K/C/D 필터·ATR 모두 전일 일봉 데이터만 사용하므로 장중에 돌릴 필요가 없다. tools/build_pension_universe.py를 신규 작성해 평일 08:30 또는 16:35 cron으로 캐시화하면 매수 윈도우 6분을 100% 활용할 수 있음. 다음 세션 과제로 메모리에 기록.
  • F5 임계값 -5% 적정성 -- 단일 케이스(대우건설 -5.95%)만으로 도입한 보수적 기본값. 1~2주 라이브 데이터로 false positive 분포를 확인 후 조정(-3%/-7% 검토). 분봉 백테스트는 ohlcv_1min 인프라(2026-04-22~ 수집중) 누적 후 별도 트랙.
  • 대우건설(047040) 보유 50주 -- F5는 신규 진입에만 영향, 기존 보유 청산 로직(STOP_LOSS·TIME_STOP·TRAILING)은 그대로. 후속 처리 필요할 수 있음.

오늘의 교훈

확인한 것

  • pykrx의 webio가 timeout 미설정인 점이 모든 KRX 의존 코드의 잠재적 데드락 포인트였다는 사실. install_pykrx_timeout()로 일괄 보호 가능.
  • 웹소켓 reader 사망 시 reconnect 자체가 예외를 던지면 영구 사망하는 패턴은 backoff 중첩 try/except로 해결 가능.
  • 드라이런 시뮬레이션(1,753 sem=8)이 실측 정량 검증 도구로 유용. 49분+ hang vs 124.8s 완주의 차이가 명확.
  • F5는 mechanism-block이라 시간 무관 일반화 가능. case-fit 로직(시간 cutoff)과 구별됨.

경계할 것

  • 봇 hang 70분의 탐지 지연 자체가 가장 큰 위험이었다. 사용자 발견까지 자동 알림이 없었음. heartbeat/idle 감시 알림이 별도 작업으로 필요.
  • 매수 7건 중 6건이 재시작 직후 4분에 몰렸다는 점은 burst load가 정상 관측이지만, 그동안 막혔던 신호가 한 번에 뚫리는 환경에서는 F5 같은 안전망이 더 중요하게 작용함.
  • 승률 33%·PF 2.02는 표면적으로 양호하나, 1건이 결과를 지배할 때는 승률·PF 단독 해석보다 비대칭 분포 자체를 봐야 함.

내일 체크포인트

  1. 🔴 09:00 봇 정상 가동 확인. 특히 [PENSION] 유니버스 구축 완료 로그가 처음으로 찍히는지 확인 (지금까지 한 번도 완료 로그 없었음).
  2. 🔴 09:00 키움 웹소켓 로그인 성공 여부 확인. 실패 시 새 backoff 경로(1→30s)가 동작하는지 로그로 검증.
  3. 🟡 F5 트리거 케이스 발생 시 로그 검토(당일 고가 이탈 매수 스킵 메시지). 어떤 종목이 어느 시점에 차단됐는지 누적해 임계값 적정성 평가 시드 데이터로 활용.
  4. 대우건설(047040) 추세 모니터. F5 안전망이 없던 시점에 들어간 포지션이라 별도 주시.
  5. pension universe pre-build 분리 작업 검토 (다음 과제).

소감

표면적으론 흑자 +22,550원에 PF 2.02로 마감했지만, 오늘은 결과가 아니라 봇 안정성을 갈아 넣은 날이다. pykrx hang은 4일째 기록만 남아 있던 잠재 버그가 오늘 처음 fully blocking 형태로 노출된 것이고, F5는 case-fit 유혹을 한 번 거치고 mechanism으로 재정식화된 결과물이다. 시스템이 위험을 명시적으로 보여주고, 그 위험을 봉합하는 패치를 검증된 형태로 적용한 하루로 정리한다.

 

4/28 일일매매 내역

 

4/28 관심종목 교체내역

 

반응형

댓글