[BE-10] PaperTradeExecutor·LiveTradeExecutor
작업 내용 (설계 의도)
변경 사항
집행 전략 두 구현을 만든다. PaperTradeExecutor는 OrderGateway·ConditionalOrderGateway를 생성자에서 받지 않는다 — 토스에 sandbox가 없어 코드가 유일한 방어선이므로, 실주문을 “설정 실수로 나갈 수 있는 것”이 아니라 참조할 대상이 없는 것으로 만든다. 근거: ADR-003.
PaperTradeExecutor(costModel)— 모의 체결만 생성. 손절·익절 도달은 가격 조회로 판정한다(브로커 위임 불가·불필요).LiveTradeExecutor(orderGateway, conditionalOrderGateway, costModel)— 토스 실주문 + 매수 체결 직후 OCO 등록. OCO 등록 실패 시 즉시 시장가 청산(SellReason.OCO_REGISTRATION_FAILED) — 손절 없는 포지션을 보유하지 않는다.
주문 멱등 키는 clientOrderId = "AT-{proposalId}"로 부여한다. 제출 실패 시 재시도하지 않고 미체결 조회로 접수 여부를 확인한다(PRD FR-20).
LiveTradeExecutor는 빈으로 등록되되 정책 모드가 PAPER인 한 선택되지 않는다 — 빈 등록 자체를 토글하지 않는다(no-conditional-on-property).
롤백: 정책 모드를 PAPER로 되돌리면 Live 경로에 진입하지 않는다(배포 불필요).
의존
- BE-05 (포지션·체결), BE-06 (비용모델), BE-08 (OCO 어댑터)
다이어그램
처리 흐름
sequenceDiagram participant Svc as AutoTradingDomainService participant Live as LiveTradeExecutor participant Order as OrderGateway participant Oco as ConditionalOrderGateway Svc->>Live: buy(proposal, marketPrice) Live->>Order: placeOrder(clientOrderId=AT-42) Order-->>Live: tossOrderId Live->>Oco: placeOcoOrder(손절가, 목표수익률) Oco-->>Live: 실패 Live->>Order: 즉시 시장가 청산
클래스 의존
flowchart LR Iface[TradeExecutor] -.->|implements| Paper[PaperTradeExecutor] Iface -.->|implements| Live[LiveTradeExecutor] Paper --> Cost[TradingCostModel] Live --> Cost Live --> OrderGw[OrderGateway] Live --> OcoGw[ConditionalOrderGateway]
PaperTradeExecutor에서 Gateway로 가는 간선이 없다.
테스트 케이스
PaperTradeExecutor.buy()가 비용 모델을 적용한 모의 체결을 반환한다.PaperTradeExecutor생성자에 Gateway 타입 파라미터가 없다(리플렉션 검증).PaperTradeExecutor로 매수·매도를 수행해도 토스 요청이 0건이다(MockWebServer).LiveTradeExecutor.buy()가 체결 후 OCO를 등록하고 포지션에 ID를 붙인다.- OCO 등록이 실패하면 즉시 시장가 청산이 호출된다(손절 없는 포지션 방지).
- 같은 제안을 두 번 집행해도 동일
clientOrderId로 중복 주문이 방지된다. - 주문 제출 타임아웃 시 재시도하지 않고 미체결 조회로 접수 여부를 확인한다.