diff --git a/src/exchange.py b/src/exchange.py index d200f32..888d25a 100644 --- a/src/exchange.py +++ b/src/exchange.py @@ -15,14 +15,12 @@ class BinanceFuturesClient: MIN_NOTIONAL = 5.0 # 바이낸스 선물 최소 명목금액 (USDT) - def calculate_quantity(self, balance: float, price: float, leverage: int) -> float: - """리스크 기반 포지션 크기 계산 (최소 명목금액 $5 보장)""" - risk_amount = balance * self.config.risk_per_trade - notional = risk_amount * leverage + def calculate_quantity(self, balance: float, price: float, leverage: int, margin_ratio: float) -> float: + """동적 증거금 비율 기반 포지션 크기 계산 (최소 명목금액 $5 보장)""" + notional = balance * margin_ratio * leverage if notional < self.MIN_NOTIONAL: notional = self.MIN_NOTIONAL quantity = notional / price - # XRP는 소수점 1자리, 단 최소 명목금액 충족 여부 재확인 qty_rounded = round(quantity, 1) if qty_rounded * price < self.MIN_NOTIONAL: qty_rounded = round(self.MIN_NOTIONAL / price + 0.05, 1) diff --git a/tests/test_exchange.py b/tests/test_exchange.py index c57a3e2..64b6345 100644 --- a/tests/test_exchange.py +++ b/tests/test_exchange.py @@ -12,11 +12,19 @@ def config(): "BINANCE_API_SECRET": "test_secret", "SYMBOL": "XRPUSDT", "LEVERAGE": "10", - "RISK_PER_TRADE": "0.02", }) return Config() +@pytest.fixture +def client(): + config = Config() + config.leverage = 10 + c = BinanceFuturesClient.__new__(BinanceFuturesClient) + c.config = config + return c + + @pytest.mark.asyncio async def test_set_leverage(config): with patch("src.exchange.Client") as MockClient: @@ -28,11 +36,21 @@ async def test_set_leverage(config): assert result is not None -def test_calculate_quantity(config): - with patch("src.exchange.Client") as MockClient: - MockClient.return_value = MagicMock() - client = BinanceFuturesClient(config) - # 잔고 1000 USDT, 리스크 2%, 레버리지 10, 가격 0.5 - qty = client.calculate_quantity(balance=1000.0, price=0.5, leverage=10) - # 1000 * 0.02 * 10 / 0.5 = 400 - assert qty == pytest.approx(400.0, rel=0.01) +def test_calculate_quantity_basic(client): + """잔고 22, 비율 50%, 레버리지 10배 → 명목금액 110, XRP 가격 2.5 → 수량 44.0""" + qty = client.calculate_quantity(balance=22.0, price=2.5, leverage=10, margin_ratio=0.50) + # 명목금액 = 22 * 0.5 * 10 = 110, 수량 = 110 / 2.5 = 44.0 + assert qty == pytest.approx(44.0, abs=0.1) + + +def test_calculate_quantity_min_notional(client): + """명목금액이 최소(5 USDT) 미만이면 최소값으로 올림""" + qty = client.calculate_quantity(balance=1.0, price=2.5, leverage=1, margin_ratio=0.01) + # 명목금액 = 1 * 0.01 * 1 = 0.01 < 5 → 최소 5 USDT + assert qty * 2.5 >= 5.0 + + +def test_calculate_quantity_zero_balance(client): + """잔고 0이면 최소 명목금액 기반 수량 반환""" + qty = client.calculate_quantity(balance=0.0, price=2.5, leverage=10, margin_ratio=0.50) + assert qty > 0