[BE-02] AutoTradingPolicy 애그리게이트

작업 내용 (설계 의도)

변경 사항

자동매매 정책(싱글톤 1행)을 Rich Domain Entity로 만들고 영속화한다. 실행 모드·킬 스위치·사다리 단계·운용 자본을 보유하며, 집행 가부 판단을 이 Entity가 캡슐화한다. 호출부가 policy.executionMode == PAPER 식으로 상태를 비교하지 않게 canPlaceNewOrder()·selectExecutor() 질의 메서드를 노출한다.

기능 토글을 @ConditionalOnProperty가 아니라 이 행의 값 조회로 처리하는 것이 핵심이다 — 재기동 없이 모드 전환·킬 스위치·롤백이 된다. 근거: ADR-001.

load()는 행이 없으면 기본값(PAPER·T1·킬스위치 off·자본 100만원)으로 생성해 반환한다.

롤백: 정책 행 값을 이전 값으로 되돌리면 즉시 반영된다(배포 불필요).

의존

  • BE-01 (도메인 계약), DB-01 (스키마)

다이어그램

처리 흐름

sequenceDiagram
    participant Svc as 호출 DomainService
    participant Repo as AutoTradingPolicyRepository
    participant Entity as AutoTradingPolicy
    Svc->>Repo: load()
    Repo-->>Svc: AutoTradingPolicy
    Svc->>Entity: canPlaceNewOrder()
    Entity-->>Svc: true / false

클래스 의존

flowchart LR
    Entity[AutoTradingPolicy] --> Mode[ExecutionMode]
    Entity --> Tier[RiskTier]
    Repo[AutoTradingPolicyRepository] -.->|implements| Impl[AutoTradingPolicyRepositoryImpl]
    Impl --> Table[(auto_trading_policy)]

테스트 케이스

  • 행이 없을 때 load()가 기본값(PAPER·T1·킬스위치 off)으로 생성해 반환한다.
  • 킬 스위치가 켜지면 canPlaceNewOrder()가 false를 반환한다.
  • promoteTier()가 T1을 T2로 올리고 전환 시각을 기록한다.
  • demoteTier()가 T2를 T1로 내린다.
  • 최하 단계 T1에서 demoteTier()를 호출해도 T1을 유지한다(경계값).
  • 저장 후 재조회 시 모드·단계·킬스위치가 보존된다(TestContainers MySQL).