This commit is contained in:
薇薇安 2026-01-16 16:37:43 +08:00
parent 32a0bf3b3a
commit 4d0ef0f76c

View File

@ -333,19 +333,18 @@ async def close_position(symbol: str):
# 导入必要的模块
try:
from binance_client import BinanceClient
from risk_manager import RiskManager
from position_manager import PositionManager
logger.info("✓ 成功导入交易系统模块")
except ImportError as import_error:
logger.warning(f"首次导入失败: {import_error}尝试从trading_system路径导入")
trading_system_path = project_root / 'trading_system'
sys.path.insert(0, str(trading_system_path))
from binance_client import BinanceClient
from risk_manager import RiskManager
from position_manager import PositionManager
logger.info("✓ 从trading_system路径导入成功")
# 创建客户端和仓位管理器
# 导入数据库模型
from database.models import Trade
# 创建客户端
logger.info(f"创建BinanceClient (testnet={use_testnet})...")
client = BinanceClient(
api_key=api_key,
@ -358,49 +357,136 @@ async def close_position(symbol: str):
logger.info("✓ 币安API连接成功")
try:
logger.info("初始化RiskManager和PositionManager...")
risk_manager = RiskManager(client)
position_manager = PositionManager(client, risk_manager)
logger.info("✓ 管理器初始化成功")
# 先检查币安是否有持仓
# 检查币安是否有持仓
logger.info(f"检查 {symbol} 在币安的持仓状态...")
positions = await client.get_open_positions()
binance_has_position = any(p['symbol'] == symbol and float(p['positionAmt']) != 0 for p in positions)
position = next((p for p in positions if p['symbol'] == symbol and float(p['positionAmt']) != 0), None)
if binance_has_position:
position_info = next((p for p in positions if p['symbol'] == symbol and float(p['positionAmt']) != 0), None)
position_amt = float(position_info['positionAmt']) if position_info else 0
logger.info(f"✓ 币安账户中有 {symbol} 持仓: {position_amt:.4f}")
else:
logger.info(f"✓ 币安账户中没有 {symbol} 持仓")
# 执行平仓reason='manual' 表示手动平仓)
logger.info(f"开始执行平仓操作: {symbol}...")
success = await position_manager.close_position(symbol, reason='manual')
if success:
logger.info(f"{symbol} 平仓成功")
if not position:
logger.warning(f"{symbol} 币安账户中没有持仓,可能已被平仓")
# 检查数据库中是否有未平仓的记录,如果有则更新
open_trades = Trade.get_by_symbol(symbol, status='open')
if open_trades:
trade = open_trades[0]
# 获取当前价格作为平仓价格
ticker = await client.get_ticker_24h(symbol)
exit_price = float(ticker['price']) if ticker else float(trade['entry_price'])
# 计算盈亏
entry_price = float(trade['entry_price'])
quantity = float(trade['quantity'])
if trade['side'] == 'BUY':
pnl = (exit_price - entry_price) * quantity
pnl_percent = ((exit_price - entry_price) / entry_price) * 100
else:
pnl = (entry_price - exit_price) * quantity
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
# 更新数据库
Trade.update_exit(
trade_id=trade['id'],
exit_price=exit_price,
exit_reason='manual',
pnl=pnl,
pnl_percent=pnl_percent,
exit_order_id=None
)
logger.info(f"✓ 已更新数据库记录(币安无持仓但数据库有记录)")
return {
"message": f"{symbol} 平仓成功",
"message": f"{symbol} 平仓操作完成(币安账户中没有持仓,可能已被平仓)",
"symbol": symbol,
"status": "closed"
}
else:
# 根据币安是否有持仓来判断失败原因
if binance_has_position:
# 币安有持仓但下单失败
error_msg = f"{symbol} 平仓失败:币安账户中有持仓,但下单失败,请检查日志或稍后重试"
logger.error(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
# 获取持仓信息
position_amt = float(position['positionAmt'])
logger.info(f"✓ 币安账户中有 {symbol} 持仓: {position_amt:.4f}")
# 确定平仓方向(与持仓相反)
side = 'SELL' if position_amt > 0 else 'BUY'
quantity = abs(position_amt)
logger.info(f"开始执行平仓操作: {symbol} {side} {quantity:.4f} @ MARKET...")
# 直接调用 BinanceClient 平仓(使用 reduceOnly=True
order = await client.place_order(
symbol=symbol,
side=side,
quantity=quantity,
order_type='MARKET',
reduce_only=True
)
if not order:
error_msg = f"{symbol} 平仓失败:下单返回 None请检查日志"
logger.error(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
order_id = order.get('orderId')
logger.info(f"{symbol} 平仓订单已提交 (订单ID: {order_id})")
# 等待订单成交,获取实际成交价格
import asyncio
await asyncio.sleep(1)
# 获取订单详情
exit_price = None
try:
order_info = await client.client.futures_get_order(symbol=symbol, orderId=order_id)
if order_info:
exit_price = float(order_info.get('avgPrice', 0)) or float(order_info.get('price', 0))
if exit_price <= 0 and order_info.get('fills'):
# 计算加权平均成交价格
total_qty = 0
total_value = 0
for fill in order_info.get('fills', []):
qty = float(fill.get('qty', 0))
price = float(fill.get('price', 0))
total_qty += qty
total_value += qty * price
if total_qty > 0:
exit_price = total_value / total_qty
except Exception as e:
logger.warning(f"获取订单详情失败: {e},使用当前价格")
# 如果无法获取订单价格,使用当前价格
if not exit_price or exit_price <= 0:
ticker = await client.get_ticker_24h(symbol)
exit_price = float(ticker['price']) if ticker else float(position.get('entryPrice', 0))
# 更新数据库记录
open_trades = Trade.get_by_symbol(symbol, status='open')
if open_trades:
trade = open_trades[0]
entry_price = float(trade['entry_price'])
trade_quantity = float(trade['quantity'])
# 计算盈亏
if trade['side'] == 'BUY':
pnl = (exit_price - entry_price) * trade_quantity
pnl_percent = ((exit_price - entry_price) / entry_price) * 100
else:
# 币安没有持仓,可能是数据库已更新
logger.warning(f"{symbol} 币安账户中没有持仓,可能已被平仓或数据库已更新")
return {
"message": f"{symbol} 平仓操作完成(币安账户中没有持仓,可能已被平仓)",
"symbol": symbol,
"status": "closed"
}
pnl = (entry_price - exit_price) * trade_quantity
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
# 更新数据库
Trade.update_exit(
trade_id=trade['id'],
exit_price=exit_price,
exit_reason='manual',
pnl=pnl,
pnl_percent=pnl_percent,
exit_order_id=order_id
)
logger.info(f"✓ 已更新数据库记录 (盈亏: {pnl:.2f} USDT, {pnl_percent:.2f}%)")
logger.info(f"{symbol} 平仓成功")
return {
"message": f"{symbol} 平仓成功",
"symbol": symbol,
"status": "closed"
}
finally:
logger.info("断开币安API连接...")
await client.disconnect()