1
This commit is contained in:
parent
f3ce4d5d11
commit
ac022bd62a
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user