feat: replace risk_per_trade with margin_ratio in calculate_quantity
Made-with: Cursor
This commit is contained in:
@@ -15,14 +15,12 @@ class BinanceFuturesClient:
|
|||||||
|
|
||||||
MIN_NOTIONAL = 5.0 # 바이낸스 선물 최소 명목금액 (USDT)
|
MIN_NOTIONAL = 5.0 # 바이낸스 선물 최소 명목금액 (USDT)
|
||||||
|
|
||||||
def calculate_quantity(self, balance: float, price: float, leverage: int) -> float:
|
def calculate_quantity(self, balance: float, price: float, leverage: int, margin_ratio: float) -> float:
|
||||||
"""리스크 기반 포지션 크기 계산 (최소 명목금액 $5 보장)"""
|
"""동적 증거금 비율 기반 포지션 크기 계산 (최소 명목금액 $5 보장)"""
|
||||||
risk_amount = balance * self.config.risk_per_trade
|
notional = balance * margin_ratio * leverage
|
||||||
notional = risk_amount * leverage
|
|
||||||
if notional < self.MIN_NOTIONAL:
|
if notional < self.MIN_NOTIONAL:
|
||||||
notional = self.MIN_NOTIONAL
|
notional = self.MIN_NOTIONAL
|
||||||
quantity = notional / price
|
quantity = notional / price
|
||||||
# XRP는 소수점 1자리, 단 최소 명목금액 충족 여부 재확인
|
|
||||||
qty_rounded = round(quantity, 1)
|
qty_rounded = round(quantity, 1)
|
||||||
if qty_rounded * price < self.MIN_NOTIONAL:
|
if qty_rounded * price < self.MIN_NOTIONAL:
|
||||||
qty_rounded = round(self.MIN_NOTIONAL / price + 0.05, 1)
|
qty_rounded = round(self.MIN_NOTIONAL / price + 0.05, 1)
|
||||||
|
|||||||
@@ -12,11 +12,19 @@ def config():
|
|||||||
"BINANCE_API_SECRET": "test_secret",
|
"BINANCE_API_SECRET": "test_secret",
|
||||||
"SYMBOL": "XRPUSDT",
|
"SYMBOL": "XRPUSDT",
|
||||||
"LEVERAGE": "10",
|
"LEVERAGE": "10",
|
||||||
"RISK_PER_TRADE": "0.02",
|
|
||||||
})
|
})
|
||||||
return Config()
|
return Config()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client():
|
||||||
|
config = Config()
|
||||||
|
config.leverage = 10
|
||||||
|
c = BinanceFuturesClient.__new__(BinanceFuturesClient)
|
||||||
|
c.config = config
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_leverage(config):
|
async def test_set_leverage(config):
|
||||||
with patch("src.exchange.Client") as MockClient:
|
with patch("src.exchange.Client") as MockClient:
|
||||||
@@ -28,11 +36,21 @@ async def test_set_leverage(config):
|
|||||||
assert result is not None
|
assert result is not None
|
||||||
|
|
||||||
|
|
||||||
def test_calculate_quantity(config):
|
def test_calculate_quantity_basic(client):
|
||||||
with patch("src.exchange.Client") as MockClient:
|
"""잔고 22, 비율 50%, 레버리지 10배 → 명목금액 110, XRP 가격 2.5 → 수량 44.0"""
|
||||||
MockClient.return_value = MagicMock()
|
qty = client.calculate_quantity(balance=22.0, price=2.5, leverage=10, margin_ratio=0.50)
|
||||||
client = BinanceFuturesClient(config)
|
# 명목금액 = 22 * 0.5 * 10 = 110, 수량 = 110 / 2.5 = 44.0
|
||||||
# 잔고 1000 USDT, 리스크 2%, 레버리지 10, 가격 0.5
|
assert qty == pytest.approx(44.0, abs=0.1)
|
||||||
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_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
|
||||||
|
|||||||
Reference in New Issue
Block a user