fix: 비멱등 주문 응답 유실 시 재전송 금지 — live 이중 체결 위험 차단 - #393
Merged
Conversation
브로커가 주문(POST)을 받았으나 응답이 유실되면(타임아웃·연결 리셋) 체결 여부가 불명하다. 기존에는 _request가 빈 dict를 돌려주고 buy_order/sell_order가 None을 반환했는데, _execute_with_retry가 이 None을 "단순 실패"로 보고 같은 주문을 재전송(최대 3회)해 한 신호에 두 번 체결될 수 있었다(HIGH). 응답 유실(체결 불명)과 브로커의 명시적 거부(주문 미접수)를 구분해 처리한다. - api/kis_api.py: 비멱등 요청의 네트워크/타임아웃 오류 시 빈 dict 대신 KISOrderResponseUnknown 예외를 던진다(POST는 여전히 1회만). 해외주문 경로는 계약 유지를 위해 예외를 잡아 None 반환. - core/order_executor.py: _execute_with_retry가 이 예외를 받으면 재전송하지 않고 ORDER_RESPONSE_UNKNOWN 표식을 반환. 호출부는 주문을 SUBMITTED로 유지하고 OrderGuard도 유지(중복 차단)하며, 장부 미반영 + requires_reconcile로 다음 KIS↔DB 동기화에서 실제 체결분을 대조하게 한다. dead-letter에는 넣지 않는다 (접수됐을 수 있어 '주문 누락'이 아님). - 명시적 거부(rt_cd!=0)·서킷오픈·400/403 등 '미접수' 경로는 기존대로 None → 재시도·dead-letter 유지. 테스트: 응답 유실 시 buy_order 1회 호출·reconcile 대기·OrderGuard 유지·장부 미반영 회귀 테스트 추가. 기존 idempotency 테스트는 새 계약(예외)으로 갱신.
easygap
added a commit
that referenced
this pull request
Jul 6, 2026
fix: 비멱등 주문 응답 유실 시 재전송 금지 — live 이중 체결 위험 차단
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
문제 (HIGH, 실거래 이중 체결)
브로커가 주문(POST)을 받았으나 응답이 유실되면(타임아웃·연결 리셋) 체결 여부가 불명하다. 기존 흐름:
kis_api._request가 비멱등 요청에서 응답 유실 시 빈 dict 반환(내부 재전송은 이미 막혀 있었음)buy_order/sell_order가None반환_execute_with_retry가 이None을 단순 실패로 보고 같은 주문을 재전송(최대 3회) → 한 신호에 두 번 체결즉, 직전 라운드에서
_request내부 재전송은 막았지만 상위 재시도 래퍼가 호출을 다시 시도하는 구멍이 남아 있었다.수정
응답 유실(체결 불명)과 브로커의 명시적 거부(주문 미접수)를 구분해 처리한다.
api/kis_api.py: 비멱등 요청의 네트워크/타임아웃 오류 시 빈 dict 대신KISOrderResponseUnknown예외를 던진다(POST는 여전히 1회만). 해외주문 경로는 계약 유지를 위해 예외를 잡아None반환.core/order_executor.py:_execute_with_retry가 이 예외를 받으면 재전송하지 않고ORDER_RESPONSE_UNKNOWN표식 반환. 호출부는 주문을SUBMITTED로 유지(미완료 주문으로 남아 다음 시도의 중복 차단) + OrderGuard 유지 +requires_reconcile로 장부 반영 보류. dead-letter에는 넣지 않는다(접수됐을 수 있어 '주문 누락'이 아님).rt_cd!=0)·서킷오픈·400/403 등 '미접수' 경로는 기존대로None→ 재시도·dead-letter 유지.테스트
test_live_buy_lost_response_does_not_resubmit_and_requires_reconcile: 응답 유실 시buy_order1회 호출·reconcile 대기·OrderGuard 유지·장부 미반영 검증.