"""매수·매도 체결 로직 (paper / live 공통 사이징).""" from __future__ import annotations from dataclasses import dataclass from typing import Any from deepcoin.ground_truth.order_sizing import max_buy_from_cash @dataclass class TradeResult: """단일 체결 결과.""" executed: bool side: str order_krw: float order_coin: float fee_krw: float price: float skip_reason: str = "" api_response: dict[str, Any] | None = None def to_dict(self) -> dict[str, Any]: """JSON 직렬화 dict.""" return { "executed": self.executed, "side": self.side, "order_krw": round(self.order_krw, 0), "order_coin": round(self.order_coin, 8), "fee_krw": round(self.fee_krw, 2), "price": self.price, "skip_reason": self.skip_reason, "api_response": self.api_response, } def compute_buy_order( *, cash_krw: float, coin_qty: float, price: float, fee_rate: float, min_order_krw: float, cluster_size: int = 1, ) -> TradeResult: """매수 주문 금액·수량을 계산한다.""" cash = max(float(cash_krw), 0.0) equity = cash + float(coin_qty) * price cash_cap = max_buy_from_cash(equity, cash) per_buy = cash / cluster_size if cluster_size > 0 else cash order_krw = min(per_buy, cash, cash_cap) if order_krw < min_order_krw: return TradeResult( executed=False, side="buy", order_krw=0.0, order_coin=0.0, fee_krw=0.0, price=price, skip_reason="원화 부족 또는 최소 주문 미만", ) fee = order_krw * fee_rate bought = (order_krw - fee) / price return TradeResult( executed=True, side="buy", order_krw=order_krw, order_coin=bought, fee_krw=fee, price=price, ) def compute_sell_order( *, coin_qty: float, price: float, fee_rate: float, min_order_krw: float, cluster_size: int = 1, ) -> TradeResult: """매도 주문 수량을 계산한다.""" qty = max(float(coin_qty), 0.0) per_sell = qty / cluster_size if cluster_size > 0 else qty order_coin = per_sell order_krw = order_coin * price if order_coin <= 0 or order_krw < min_order_krw: return TradeResult( executed=False, side="sell", order_krw=0.0, order_coin=0.0, fee_krw=0.0, price=price, skip_reason="코인 부족 또는 최소 주문 미만", ) fee = order_krw * fee_rate return TradeResult( executed=True, side="sell", order_krw=order_krw, order_coin=order_coin, fee_krw=fee, price=price, ) def apply_trade_to_portfolio( portfolio: dict[str, Any], trade: TradeResult, ) -> None: """체결 결과를 포트폴리오에 반영한다.""" cash = float(portfolio.get("cash_krw", 0)) coin = float(portfolio.get("coin_qty", 0)) if not trade.executed: return if trade.side == "buy": portfolio["cash_krw"] = cash - trade.order_krw portfolio["coin_qty"] = coin + trade.order_coin else: portfolio["cash_krw"] = cash + trade.order_krw - trade.fee_krw portfolio["coin_qty"] = coin - trade.order_coin