a
This commit is contained in:
parent
32a0bf3b3a
commit
4d0ef0f76c
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user