[BE-18] RiskTier 총 노출 비율 추가
이 티켓은 BE-30 으로 흡수됐습니다 —
RiskGate.kt·RiskTier.kt·TradeCandidate.kt를 같이 건드리므로 분리하면 Single Writer 충돌입니다 (Tickets/BE-30-게이트-입력불변식-총노출.md). 이 파일은 이력 보존용이며 착수 대상이 아닙니다.BE-30 이 흡수한 이유가 하나 더 있습니다 — codex 재검수에서
totalExposureLimit이 검증 없는 외부 입력이라 실제 T1 상한 50만원을 넘겨도 승인되는 p0 가 나왔습니다. 값의 출처를 만드는 것(이 티켓)과 입력을 검증하는 것(BE-30)이 같은 결함의 양면입니다.
작업 내용 (설계 의도)
변경 사항
RiskTier가 totalExposureRatio를 보유해 종목당 비율·일일 손실 한도와 대칭을 이루게 한다. 현재 RiskGate는 RiskPolicySnapshot.totalExposureLimit을 외부 주입으로 받는데, 그 값의 출처가 코드 어디에도 없어 호출부(BE-11)가 값을 지어낼 위험이 있다.
값은 이론상 최대치 — positionRatio × maxOpenPositions다 (2026-08-28 확정, TDD “RiskGate 입력 계약”).
| 단계 | 종목당 | 보유 상한 | 총 노출 상한 |
|---|---|---|---|
| T1 | 10% | 5종목 | 50% |
| T2 | 15% | 5종목 | 75% |
| T3 | 20% | 4종목 | 80% |
종목당 한도와 보유 수 한도가 이미 같은 상한을 강제하므로, 이 검사는 구속력 있는 제약이 아니라 회계 드리프트를 잡는 안전망이다 — 포지션 종료 누락 등으로 장부 노출이 실제보다 크게 잡히면 걸린다. 별도 정책 컬럼을 두지 않는다(단계와 무관하게 움직일 이유가 없고, 컬럼을 두면 세 한도의 정합을 사람이 지켜야 한다).
의존
- 없음 (main에 머지된
RiskTier수정)
다이어그램
처리 흐름
해당 없음 — enum에 파생 계산 메서드를 추가하며 협력 객체 간 흐름이 없다.
클래스 의존
flowchart LR Tier[RiskTier] --> Limit[totalExposureLimit 산출] Svc[AutoTradingDomainService] --> Tier Svc --> Snap[RiskPolicySnapshot]
테스트 케이스
RiskTier.T1.totalExposureLimit(100만원)이 50만원을 반환한다.RiskTier.T2.totalExposureLimit(100만원)이 75만원을 반환한다.RiskTier.T3.totalExposureLimit(100만원)이 80만원을 반환한다 — 보유 상한이 4종목이라 T2(75%)보다 크되 T2×5(=100%)는 아니다.- 모든 단계에서
totalExposureLimit == positionLimit × maxOpenPositions가 성립한다(파생 관계 불변식). - 자본이 0이면 총 노출 상한도 0이다(경계값).