1
This commit is contained in:
parent
16cf4f2157
commit
a52b8c4738
|
|
@ -1750,17 +1750,66 @@ async def sync_positions(account_id: int = Depends(get_account_id)):
|
||||||
else:
|
else:
|
||||||
logger.info("✓ 数据库与币安状态一致,无需更新")
|
logger.info("✓ 数据库与币安状态一致,无需更新")
|
||||||
|
|
||||||
# 4. 检查币安有但数据库没有记录的持仓
|
# 4. 币安有仓但数据库无记录:从币安成交里取 orderId 并补建交易记录,便于在「订单记录」和统计中展示(含系统挂单/条件单)
|
||||||
missing_in_db = binance_symbols - db_open_symbols
|
missing_in_db = binance_symbols - db_open_symbols
|
||||||
|
recovered_count = 0
|
||||||
if missing_in_db:
|
if missing_in_db:
|
||||||
logger.info(f"发现 {len(missing_in_db)} 个持仓在币安存在但数据库中没有记录: {', '.join(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 = {
|
result = {
|
||||||
"message": "持仓状态同步完成",
|
"message": "持仓状态同步完成",
|
||||||
"binance_positions": len(binance_symbols),
|
"binance_positions": len(binance_symbols),
|
||||||
"db_open_positions": len(db_open_symbols),
|
"db_open_positions": len(db_open_symbols),
|
||||||
"updated_to_closed": updated_count,
|
"updated_to_closed": updated_count,
|
||||||
|
"recovered_count": recovered_count,
|
||||||
"missing_in_binance": list(missing_in_binance),
|
"missing_in_binance": list(missing_in_binance),
|
||||||
"missing_in_db": list(missing_in_db)
|
"missing_in_db": list(missing_in_db)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,10 @@ const StatsDashboard = () => {
|
||||||
if (result.updated_to_closed > 0) {
|
if (result.updated_to_closed > 0) {
|
||||||
message += `,已更新 ${result.updated_to_closed} 条记录为已平仓`
|
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} 个币安持仓在数据库中没有记录`
|
message += `,发现 ${result.missing_in_db.length} 个币安持仓在数据库中没有记录`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2818,6 +2818,17 @@ class PositionManager:
|
||||||
f"{symbol} [状态同步] 检测到手动开仓,创建数据库记录... "
|
f"{symbol} [状态同步] 检测到手动开仓,创建数据库记录... "
|
||||||
f"({side} {quantity:.4f} @ {entry_price:.4f})"
|
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,避免多账号混用)
|
# 创建数据库记录(显式传入 account_id,避免多账号混用)
|
||||||
trade_id = Trade.create(
|
trade_id = Trade.create(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
|
|
@ -2826,6 +2837,7 @@ class PositionManager:
|
||||||
entry_price=entry_price,
|
entry_price=entry_price,
|
||||||
leverage=binance_position.get('leverage', 10),
|
leverage=binance_position.get('leverage', 10),
|
||||||
entry_reason='manual_entry', # 标记为手动开仓
|
entry_reason='manual_entry', # 标记为手动开仓
|
||||||
|
entry_order_id=entry_order_id,
|
||||||
notional_usdt=notional,
|
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,
|
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,
|
account_id=self.account_id,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user