[BE-11] AutoTradingDomainService

작업 내용 (설계 의도)

변경 사항

사이클 전체를 조율하는 도메인 서비스를 만든다. 입력은 application이 매핑해 넘긴 List<TradeCandidate>, 출력은 List<OrderProposal>이다.

흐름: 정책 로드 → 거래일 로드 → 열린 포지션 조회 → 후보별 RiskGate 판정 → 통과분 사이징 → 임계 비교로 자동/승인 분기 → 자동분 집행 → 체결·포지션·거래일 갱신 → 알림.

핵심 규칙 셋:

  • 게이트를 우회하는 경로를 만들지 않는다. 집행은 반드시 RiskGate 통과 후에만 일어난다.
  • 거부된 후보도 OrderProposal로 전량 저장한다 — 감사 로그를 겸한다.
  • 집행 직전 policy.canPlaceNewOrder()를 트랜잭션 안에서 재확인한다 — 킬 스위치 경합 방어.

List<TradeExecutor>를 주입받아 정책 모드에 맞는 구현을 선택한다.

의존

  • BE-02, BE-03, BE-04, BE-05, BE-07, BE-09 (TradeExecutor 는 interface 의존이라 병렬 가능)

다이어그램

처리 흐름

sequenceDiagram
    participant Svc as AutoTradingDomainService
    participant Gate as RiskGate
    participant Exec as TradeExecutor
    participant Repo as OrderProposalRepository
    Svc->>Gate: evaluate(candidate, ...)
    Gate-->>Svc: 통과
    Svc->>Svc: canPlaceNewOrder() 재확인
    Svc->>Exec: buy(proposal, marketPrice)
    Exec-->>Svc: TradingFill
    Svc->>Repo: saveAll(제안 전량)

클래스 의존

flowchart LR
    Svc[AutoTradingDomainService] --> Gate[RiskGate]
    Svc --> Exec[TradeExecutor]
    Svc --> Policy[AutoTradingPolicyRepository]
    Svc --> Day[TradingDayRepository]
    Svc --> Prop[OrderProposalRepository]
    Svc --> Pos[TradingPositionRepository]
    Svc --> Alert[AutoTradingAlertGateway]

테스트 케이스

  • 게이트를 통과한 후보가 임계 이하면 승인 없이 집행된다.
  • 임계를 초과하면 집행하지 않고 PENDING 제안만 만든다.
  • 게이트가 거부한 후보도 REJECTED_BY_RISK 제안으로 저장된다(감사 로그).
  • 정책 모드가 PAPERPaperTradeExecutor가 선택된다.
  • 집행 직전 킬 스위치가 켜지면 그 사이클의 집행이 중단된다(경합 방어).
  • 당일 손실 한도에 도달하면 이후 후보를 전건 거부한다.
  • 체결 후 거래일 실현손익과 집행 건수가 갱신된다.