Skip to content

fix: 비멱등 주문 응답 유실 시 재전송 금지 — live 이중 체결 위험 차단 - #393

Merged
easygap merged 1 commit into
mainfrom
fix/order-lost-response-no-resubmit
Jun 9, 2026
Merged

fix: 비멱등 주문 응답 유실 시 재전송 금지 — live 이중 체결 위험 차단#393
easygap merged 1 commit into
mainfrom
fix/order-lost-response-no-resubmit

Conversation

@easygap

@easygap easygap commented Jun 9, 2026

Copy link
Copy Markdown
Owner

문제 (HIGH, 실거래 이중 체결)

브로커가 주문(POST)을 받았으나 응답이 유실되면(타임아웃·연결 리셋) 체결 여부가 불명하다. 기존 흐름:

  1. kis_api._request가 비멱등 요청에서 응답 유실 시 빈 dict 반환(내부 재전송은 이미 막혀 있었음)
  2. buy_order/sell_orderNone 반환
  3. _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_order 1회 호출·reconcile 대기·OrderGuard 유지·장부 미반영 검증.
  • 기존 idempotency 테스트를 새 계약(예외)으로 갱신.
  • 전체 스위트 1410 passed.

브로커가 주문(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
easygap merged commit 5d20e74 into main Jun 9, 2026
1 check passed
easygap added a commit that referenced this pull request Jul 6, 2026
fix: 비멱등 주문 응답 유실 시 재전송 금지 — live 이중 체결 위험 차단
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant