This commit is contained in:
薇薇安 2026-02-25 23:07:14 +08:00
parent f3ce4d5d11
commit ac022bd62a
2 changed files with 47 additions and 47 deletions

View File

@ -758,9 +758,9 @@ async def fetch_realtime_positions(account_id: int):
matched = None
for db_trade in db_trades:
try:
if abs(float(db_trade.get('entry_price', 0)) - entry_price) < 0.01:
if abs(float(db_trade.get('entry_price', 0)) - entry_price) < 0.01:
matched = db_trade
break
break
except Exception:
continue
if matched is None:
@ -938,8 +938,8 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
# 兼容旧逻辑:如果原始接口异常,回退到封装方法
if not nonzero_positions:
try:
positions = await client.get_open_positions()
position = next((p for p in positions if p['symbol'] == symbol and float(p['positionAmt']) != 0), None)
positions = await client.get_open_positions()
position = next((p for p in positions if p['symbol'] == symbol and float(p['positionAmt']) != 0), None)
if position:
nonzero_positions = [(float(position["positionAmt"]), {"positionAmt": position["positionAmt"]})]
except Exception:
@ -1013,7 +1013,7 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
if dual_side is None:
if any(isinstance(p, dict) and (p.get("positionSide") in ("LONG", "SHORT")) for _, p in nonzero_positions):
dual_side = True
else:
else:
dual_side = False
logger.info(f"{symbol} 持仓模式: {'HEDGE(对冲)' if dual_side else 'ONE-WAY(单向)'}")
@ -1067,13 +1067,13 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
oid = order.get("orderId")
if oid:
order_ids.append(oid)
except Exception as order_error:
error_msg = f"{symbol} 平仓失败:下单异常 - {str(order_error)}"
logger.error(error_msg)
logger.error(f" 错误类型: {type(order_error).__name__}")
import traceback
logger.error(f" 完整错误堆栈:\n{traceback.format_exc()}")
raise HTTPException(status_code=500, detail=error_msg)
except Exception as order_error:
error_msg = f"{symbol} 平仓失败:下单异常 - {str(order_error)}"
logger.error(error_msg)
logger.error(f" 错误类型: {type(order_error).__name__}")
import traceback
logger.error(f" 完整错误堆栈:\n{traceback.format_exc()}")
raise HTTPException(status_code=500, detail=error_msg)
if not orders:
raise HTTPException(status_code=400, detail=f"{symbol} 无可平仓的有效仓位数量调整后为0或无持仓")
@ -1103,21 +1103,21 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
try:
# 1. 获取价格
order_info = await client.client.futures_get_order(symbol=symbol, orderId=oid)
if order_info:
if order_info:
p = float(order_info.get('avgPrice', 0)) or float(order_info.get('price', 0))
if p <= 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:
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:
p = total_value / total_qty
if p > 0:
exit_prices[oid] = p
# 2. 计算佣金和实际盈亏(从 recent_trades 匹配)
related_trades = [t for t in recent_trades if str(t.get('orderId')) == str(oid)]
if related_trades:
@ -1128,11 +1128,11 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
total_realized_pnl += float(t.get('realizedPnl', 0))
total_commission += float(t.get('commission', 0))
commission_assets.add(t.get('commissionAsset'))
exit_realized_pnls[oid] = total_realized_pnl
exit_commissions[oid] = total_commission
exit_commission_assets[oid] = "/".join(commission_assets) if commission_assets else None
except Exception as e:
except Exception as e:
logger.warning(f"获取订单详情失败 (orderId={oid}): {e}")
# 兜底:如果无法获取订单价格,使用当前价格
@ -1150,8 +1150,8 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
used_order_ids = set()
for trade in open_trades:
try:
entry_price = float(trade['entry_price'])
trade_quantity = float(trade['quantity'])
entry_price = float(trade['entry_price'])
trade_quantity = float(trade['quantity'])
except Exception:
continue
@ -1171,24 +1171,24 @@ async def close_position(symbol: str, account_id: int = Depends(get_account_id))
exit_price = fallback_exit_price or entry_price
# 计算盈亏(数据库侧依旧按名义盈亏;收益率展示用保证金口径在前端/统计里另算)
if trade['side'] == 'BUY':
pnl = (exit_price - entry_price) * trade_quantity
pnl_percent = ((exit_price - entry_price) / entry_price) * 100
else:
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,
if trade['side'] == 'BUY':
pnl = (exit_price - entry_price) * trade_quantity
pnl_percent = ((exit_price - entry_price) / entry_price) * 100
else:
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=chosen_oid,
realized_pnl=exit_realized_pnls.get(chosen_oid),
commission=exit_commissions.get(chosen_oid),
commission_asset=exit_commission_assets.get(chosen_oid)
)
)
logger.info(f"✓ 已更新数据库记录 trade_id={trade['id']} order_id={chosen_oid} (盈亏: {pnl:.2f} USDT, {pnl_percent:.2f}%)")
logger.info(f"{symbol} 平仓成功")
@ -1839,21 +1839,21 @@ async def sync_positions(
pass
if not exit_price or exit_price <= 0:
ticker = await client.get_ticker_24h(symbol)
exit_price = float(ticker['price']) if ticker else entry_price
ticker = await client.get_ticker_24h(symbol)
exit_price = float(ticker['price']) if ticker else entry_price
# 计算盈亏
if trade['side'] == 'BUY':
pnl = (exit_price - entry_price) * quantity
else:
pnl = (entry_price - exit_price) * quantity
# 计算基于保证金的盈亏百分比
leverage = float(trade.get('leverage', 10))
entry_value = entry_price * quantity
margin = entry_value / leverage if leverage > 0 else entry_value
pnl_percent_margin = (pnl / margin * 100) if margin > 0 else 0
# 从币安成交获取手续费与实际盈亏,保证统计与币安一致
sync_commission = None
sync_commission_asset = None

View File

@ -571,8 +571,8 @@ async def sync_trades_from_binance(
try:
# 这是平仓订单close_orders 已经筛选出 reduceOnly=True 的订单)
# 首先检查是否已经通过订单号同步过(避免重复)
existing_trade = Trade.get_by_exit_order_id(order_id)
# 首先检查是否已经通过订单号同步过(避免重复)
existing_trade = Trade.get_by_exit_order_id(order_id)
# 如果已有 exit_order_id 且 exit_reason 不是 sync说明已完整同步跳过
if existing_trade and existing_trade.get("exit_order_id") and existing_trade.get("exit_reason") not in (None, "", "sync"):
skipped_existing += 1