This commit is contained in:
薇薇安 2026-02-14 18:18:07 +08:00
parent 16cf4f2157
commit a52b8c4738
3 changed files with 67 additions and 3 deletions

View File

@ -1750,17 +1750,66 @@ async def sync_positions(account_id: int = Depends(get_account_id)):
else:
logger.info("✓ 数据库与币安状态一致,无需更新")
# 4. 检查币安有但数据库没有记录的持仓
# 4. 币安有仓但数据库无记录:从币安成交里取 orderId 并补建交易记录,便于在「订单记录」和统计中展示(含系统挂单/条件单)
missing_in_db = binance_symbols - db_open_symbols
recovered_count = 0
if missing_in_db:
logger.info(f"发现 {len(missing_in_db)} 个持仓在币安存在但数据库中没有记录: {', '.join(missing_in_db)}")
logger.info(" 这些持仓可能是手动开仓的,建议手动处理")
for symbol in missing_in_db:
try:
pos = next((p for p in binance_positions if p.get('symbol') == symbol), None)
if not pos or float(pos.get('positionAmt', 0)) == 0:
continue
position_amt = float(pos['positionAmt'])
quantity = abs(position_amt)
side = 'BUY' if position_amt > 0 else 'SELL'
entry_price = float(pos.get('entryPrice', 0))
leverage = int(pos.get('leverage', 10)) or 10
notional = quantity * entry_price
if notional < 1.0:
continue
entry_order_id = None
try:
trades = await client.get_recent_trades(symbol, limit=30)
if trades:
same_side = [t for t in trades if str(t.get('side', '')).upper() == side]
if same_side:
same_side.sort(key=lambda x: int(x.get('time', 0)), reverse=True)
entry_order_id = same_side[0].get('orderId')
except Exception as e:
logger.debug(f"获取 {symbol} 成交记录失败: {e}")
if entry_order_id and hasattr(Trade, 'get_by_entry_order_id'):
try:
existing = Trade.get_by_entry_order_id(entry_order_id)
if existing:
continue
except Exception:
pass
trade_id = Trade.create(
symbol=symbol,
side=side,
quantity=quantity,
entry_price=entry_price,
leverage=leverage,
entry_reason='sync_recovered',
entry_order_id=entry_order_id,
notional_usdt=notional,
margin_usdt=(notional / leverage) if leverage > 0 else None,
account_id=account_id,
)
recovered_count += 1
logger.info(f"{symbol} 已补建交易记录 (ID: {trade_id}, orderId: {entry_order_id or '-'})")
except Exception as e:
logger.warning(f"{symbol} 补建记录失败: {e}")
if recovered_count > 0:
logger.info(f"共补建 {recovered_count} 条交易记录,将在订单记录与统计中展示")
result = {
"message": "持仓状态同步完成",
"binance_positions": len(binance_symbols),
"db_open_positions": len(db_open_symbols),
"updated_to_closed": updated_count,
"recovered_count": recovered_count,
"missing_in_binance": list(missing_in_binance),
"missing_in_db": list(missing_in_db)
}

View File

@ -234,7 +234,10 @@ const StatsDashboard = () => {
if (result.updated_to_closed > 0) {
message += `,已更新 ${result.updated_to_closed} 条记录为已平仓`
}
if (result.missing_in_db && result.missing_in_db.length > 0) {
if (result.recovered_count > 0) {
message += `,已为 ${result.recovered_count} 个持仓补建交易记录(将出现在订单记录与统计中)`
}
if (result.missing_in_db && result.missing_in_db.length > 0 && (result.recovered_count || 0) === 0) {
message += `,发现 ${result.missing_in_db.length} 个币安持仓在数据库中没有记录`
}

View File

@ -2818,6 +2818,17 @@ class PositionManager:
f"{symbol} [状态同步] 检测到手动开仓,创建数据库记录... "
f"({side} {quantity:.4f} @ {entry_price:.4f})"
)
# 尽量从币安成交取 entry_order_id便于订单记录/统计对账
entry_order_id = None
try:
recent = await self.client.get_recent_trades(symbol, limit=30)
if recent:
same_side = [t for t in recent if str(t.get('side', '')).upper() == side]
if same_side:
same_side.sort(key=lambda x: int(x.get('time', 0)), reverse=True)
entry_order_id = same_side[0].get('orderId')
except Exception:
pass
# 创建数据库记录(显式传入 account_id避免多账号混用
trade_id = Trade.create(
symbol=symbol,
@ -2826,6 +2837,7 @@ class PositionManager:
entry_price=entry_price,
leverage=binance_position.get('leverage', 10),
entry_reason='manual_entry', # 标记为手动开仓
entry_order_id=entry_order_id,
notional_usdt=notional,
margin_usdt=(notional / float(binance_position.get('leverage', 10) or 10)) if float(binance_position.get('leverage', 10) or 0) > 0 else None,
account_id=self.account_id,