a
This commit is contained in:
parent
32a0bf3b3a
commit
4d0ef0f76c
|
|
@ -333,19 +333,18 @@ async def close_position(symbol: str):
|
||||||
# 导入必要的模块
|
# 导入必要的模块
|
||||||
try:
|
try:
|
||||||
from binance_client import BinanceClient
|
from binance_client import BinanceClient
|
||||||
from risk_manager import RiskManager
|
|
||||||
from position_manager import PositionManager
|
|
||||||
logger.info("✓ 成功导入交易系统模块")
|
logger.info("✓ 成功导入交易系统模块")
|
||||||
except ImportError as import_error:
|
except ImportError as import_error:
|
||||||
logger.warning(f"首次导入失败: {import_error},尝试从trading_system路径导入")
|
logger.warning(f"首次导入失败: {import_error},尝试从trading_system路径导入")
|
||||||
trading_system_path = project_root / 'trading_system'
|
trading_system_path = project_root / 'trading_system'
|
||||||
sys.path.insert(0, str(trading_system_path))
|
sys.path.insert(0, str(trading_system_path))
|
||||||
from binance_client import BinanceClient
|
from binance_client import BinanceClient
|
||||||
from risk_manager import RiskManager
|
|
||||||
from position_manager import PositionManager
|
|
||||||
logger.info("✓ 从trading_system路径导入成功")
|
logger.info("✓ 从trading_system路径导入成功")
|
||||||
|
|
||||||
# 创建客户端和仓位管理器
|
# 导入数据库模型
|
||||||
|
from database.models import Trade
|
||||||
|
|
||||||
|
# 创建客户端
|
||||||
logger.info(f"创建BinanceClient (testnet={use_testnet})...")
|
logger.info(f"创建BinanceClient (testnet={use_testnet})...")
|
||||||
client = BinanceClient(
|
client = BinanceClient(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
|
|
@ -358,49 +357,136 @@ async def close_position(symbol: str):
|
||||||
logger.info("✓ 币安API连接成功")
|
logger.info("✓ 币安API连接成功")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info("初始化RiskManager和PositionManager...")
|
# 检查币安是否有持仓
|
||||||
risk_manager = RiskManager(client)
|
|
||||||
position_manager = PositionManager(client, risk_manager)
|
|
||||||
logger.info("✓ 管理器初始化成功")
|
|
||||||
|
|
||||||
# 先检查币安是否有持仓
|
|
||||||
logger.info(f"检查 {symbol} 在币安的持仓状态...")
|
logger.info(f"检查 {symbol} 在币安的持仓状态...")
|
||||||
positions = await client.get_open_positions()
|
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:
|
if not position:
|
||||||
position_info = next((p for p in positions if p['symbol'] == symbol and float(p['positionAmt']) != 0), None)
|
logger.warning(f"⚠ {symbol} 币安账户中没有持仓,可能已被平仓")
|
||||||
position_amt = float(position_info['positionAmt']) if position_info else 0
|
# 检查数据库中是否有未平仓的记录,如果有则更新
|
||||||
logger.info(f"✓ 币安账户中有 {symbol} 持仓: {position_amt:.4f}")
|
open_trades = Trade.get_by_symbol(symbol, status='open')
|
||||||
else:
|
if open_trades:
|
||||||
logger.info(f"✓ 币安账户中没有 {symbol} 持仓")
|
trade = open_trades[0]
|
||||||
|
# 获取当前价格作为平仓价格
|
||||||
|
ticker = await client.get_ticker_24h(symbol)
|
||||||
|
exit_price = float(ticker['price']) if ticker else float(trade['entry_price'])
|
||||||
|
|
||||||
# 执行平仓(reason='manual' 表示手动平仓)
|
# 计算盈亏
|
||||||
logger.info(f"开始执行平仓操作: {symbol}...")
|
entry_price = float(trade['entry_price'])
|
||||||
success = await position_manager.close_position(symbol, reason='manual')
|
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"✓ 已更新数据库记录(币安无持仓但数据库有记录)")
|
||||||
|
|
||||||
if success:
|
|
||||||
logger.info(f"✓ {symbol} 平仓成功")
|
|
||||||
return {
|
return {
|
||||||
"message": f"{symbol} 平仓成功",
|
"message": f"{symbol} 平仓操作完成(币安账户中没有持仓,可能已被平仓)",
|
||||||
"symbol": symbol,
|
"symbol": symbol,
|
||||||
"status": "closed"
|
"status": "closed"
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
# 根据币安是否有持仓来判断失败原因
|
# 获取持仓信息
|
||||||
if binance_has_position:
|
position_amt = float(position['positionAmt'])
|
||||||
# 币安有持仓但下单失败
|
logger.info(f"✓ 币安账户中有 {symbol} 持仓: {position_amt:.4f}")
|
||||||
error_msg = f"{symbol} 平仓失败:币安账户中有持仓,但下单失败,请检查日志或稍后重试"
|
|
||||||
logger.error(error_msg)
|
# 确定平仓方向(与持仓相反)
|
||||||
raise HTTPException(status_code=500, detail=error_msg)
|
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:
|
else:
|
||||||
# 币安没有持仓,可能是数据库已更新
|
pnl = (entry_price - exit_price) * trade_quantity
|
||||||
logger.warning(f"⚠ {symbol} 币安账户中没有持仓,可能已被平仓或数据库已更新")
|
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
|
||||||
return {
|
|
||||||
"message": f"{symbol} 平仓操作完成(币安账户中没有持仓,可能已被平仓)",
|
# 更新数据库
|
||||||
"symbol": symbol,
|
Trade.update_exit(
|
||||||
"status": "closed"
|
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:
|
finally:
|
||||||
logger.info("断开币安API连接...")
|
logger.info("断开币安API连接...")
|
||||||
await client.disconnect()
|
await client.disconnect()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user