안녕하세요. 주식관련 프로그램을 제작하는 인천고래입니다.
요 몇 일 사이에 도서관에 와서 작업을 하고 있는데 다수의 인원들이 사용하는 네트웍이다보니 기존에 만든 로직이 엉키는 상황이 발생되었습니다.

네트웍이 느린 도서관에서만 실행을 하면 에러가 발생이 되는데
로그 시점을 보면 아래의 구간에서 거의 100%의 확률로 나옵니다.
횟수 => 0 2026-05-03 10:43:11
📡 openAPI : _OnReceiveTrData() TR 데이터 수신: rqname=주식기본정보요청, trcode=OPTKWFID, next=0
📡 openAPI : _OnReceiveTrData() TR 데이터 수신: rqname=계좌평가잔고내역, trcode=opw00018, next=
[V8 Client] opw00018 normalized portfolio rows=13
일종의 API 호출로 계좌번호를 받아와야 하는데 지연되면서 다음의 코드가 진행이 되게 되었고
못 받아온 계좌번호를 무시한 상태로 다음 코드에서 비어있는 계좌 번호로 계좌 관련 정보를 요청할 때 인해 에러가 발생한 케이스입니다.
즉, KHOpenAPI 요청들이 서로 겹치면서 입력값이 섞이는 현상으로 봐야 할 것 같습니다.
흐름이 이렇게 됩니다.
1. _app_trader_8_0.py (line 527)에서 시작 계좌조회 OPW를 예약함
opw00001, opw00004, opw00005, opw00007, opw00018을 0ms, 350ms, 700ms, 1050ms, 1400ms 간격으로 실행
2. 그런데 _app_trader_8_0.py (line 545) 이후 바로 startup 완료 처리되고, _app_trader_8_0.py (line 557)에서 대기 중이던 현재가 조회가 시작됨
3. 그래서 로그처럼 opw00001 받은 직후에 바로 get_multiple_current_prices()가 실행됨
4. 그 사이 아직 opw00018 같은 계좌 TR도 남아 있음
OPW 함수들은 이미 SetInputValue("계좌번호", ...)를 먼저 해놓고 comm_rq_data()를 부릅니다. 그런데 comm_rq_data() 안의 exit_check()가 _client_api_comm.py (line 267)에서 작은 이벤트 루프를 돌립니다.
이때 다른 QTimer가 끼어들 수 있어요.
그래서 이런 일이 생길 수 있습니다.
opw00004: 계좌번호 SetInputValue 함
opw00004: exit_check에서 대기하면서 이벤트 처리
그 사이 opw00005/opw00018/현재가조회가 끼어듦
SetInputValue 값이 다른 요청 값으로 덮임
원래 opw00004 CommRqData가 잘못된 입력값으로 전송됨
KHOpenAPI: 계좌정보를 찾을 수 없습니다
그래서 메시지는 계좌번호 오류처럼 보이지만,
실제 원인은 TR 요청 직렬화 실패 + SetInputValue/CommRqData 사이에 이벤트 루프가 열리는 구조입니다. “가끔” 발생하는 이유도 타이밍 문제라서 그렇고요.
임시 완화책으로는 현재가 조회 시작을 opw00018 이후 2~3초 뒤로 미루면 증상이 줄어들긴 하겠지만
근본 해결은 KHOpenAPI TR 요청 큐를 만들어서 SetInputValue -> CommRqData -> OnReceiveTrData 완료까지 한 번에 하나만 처리하게 만드는 쪽입니다.
그래서 임시 완화책인 호출을 하는 고정 텀은 근본 해결이 아니라서, 이번에 응답 기반 TR 요청 큐로 바꿨습니다.
변경 내용은 이렇습니다.
- _client_api_comm.py (line 214)에 enqueue_serial_tr_request() 큐 추가
- 시작 OPW 요청을 기존 0/350/700/1050/1400ms 타이머 방식에서 제거
- _client_api_comm.py (line 358)의 schedule_startup_opw_requests()가 이제 opw00001 -> opw00004 -> opw00005 -> opw00007 -> opw00018 순서로 큐에 넣음
- module/kiwoom_api/TR.py (line 182)에서 OnReceiveTrData 처리 후 큐에 “이 TR 끝났다”를 알려줌
- _app_trader_8_0.py (line 5724)의 주식기본정보요청(OPTKWFID) 현재가 묶음 조회도 같은 큐에 태움
_app_trader_7_2.py : get_multiple_current_prices()
Requesting current-price snapshot ['005690', '005740', '007110', '007690', '027830', '036630', '045390', '149950', '272110', '293580', '356860', '368970', '418420']
📡 openAPI : _OnReceiveTrData() TR 데이터 수신: rqname=체결잔고, trcode=opw00005, next=
[V8 Client] TR queue done: startup_opw00005 (received)
[V8 Client] TR queue request: startup_opw00007
📡 openAPI : _OnReceiveTrData() TR 데이터 수신: rqname=계좌별주문체결내역상세, trcode=opw00007, next=
opw00007 주문체결내역상세 정보가 없습니다. 0건
[V8 Client] TR queue done: startup_opw00007 (received)
[V8 Client] TR queue request: startup_opw00018
📡 openAPI : _OnReceiveTrData() TR 데이터 수신: rqname=계좌평가잔고내역, trcode=opw00018, next=
[V8 Client] opw00018 normalized portfolio rows=13
[V8 Client] TR queue done: startup_opw00018 (received)
즉 OPTKWFID가 opw00018 중간에 끼어드는 상황을 막기 위해 큐 형식으로 변경하였고
Kiwoom TR은 SetInputValue 상태를 공유하는 구조라, 요청 전송 구간은 병렬보다 직렬이 안전합니다.
실시간 수신, Chejan, 후처리 계산은 별개로 계속 비동기적으로 돌아갈 수 있고요.
이상 계좌정보를 찾을 수 없다는 에러 발생 원인과 해결 방식에 대해서까지 공유 드렸습니다.
오늘도 제 글을 읽어 주신 모든 분들께 감사의 말씀을 드립니다.
감사합니다. ^^
'인천고래 프로그램' 카테고리의 다른 글
| Codex에서는 왜? rg를 먼저 실행할까? (1) | 2026.05.03 |
|---|---|
| KRX API를 걷어 냈습니다. (0) | 2026.05.02 |
| 일봉 + 5분봉(분봉) 데이터 수집/리포트 프로그램 안내 (0) | 2026.02.18 |
| KRX 시세 데이터, 이제 로그인 & API를 신청 해야 쓸 수 있네요. ㅜ (0) | 2026.01.06 |