a
This commit is contained in:
parent
ae953d119e
commit
2ba8d69ee0
|
|
@ -1149,6 +1149,11 @@ class PositionManager:
|
||||||
current_price=current_price,
|
current_price=current_price,
|
||||||
working_type="MARK_PRICE",
|
working_type="MARK_PRICE",
|
||||||
)
|
)
|
||||||
|
if sl_order:
|
||||||
|
logger.info(f"{symbol} ✓ 止损单已成功挂到交易所: {sl_order.get('algoId', 'N/A')}")
|
||||||
|
else:
|
||||||
|
logger.error(f"{symbol} ❌ 止损单挂单失败!将依赖WebSocket监控,但可能无法及时止损")
|
||||||
|
|
||||||
tp_order = await self.client.place_trigger_close_position_order(
|
tp_order = await self.client.place_trigger_close_position_order(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
position_direction=side,
|
position_direction=side,
|
||||||
|
|
@ -1157,6 +1162,10 @@ class PositionManager:
|
||||||
current_price=current_price,
|
current_price=current_price,
|
||||||
working_type="MARK_PRICE",
|
working_type="MARK_PRICE",
|
||||||
)
|
)
|
||||||
|
if tp_order:
|
||||||
|
logger.info(f"{symbol} ✓ 止盈单已成功挂到交易所: {tp_order.get('algoId', 'N/A')}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"{symbol} ⚠️ 止盈单挂单失败,将依赖WebSocket监控")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Algo 接口返回 algoId
|
# Algo 接口返回 algoId
|
||||||
|
|
|
||||||
|
|
@ -285,63 +285,133 @@ class RiskManager:
|
||||||
if not actual_leverage or actual_leverage <= 0:
|
if not actual_leverage or actual_leverage <= 0:
|
||||||
actual_leverage = 10
|
actual_leverage = 10
|
||||||
|
|
||||||
# 根据涨跌幅调整仓位大小(涨跌幅越大,保证金占比可以适当增加)
|
# ⚠️ 优化3:固定风险百分比仓位计算(凯利公式)
|
||||||
base_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT']
|
# 公式:仓位大小 = (总资金 * 每笔单子承受的风险%) / (入场价 - 止损价)
|
||||||
max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT']
|
use_fixed_risk = config.TRADING_CONFIG.get('USE_FIXED_RISK_SIZING', True)
|
||||||
min_position_percent = config.TRADING_CONFIG['MIN_POSITION_PERCENT']
|
fixed_risk_percent = config.TRADING_CONFIG.get('FIXED_RISK_PERCENT', 0.02) # 默认2%
|
||||||
|
quantity = None
|
||||||
|
|
||||||
# 涨跌幅超过5%时,可以适当增加保证金占比,但必须遵守 MAX_POSITION_PERCENT 上限
|
if use_fixed_risk and entry_price and side:
|
||||||
if abs(change_percent) > 5:
|
# 如果未提供止损价格,先估算
|
||||||
position_percent = min(
|
if stop_loss_price is None:
|
||||||
base_position_percent * 1.5,
|
# 尝试使用ATR估算止损距离
|
||||||
max_position_percent
|
if atr and atr > 0:
|
||||||
)
|
atr_multiplier = config.TRADING_CONFIG.get('ATR_STOP_LOSS_MULTIPLIER', 1.8)
|
||||||
logger.info(f" 涨跌幅 {change_percent:.2f}% > 5%,使用增强仓位比例: {position_percent*100:.1f}%")
|
if side == 'BUY':
|
||||||
else:
|
estimated_stop_loss = entry_price - (atr * atr_multiplier)
|
||||||
position_percent = base_position_percent
|
else: # SELL
|
||||||
logger.info(f" 涨跌幅 {change_percent:.2f}%,使用标准仓位比例: {position_percent*100:.1f}%")
|
estimated_stop_loss = entry_price + (atr * atr_multiplier)
|
||||||
|
stop_loss_price = estimated_stop_loss
|
||||||
|
else:
|
||||||
|
# 使用固定百分比估算(基于保证金)
|
||||||
|
stop_loss_pct = config.TRADING_CONFIG.get('STOP_LOSS_PERCENT', 0.10)
|
||||||
|
# 先估算一个临时仓位来计算止损距离
|
||||||
|
temp_margin = total_balance * 0.05 # 临时使用5%保证金
|
||||||
|
temp_notional = temp_margin * actual_leverage
|
||||||
|
temp_quantity = temp_notional / entry_price
|
||||||
|
stop_loss_amount = temp_margin * stop_loss_pct
|
||||||
|
if side == 'BUY':
|
||||||
|
estimated_stop_loss = entry_price - (stop_loss_amount / temp_quantity)
|
||||||
|
else: # SELL
|
||||||
|
estimated_stop_loss = entry_price + (stop_loss_amount / temp_quantity)
|
||||||
|
stop_loss_price = estimated_stop_loss
|
||||||
|
|
||||||
|
# 计算止损距离
|
||||||
|
if side == 'BUY':
|
||||||
|
stop_distance = entry_price - stop_loss_price
|
||||||
|
else: # SELL
|
||||||
|
stop_distance = stop_loss_price - entry_price
|
||||||
|
|
||||||
|
if stop_distance > 0:
|
||||||
|
# 固定风险金额
|
||||||
|
risk_amount = total_balance * fixed_risk_percent
|
||||||
|
|
||||||
|
# 根据止损距离反算仓位
|
||||||
|
# 风险金额 = (入场价 - 止损价) × 数量
|
||||||
|
# 所以:数量 = 风险金额 / (入场价 - 止损价)
|
||||||
|
quantity = risk_amount / stop_distance
|
||||||
|
|
||||||
|
# 计算对应的保证金和名义价值
|
||||||
|
notional_value = quantity * entry_price
|
||||||
|
margin_value = notional_value / actual_leverage
|
||||||
|
|
||||||
|
logger.info(f" ⚠️ 使用固定风险百分比计算仓位:")
|
||||||
|
logger.info(f" 固定风险: {fixed_risk_percent*100:.2f}% = {risk_amount:.4f} USDT")
|
||||||
|
logger.info(f" 止损距离: {stop_distance:.4f} USDT ({stop_distance/entry_price*100:.2f}%)")
|
||||||
|
logger.info(f" 计算数量: {quantity:.4f}")
|
||||||
|
logger.info(f" 名义价值: {notional_value:.2f} USDT")
|
||||||
|
logger.info(f" 保证金: {margin_value:.4f} USDT ({margin_value/total_balance*100:.2f}%)")
|
||||||
|
|
||||||
|
# 检查是否超过最大仓位限制
|
||||||
|
max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT']
|
||||||
|
max_margin_value = available_balance * max_position_percent
|
||||||
|
|
||||||
|
if margin_value > max_margin_value:
|
||||||
|
# 如果超过最大仓位,使用最大仓位
|
||||||
|
logger.warning(f" ⚠️ 固定风险计算的保证金 {margin_value:.4f} USDT > 最大限制 {max_margin_value:.2f} USDT")
|
||||||
|
logger.info(f" ✓ 调整为最大仓位限制: {max_margin_value:.2f} USDT")
|
||||||
|
margin_value = max_margin_value
|
||||||
|
notional_value = margin_value * actual_leverage
|
||||||
|
quantity = notional_value / entry_price
|
||||||
|
else:
|
||||||
|
# 使用固定风险计算的仓位
|
||||||
|
pass # quantity已经计算好了
|
||||||
|
|
||||||
# 计算保证金与名义价值
|
# 如果未使用固定风险计算,使用原来的方法
|
||||||
margin_value = available_balance * position_percent
|
if quantity is None:
|
||||||
notional_value = margin_value * actual_leverage
|
# ⚠️ 优化3:信号强度分级 - 9-10分高权重,8分轻仓
|
||||||
logger.info(f" 计算保证金: {margin_value:.4f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})")
|
signal_multiplier = 1.0
|
||||||
logger.info(f" 计算名义价值: {notional_value:.2f} USDT (保证金 {margin_value:.4f} × 杠杆 {actual_leverage}x)")
|
if signal_strength is not None:
|
||||||
|
signal_multipliers = config.TRADING_CONFIG.get('SIGNAL_STRENGTH_POSITION_MULTIPLIER', {8: 0.5, 9: 1.0, 10: 1.0})
|
||||||
|
signal_multiplier = signal_multipliers.get(signal_strength, 1.0)
|
||||||
|
if signal_strength == 8:
|
||||||
|
logger.info(f" ⚠️ 信号强度8分,使用50%仓位(轻仓试探)")
|
||||||
|
elif signal_strength >= 9:
|
||||||
|
logger.info(f" ✓ 信号强度{signal_strength}分,使用100%仓位(高质量信号)")
|
||||||
|
|
||||||
|
# 根据涨跌幅调整仓位大小(涨跌幅越大,保证金占比可以适当增加)
|
||||||
|
base_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] * signal_multiplier
|
||||||
|
max_position_percent = config.TRADING_CONFIG['MAX_POSITION_PERCENT'] * signal_multiplier
|
||||||
|
min_position_percent = config.TRADING_CONFIG['MIN_POSITION_PERCENT']
|
||||||
|
|
||||||
|
# 涨跌幅超过5%时,可以适当增加保证金占比,但必须遵守 MAX_POSITION_PERCENT 上限
|
||||||
|
if abs(change_percent) > 5:
|
||||||
|
position_percent = min(
|
||||||
|
base_position_percent * 1.5,
|
||||||
|
max_position_percent
|
||||||
|
)
|
||||||
|
logger.info(f" 涨跌幅 {change_percent:.2f}% > 5%,使用增强仓位比例: {position_percent*100:.1f}%")
|
||||||
|
else:
|
||||||
|
position_percent = base_position_percent
|
||||||
|
logger.info(f" 涨跌幅 {change_percent:.2f}%,使用标准仓位比例: {position_percent*100:.1f}%")
|
||||||
|
|
||||||
|
# 计算保证金与名义价值
|
||||||
|
margin_value = available_balance * position_percent
|
||||||
|
notional_value = margin_value * actual_leverage
|
||||||
|
logger.info(f" 计算保证金: {margin_value:.4f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})")
|
||||||
|
logger.info(f" 计算名义价值: {notional_value:.2f} USDT (保证金 {margin_value:.4f} × 杠杆 {actual_leverage}x)")
|
||||||
|
|
||||||
|
# 计算数量
|
||||||
|
quantity = notional_value / current_price
|
||||||
|
|
||||||
|
logger.info(f" 计算数量: {quantity:.4f}")
|
||||||
|
|
||||||
|
# 计算名义价值和保证金(如果还未计算)
|
||||||
|
if 'notional_value' not in locals() or 'margin_value' not in locals():
|
||||||
|
notional_value = quantity * current_price
|
||||||
|
margin_value = notional_value / actual_leverage
|
||||||
|
|
||||||
# 确保仓位价值满足最小名义价值要求(币安要求至少5 USDT)
|
# 确保仓位价值满足最小名义价值要求(币安要求至少5 USDT)
|
||||||
min_notional = 5.0 # 币安合约最小名义价值
|
min_notional = 5.0 # 币安合约最小名义价值
|
||||||
if notional_value < min_notional:
|
calculated_notional = quantity * current_price
|
||||||
logger.warning(f" ⚠ 名义价值 {notional_value:.2f} USDT < 最小名义价值 {min_notional:.2f} USDT")
|
if calculated_notional < min_notional:
|
||||||
|
# 如果计算出的名义价值仍然不足,增加数量
|
||||||
# 计算需要的最小保证金来满足最小名义价值:margin >= min_notional / leverage
|
required_quantity = min_notional / current_price
|
||||||
required_margin = min_notional / actual_leverage
|
logger.warning(f" ⚠ 计算出的名义价值 {calculated_notional:.2f} USDT < {min_notional:.2f} USDT")
|
||||||
required_margin_percent = required_margin / available_balance
|
logger.info(f" ✓ 调整数量从 {quantity:.4f} 到 {required_quantity:.4f}")
|
||||||
logger.info(
|
quantity = required_quantity
|
||||||
f" 需要的最小保证金: {required_margin:.4f} USDT "
|
notional_value = required_quantity * current_price
|
||||||
f"(占比 {required_margin_percent*100:.2f}%)"
|
margin_value = notional_value / actual_leverage
|
||||||
)
|
|
||||||
|
|
||||||
# 检查是否可以使用更大的保证金占比(但不超过最大保证金限制)
|
|
||||||
if required_margin_percent <= max_position_percent:
|
|
||||||
position_percent = required_margin_percent
|
|
||||||
margin_value = required_margin
|
|
||||||
notional_value = min_notional
|
|
||||||
logger.info(f" ✓ 调整保证金占比到 {position_percent*100:.2f}% 以满足最小名义价值: {notional_value:.2f} USDT")
|
|
||||||
else:
|
|
||||||
# 即使使用最大仓位比例也无法满足最小名义价值
|
|
||||||
max_allowed_margin = available_balance * max_position_percent
|
|
||||||
max_allowed_notional = max_allowed_margin * actual_leverage
|
|
||||||
logger.warning(
|
|
||||||
f" ❌ 无法满足最小名义价值要求: "
|
|
||||||
f"需要 {min_notional:.2f} USDT (需要保证金 {required_margin:.4f} USDT),"
|
|
||||||
f"但最大允许名义 {max_allowed_notional:.2f} USDT (最大保证金 {max_allowed_margin:.2f} USDT)"
|
|
||||||
)
|
|
||||||
logger.warning(f" 💡 建议: 提高 MAX_POSITION_PERCENT 或降低杠杆/更换币种,确保最小名义价值可满足")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# quantity 应该已经计算好了(固定风险或传统方法)
|
|
||||||
if quantity is None:
|
|
||||||
logger.error(f" ❌ {symbol} 仓位计算失败:quantity为None")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 计算名义价值和保证金(如果还未计算)
|
# 计算名义价值和保证金(如果还未计算)
|
||||||
if 'notional_value' not in locals() or 'margin_value' not in locals():
|
if 'notional_value' not in locals() or 'margin_value' not in locals():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user