This commit is contained in:
薇薇安 2026-01-13 15:46:45 +08:00
parent d269538810
commit c3da195363
2 changed files with 107 additions and 23 deletions

View File

@ -54,14 +54,22 @@ class PositionManager:
await self.client.set_leverage(symbol, leverage)
# 计算仓位大小
logger.info(f"开始为 {symbol} 计算仓位大小...")
quantity = await self.risk_manager.calculate_position_size(
symbol, change_percent
)
if quantity is None:
logger.warning(f"{symbol} 仓位计算失败,跳过交易")
logger.warning(f"{symbol} 仓位计算失败,跳过交易")
logger.warning(f" 可能原因:")
logger.warning(f" 1. 账户余额不足")
logger.warning(f" 2. 单笔仓位超过限制")
logger.warning(f" 3. 总仓位超过限制")
logger.warning(f" 4. 无法获取价格数据")
return None
logger.info(f"{symbol} 仓位计算成功: {quantity:.4f}")
# 确定交易方向(优先使用技术指标信号)
if trade_direction:
side = trade_direction

View File

@ -34,17 +34,20 @@ class RiskManager:
是否通过检查
"""
try:
logger.info(f"检查 {symbol} 单笔仓位大小...")
# 获取账户余额
balance = await self.client.get_account_balance()
available_balance = balance.get('available', 0)
if available_balance <= 0:
logger.warning("账户余额不足")
logger.warning(f"{symbol} 账户可用余额不足: {available_balance:.2f} USDT")
return False
# 计算仓位价值(假设使用当前价格)
ticker = await self.client.get_ticker_24h(symbol)
if not ticker:
logger.warning(f"{symbol} 无法获取价格数据")
return False
current_price = ticker['price']
@ -53,33 +56,45 @@ class RiskManager:
# 检查单笔仓位是否超过最大限制
max_position_value = available_balance * self.config['MAX_POSITION_PERCENT']
min_position_value = available_balance * self.config['MIN_POSITION_PERCENT']
max_position_pct = self.config['MAX_POSITION_PERCENT'] * 100
min_position_pct = self.config['MIN_POSITION_PERCENT'] * 100
logger.info(f" 数量: {quantity:.4f}")
logger.info(f" 价格: {current_price:.4f} USDT")
logger.info(f" 仓位价值: {position_value:.2f} USDT")
logger.info(f" 单笔最大限制: {max_position_value:.2f} USDT ({max_position_pct:.1f}%)")
logger.info(f" 单笔最小限制: {min_position_value:.2f} USDT ({min_position_pct:.1f}%)")
if position_value > max_position_value:
logger.warning(
f"{symbol} 仓位过大: {position_value:.2f} USDT > "
f"最大限制: {max_position_value:.2f} USDT"
f"{symbol} 单笔仓位过大: {position_value:.2f} USDT > "
f"最大限制: {max_position_value:.2f} USDT "
f"(超出: {position_value - max_position_value:.2f} USDT)"
)
return False
if position_value < min_position_value:
logger.warning(
f"{symbol} 仓位过小: {position_value:.2f} USDT < "
f"{symbol} 单笔仓位过小: {position_value:.2f} USDT < "
f"最小限制: {min_position_value:.2f} USDT"
)
return False
logger.info(f"{symbol} 单笔仓位大小检查通过")
# 检查总仓位是否超过限制
logger.info(f"检查 {symbol} 总仓位限制...")
if not await self.check_total_position(position_value):
return False
logger.info(
f"{symbol} 仓位检查通过: {position_value:.2f} USDT "
f"(账户余额: {available_balance:.2f} USDT)"
f"{symbol} 所有仓位检查通过: {position_value:.2f} USDT "
f"(账户可用余额: {available_balance:.2f} USDT)"
)
return True
except Exception as e:
logger.error(f"检查仓位大小失败: {e}")
logger.error(f"检查仓位大小失败 {symbol}: {e}", exc_info=True)
return False
async def check_total_position(self, new_position_value: float) -> bool:
@ -97,38 +112,81 @@ class RiskManager:
positions = await self.client.get_open_positions()
# 计算当前总仓位价值
total_position_value = sum(
abs(pos['positionAmt'] * pos['entryPrice'])
for pos in positions
)
current_position_values = []
total_position_value = 0
for pos in positions:
position_value = abs(pos['positionAmt'] * pos['entryPrice'])
current_position_values.append({
'symbol': pos['symbol'],
'value': position_value,
'amount': pos['positionAmt'],
'entryPrice': pos['entryPrice']
})
total_position_value += position_value
# 加上新仓位
total_position_value += new_position_value
total_with_new = total_position_value + new_position_value
# 获取账户余额
balance = await self.client.get_account_balance()
total_balance = balance.get('total', 0)
available_balance = balance.get('available', 0)
if total_balance <= 0:
logger.warning("账户总余额为0无法开仓")
return False
max_total_position = total_balance * self.config['MAX_TOTAL_POSITION_PERCENT']
max_total_position_pct = self.config['MAX_TOTAL_POSITION_PERCENT'] * 100
if total_position_value > max_total_position:
# 详细日志
logger.info("=" * 60)
logger.info("总仓位检查详情:")
logger.info(f" 账户总余额: {total_balance:.2f} USDT")
logger.info(f" 账户可用余额: {available_balance:.2f} USDT")
logger.info(f" 总仓位上限: {max_total_position:.2f} USDT ({max_total_position_pct:.1f}%)")
logger.info(f" 当前持仓数量: {len(positions)}")
if current_position_values:
logger.info(" 当前持仓明细:")
for pos_info in current_position_values:
logger.info(
f" - {pos_info['symbol']}: "
f"{pos_info['value']:.2f} USDT "
f"(数量: {pos_info['amount']:.4f}, "
f"入场价: {pos_info['entryPrice']:.4f})"
)
logger.info(f" 当前总仓位: {total_position_value:.2f} USDT")
logger.info(f" 新仓位价值: {new_position_value:.2f} USDT")
logger.info(f" 开仓后总仓位: {total_with_new:.2f} USDT")
logger.info(f" 剩余可用仓位: {max_total_position - total_position_value:.2f} USDT")
if total_with_new > max_total_position:
logger.warning("=" * 60)
logger.warning(
f"总仓位超限: {total_position_value:.2f} USDT > "
f"❌ 总仓位超限: {total_with_new:.2f} USDT > "
f"最大限制: {max_total_position:.2f} USDT"
)
logger.warning(
f" 超出: {total_with_new - max_total_position:.2f} USDT "
f"({((total_with_new - max_total_position) / max_total_position * 100):.1f}%)"
)
logger.warning(" 建议: 平掉部分持仓或等待现有持仓平仓后再开新仓")
logger.warning("=" * 60)
return False
logger.info(
f"总仓位检查通过: {total_position_value:.2f} USDT / "
f"最大限制: {max_total_position:.2f} USDT"
f"✓ 总仓位检查通过: {total_with_new:.2f} USDT / "
f"最大限制: {max_total_position:.2f} USDT "
f"({(total_with_new / max_total_position * 100):.1f}%)"
)
logger.info("=" * 60)
return True
except Exception as e:
logger.error(f"检查总仓位失败: {e}")
logger.error(f"检查总仓位失败: {e}", exc_info=True)
return False
async def calculate_position_size(
@ -147,46 +205,64 @@ class RiskManager:
建议的仓位数量如果不符合条件则返回None
"""
try:
logger.info(f"开始计算 {symbol} 的仓位大小...")
# 获取账户余额
balance = await self.client.get_account_balance()
available_balance = balance.get('available', 0)
total_balance = balance.get('total', 0)
logger.info(f" 账户可用余额: {available_balance:.2f} USDT")
logger.info(f" 账户总余额: {total_balance:.2f} USDT")
if available_balance <= 0:
logger.warning("账户余额不足")
logger.warning(f"{symbol} 账户可用余额不足: {available_balance:.2f} USDT")
return None
# 获取当前价格
ticker = await self.client.get_ticker_24h(symbol)
if not ticker:
logger.warning(f"{symbol} 无法获取价格数据")
return None
current_price = ticker['price']
logger.info(f" 当前价格: {current_price:.4f} USDT")
# 根据涨跌幅调整仓位大小(涨跌幅越大,仓位可以适当增加)
base_position_percent = self.config['MAX_POSITION_PERCENT']
max_position_percent = self.config['MAX_POSITION_PERCENT']
min_position_percent = self.config['MIN_POSITION_PERCENT']
# 涨跌幅超过5%时可以适当增加仓位但不超过1.5倍)
if abs(change_percent) > 5:
position_percent = min(
base_position_percent * 1.5,
self.config['MAX_POSITION_PERCENT'] * 1.5
max_position_percent * 1.5
)
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}%")
# 计算仓位价值
position_value = available_balance * position_percent
logger.info(f" 计算仓位价值: {position_value:.2f} USDT ({position_percent*100:.1f}% of {available_balance:.2f})")
# 计算数量(考虑合约的最小数量精度)
quantity = position_value / current_price
logger.info(f" 计算数量: {quantity:.4f} (价值: {position_value:.2f} / 价格: {current_price:.4f})")
# 检查是否通过风险控制
logger.info(f" 检查仓位大小是否符合风险控制要求...")
if await self.check_position_size(symbol, quantity):
logger.info(f"{symbol} 仓位计算成功: {quantity:.4f} (价值: {position_value:.2f} USDT)")
return quantity
return None
else:
logger.warning(f"{symbol} 仓位检查未通过,无法开仓")
return None
except Exception as e:
logger.error(f"计算仓位大小失败: {e}")
logger.error(f"计算仓位大小失败 {symbol}: {e}", exc_info=True)
return None
async def should_trade(self, symbol: str, change_percent: float) -> bool: