This commit is contained in:
aaron 2026-04-28 15:48:42 +08:00
parent b531ed4055
commit f684cf746a
4 changed files with 42 additions and 0 deletions

View File

@ -66,10 +66,12 @@ class CryptoAgent:
'UNI': 5, # 1 UNI/张 ≈ $50
},
'max_margin_pct': 0.25, # 单笔最大25%(支持超激进配置)
'min_position_value_balance_ratio': 1.0, # 合约单最低名义仓位 = 1x 账户权益
},
'PaperTrading': {
'min_margin': {}, # 无最小限制
'max_margin_pct': 0.25, # 单笔最大25%(与实盘一致)
'min_position_value_balance_ratio': 1.0,
}
}
@ -3639,6 +3641,7 @@ class CryptoAgent:
symbol = signal.get('symbol', '').replace('USDT', '').upper()
min_margin = min_margin_rules.get(symbol, 0)
min_position_value_balance_ratio = float(rules.get('min_position_value_balance_ratio', 0) or 0)
current_leverage = account.get('current_total_leverage', 0)
max_leverage = account.get('max_total_leverage', 10)
order_leverage = account.get('order_leverage', 10)
@ -3679,6 +3682,7 @@ class CryptoAgent:
setup_margin_pct_cap = setup_profile.get('max_margin_pct_cap')
effective_max_margin_pct = min(max_margin_pct, setup_margin_pct_cap) if isinstance(setup_margin_pct_cap, (int, float)) else max_margin_pct
min_position_value = balance * min_position_value_balance_ratio if min_position_value_balance_ratio > 0 else 0.0
margin, _, budget_reason = calculate_margin_and_position_value(
balance=balance,
@ -3689,6 +3693,7 @@ class CryptoAgent:
target_margin_pct=target_margin_pct,
max_margin_pct=effective_max_margin_pct,
min_margin=min_margin,
min_position_value=min_position_value,
min_effective_leverage=min_effective_leverage,
)

View File

@ -440,6 +440,7 @@ class PaperTradingService:
order_leverage=self.leverage,
target_margin_pct=target_margin_pct,
max_margin_pct=0.25,
min_position_value=balance,
)
if margin <= 0:

View File

@ -115,6 +115,7 @@ def calculate_margin_and_position_value(
target_margin_pct: float,
max_margin_pct: float,
min_margin: float = 0.0,
min_position_value: float = 0.0,
min_effective_leverage: float = 1.0,
reserve_ratio: float = 0.05,
) -> Tuple[float, float, str]:
@ -152,16 +153,31 @@ def calculate_margin_and_position_value(
target_margin = balance * target_margin_pct
margin = min(target_margin, hard_cap)
adjustments = []
if min_margin > 0 and margin < min_margin:
if min_margin <= hard_cap:
margin = min_margin
adjustments.append(f"按最小保证金抬升至 ${min_margin:.2f}")
else:
return 0.0, 0.0, (
f"最小保证金 ${min_margin:.2f} 超过当前可用额度 "
f"${hard_cap:.2f}"
)
if min_position_value > 0:
required_margin_for_min_position = min_position_value / order_leverage
if margin < required_margin_for_min_position:
if required_margin_for_min_position <= hard_cap:
margin = required_margin_for_min_position
adjustments.append(
f"按最低名义仓位 ${min_position_value:.2f} 抬升保证金至 ${margin:.2f}"
)
else:
adjustments.append(
f"最低名义仓位 ${min_position_value:.2f} 受当前上限约束,实际最多 ${hard_cap * order_leverage:.2f}"
)
position_value = margin * order_leverage
if position_value < 50:
return 0.0, 0.0, "可开仓位不足 $50"
@ -172,5 +188,7 @@ def calculate_margin_and_position_value(
f"目标保证金 ${target_margin:.2f}, 实际保证金 ${margin:.2f}, "
f"名义仓位 ${position_value:.2f}, 单笔杠杆 {order_leverage:.1f}x"
)
if adjustments:
detail = f"{detail}, 调整: {'; '.join(adjustments)}"
logger.info(f"仓位预算计算: {detail}")
return margin, position_value, detail

View File

@ -165,3 +165,21 @@ def test_setup_profile_can_cap_margin_budget_more_conservatively():
assert margin == pytest.approx(1320.0)
assert position_value == pytest.approx(13200.0)
def test_min_position_value_floor_can_raise_too_small_contract_position():
module = load_position_sizing_module()
margin, position_value, _ = module.calculate_margin_and_position_value(
balance=1400,
available_margin=1400,
current_total_leverage=0,
max_total_leverage=10,
order_leverage=10,
target_margin_pct=0.028, # 原本只会开到约 392U 名义仓位
max_margin_pct=0.25,
min_position_value=1400,
)
assert margin == pytest.approx(140.0)
assert position_value == pytest.approx(1400.0)