"""주문 실행 디스패처 — 국내/해외 공통 인터페이스. trade_executor.py 의 is_domestic 분기를 여기서 캡슐화. 신규 시장(예: 일본, 홍콩) 추가 시 이 모듈만 수정. """ from __future__ import annotations import logging from typing import Protocol from app.services.korea_investment_api import korea_investment_api logger = logging.getLogger(__name__) class OrderDispatcher(Protocol): """주문 실행 인터페이스.""" async def place( self, stock_code: str, order_type: str, quantity: int, price: float, ) -> dict: ... async def get_execution(self, order_id: str) -> dict: ... def adjust_price(self, price: float, order_type: str) -> float: ... class DomesticOrderDispatcher: """국내 주식 주문 실행.""" async def place( self, stock_code: str, order_type: str, quantity: int, price: float, **_, ) -> dict: return await korea_investment_api.place_order(stock_code, order_type, quantity, price) async def get_execution(self, order_id: str) -> dict: return await korea_investment_api.get_order_execution(order_id, is_domestic=True) def adjust_price(self, price: float, order_type: str) -> float: """BUY +0.5% / SELL -0.5%, 원화 정수 반환.""" adjusted = price * 1.005 if order_type == "BUY" else price * 0.995 return float(int(adjusted)) class OverseasOrderDispatcher: """해외 주식 주문 실행.""" def __init__(self, exchange_code: str = "NASD"): self.exchange_code = exchange_code async def place( self, stock_code: str, order_type: str, quantity: int, price: float, **_, ) -> dict: return await korea_investment_api.place_overseas_order( stock_code, order_type, quantity, float(price), self.exchange_code ) async def get_execution(self, order_id: str) -> dict: # 해외 체결 조회 미지원 — 포트폴리오 동기화로 대체 return {"filled_qty": 0, "filled_price": 0.0} def adjust_price(self, price: float, order_type: str) -> float: """BUY +0.5% / SELL -0.5%, USD 소수점 2자리 반환.""" adjusted = price * 1.005 if order_type == "BUY" else price * 0.995 return round(adjusted, 2) def get_dispatcher(is_domestic: bool, exchange_code: str = "NASD") -> DomesticOrderDispatcher | OverseasOrderDispatcher: """is_domestic 에 따라 적절한 디스패처 반환.""" if is_domestic: return DomesticOrderDispatcher() return OverseasOrderDispatcher(exchange_code=exchange_code)