From fe9690698a92affe1d840574f1b2553b00de3902 Mon Sep 17 00:00:00 2001 From: 21in7 Date: Sun, 1 Mar 2026 20:37:46 +0900 Subject: [PATCH] feat: add get_dynamic_margin_ratio to RiskManager Made-with: Cursor --- src/risk_manager.py | 11 +++++++++ tests/test_risk_manager.py | 49 +++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/risk_manager.py b/src/risk_manager.py index d907ed1..480bb34 100644 --- a/src/risk_manager.py +++ b/src/risk_manager.py @@ -34,3 +34,14 @@ class RiskManager: """매일 자정 초기화""" self.daily_pnl = 0.0 logger.info("일일 PnL 초기화") + + def set_base_balance(self, balance: float) -> None: + """봇 시작 시 기준 잔고 설정 (동적 비율 계산 기준점)""" + self.initial_balance = balance + + def get_dynamic_margin_ratio(self, balance: float) -> float: + """잔고에 따라 선형 감소하는 증거금 비율 반환""" + ratio = self.config.margin_max_ratio - ( + (balance - self.initial_balance) * self.config.margin_decay_rate + ) + return max(self.config.margin_min_ratio, min(self.config.margin_max_ratio, ratio)) diff --git a/tests/test_risk_manager.py b/tests/test_risk_manager.py index ae61196..0aa06be 100644 --- a/tests/test_risk_manager.py +++ b/tests/test_risk_manager.py @@ -11,7 +11,6 @@ def config(): "BINANCE_API_SECRET": "s", "SYMBOL": "XRPUSDT", "LEVERAGE": "10", - "RISK_PER_TRADE": "0.02", }) return Config() @@ -34,3 +33,51 @@ def test_position_size_capped(config): rm = RiskManager(config, max_daily_loss_pct=0.05) rm.open_positions = ["pos1", "pos2", "pos3"] assert rm.can_open_new_position() is False + + +# --- 동적 증거금 비율 테스트 --- + +@pytest.fixture +def dynamic_config(): + c = Config() + c.margin_max_ratio = 0.50 + c.margin_min_ratio = 0.20 + c.margin_decay_rate = 0.0006 + return c + + +@pytest.fixture +def risk(dynamic_config): + r = RiskManager(dynamic_config) + r.set_base_balance(22.0) + return r + + +def test_set_base_balance(risk): + assert risk.initial_balance == 22.0 + + +def test_ratio_at_base_balance(risk): + """기준 잔고에서 최대 비율(50%) 반환""" + ratio = risk.get_dynamic_margin_ratio(22.0) + assert ratio == pytest.approx(0.50, abs=1e-6) + + +def test_ratio_decreases_as_balance_grows(risk): + """잔고가 늘수록 비율 감소""" + ratio_100 = risk.get_dynamic_margin_ratio(100.0) + ratio_300 = risk.get_dynamic_margin_ratio(300.0) + assert ratio_100 < 0.50 + assert ratio_300 < ratio_100 + + +def test_ratio_clamped_at_min(risk): + """잔고가 매우 커도 최소 비율(20%) 이하로 내려가지 않음""" + ratio = risk.get_dynamic_margin_ratio(10000.0) + assert ratio == pytest.approx(0.20, abs=1e-6) + + +def test_ratio_clamped_at_max(risk): + """잔고가 기준보다 작아도 최대 비율(50%) 초과하지 않음""" + ratio = risk.get_dynamic_margin_ratio(5.0) + assert ratio == pytest.approx(0.50, abs=1e-6)