[BE-06] TradingCostModel·PositionSizer

작업 내용 (설계 의도)

변경 사항

거래비용과 포지션 사이징을 값 객체로 만든다. 둘 다 순수 계산이라 외부 의존이 없다.

  • TradingCostModelml/app/cost_model.pyCostModel동일 공식을 Kotlin으로 재구현한다. 매수는 슬리피지로 체결가가 오르고 수수료가 가산되며, 매도는 체결가가 내리고 수수료+거래세가 차감된다. 근거: ADR-004.
  • PositionSizersizeFor(capital, tier, marketPrice) → 매수 주수. 1주 가격이 종목당 상한을 넘으면 0을 반환한다(PRD FR-36). 100만원·T1 기준 KRX 117종목 중 63종목이 여기 해당한다.

진짜 위험은 코드 중복이 아니라 파라미터 드리프트다. 수수료율·거래세율·슬리피지율 3개를 설정으로 단일화하고, ml과 동일 입력·동일 출력을 검증하는 교차 테스트(ml/tests/test_cost_model_parity.py)를 함께 만든다.

의존

  • BE-01 (도메인 계약 — CostModelParityFixture 소비)

다이어그램

처리 흐름

해당 없음 — 순수 함수 계산이며 협력 객체 간 흐름이 없다.

클래스 의존

flowchart LR
    Sizer[PositionSizer] --> Tier[RiskTier]
    Cost[TradingCostModel] --> Fill[FillPrice 값객체]
    Parity[교차검증 테스트] --> Cost
    Parity --> Py[ml/app/cost_model.py]

테스트 케이스

  • 매수 시 실효단가가 의도가보다 높다(슬리피지 + 수수료 가산).
  • 매도 시 실효단가가 의도가보다 낮다(슬리피지 + 수수료 + 거래세 차감).
  • 수량 0이면 비용이 0으로 산출된다(경계값).
  • ml/app/cost_model.py와 동일 입력에서 동일 출력을 낸다(교차 검증, 공통 픽스처 사용).
  • 자본 100만원·T1(10%)·주가 9만원이면 1주를 산출한다.
  • 자본 100만원·T1(10%)·주가 26만원이면 0주를 반환한다(1주 미달 제외).
  • 자본 100만원·T3(20%)·주가 19만원이면 1주를 산출한다(단계별 차이).