a
This commit is contained in:
parent
f737c32ea2
commit
bb1490b909
|
|
@ -276,7 +276,13 @@ async def sync_trades_from_binance(
|
|||
|
||||
try:
|
||||
if reduce_only:
|
||||
# 这是平仓订单,更新数据库中的对应记录
|
||||
# 这是平仓订单
|
||||
# 首先检查是否已经通过订单号同步过(避免重复)
|
||||
existing_trade = Trade.get_by_exit_order_id(order_id)
|
||||
if existing_trade:
|
||||
logger.debug(f"订单 {order_id} 已同步过,跳过")
|
||||
continue
|
||||
|
||||
# 查找数据库中该交易对的open状态记录
|
||||
open_trades = Trade.get_by_symbol(symbol, status='open')
|
||||
if open_trades:
|
||||
|
|
@ -298,30 +304,36 @@ async def sync_trades_from_binance(
|
|||
pnl = (entry_price - avg_price) * actual_quantity
|
||||
pnl_percent = ((entry_price - avg_price) / entry_price) * 100
|
||||
|
||||
# 更新数据库
|
||||
# 更新数据库(包含订单号)
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=avg_price,
|
||||
exit_reason='sync',
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=order_id # 保存订单号,确保唯一性
|
||||
)
|
||||
updated_count += 1
|
||||
logger.debug(f"✓ 更新平仓记录: {symbol} (ID: {trade_id}, 成交价: {avg_price:.4f})")
|
||||
logger.debug(f"✓ 更新平仓记录: {symbol} (ID: {trade_id}, 订单号: {order_id}, 成交价: {avg_price:.4f})")
|
||||
else:
|
||||
# 这是开仓订单,检查数据库中是否已存在
|
||||
# 这里可以添加逻辑来创建缺失的开仓记录
|
||||
# 但为了简化,暂时只处理平仓订单
|
||||
pass
|
||||
# 这是开仓订单,检查数据库中是否已存在(通过订单号)
|
||||
existing_trade = Trade.get_by_entry_order_id(order_id)
|
||||
if not existing_trade:
|
||||
# 如果不存在,可以创建新记录(但需要更多信息,暂时跳过)
|
||||
logger.debug(f"发现新的开仓订单 {order_id},但缺少必要信息,跳过创建")
|
||||
else:
|
||||
logger.debug(f"开仓订单 {order_id} 已存在,跳过")
|
||||
except Exception as e:
|
||||
logger.warning(f"同步订单失败 {symbol} (订单ID: {order_id}): {e}")
|
||||
continue
|
||||
|
||||
result = {
|
||||
"success": True,
|
||||
"message": f"同步完成:更新了 {updated_count} 条平仓记录",
|
||||
"message": f"同步完成:更新了 {updated_count} 条平仓记录(基于订单号匹配,确保唯一性)",
|
||||
"total_orders": len(all_orders),
|
||||
"updated_trades": updated_count
|
||||
"updated_trades": updated_count,
|
||||
"close_orders": len([o for o in all_orders if o.get('reduceOnly', False)]),
|
||||
"open_orders": len([o for o in all_orders if not o.get('reduceOnly', False)])
|
||||
}
|
||||
|
||||
logger.info(f"✓ 同步完成:处理了 {len(all_orders)} 个订单,更新了 {updated_count} 条记录")
|
||||
|
|
|
|||
16
backend/database/add_order_ids.sql
Normal file
16
backend/database/add_order_ids.sql
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
-- 添加币安订单号字段,用于与币安订单同步和对账
|
||||
|
||||
USE `auto_trade_sys`;
|
||||
|
||||
-- 添加开仓订单号字段(币安开仓订单ID)
|
||||
ALTER TABLE `trades`
|
||||
ADD COLUMN `entry_order_id` BIGINT UNIQUE COMMENT '币安开仓订单号(唯一)' AFTER `leverage`;
|
||||
|
||||
-- 添加平仓订单号字段(币安平仓订单ID)
|
||||
ALTER TABLE `trades`
|
||||
ADD COLUMN `exit_order_id` BIGINT UNIQUE COMMENT '币安平仓订单号(唯一)' AFTER `entry_order_id`;
|
||||
|
||||
-- 添加索引以便快速查询
|
||||
ALTER TABLE `trades`
|
||||
ADD INDEX `idx_entry_order_id` (`entry_order_id`),
|
||||
ADD INDEX `idx_exit_order_id` (`exit_order_id`);
|
||||
|
|
@ -88,27 +88,63 @@ class Trade:
|
|||
"""交易记录模型"""
|
||||
|
||||
@staticmethod
|
||||
def create(symbol, side, quantity, entry_price, leverage=10, entry_reason=None):
|
||||
"""创建交易记录(使用北京时间)"""
|
||||
def create(symbol, side, quantity, entry_price, leverage=10, entry_reason=None, entry_order_id=None):
|
||||
"""创建交易记录(使用北京时间)
|
||||
|
||||
Args:
|
||||
symbol: 交易对
|
||||
side: 方向
|
||||
quantity: 数量
|
||||
entry_price: 入场价
|
||||
leverage: 杠杆
|
||||
entry_reason: 入场原因
|
||||
entry_order_id: 币安开仓订单号(可选,用于对账)
|
||||
"""
|
||||
entry_time = get_beijing_time()
|
||||
db.execute_update(
|
||||
"""INSERT INTO trades
|
||||
(symbol, side, quantity, entry_price, leverage, entry_reason, status, entry_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, 'open', %s)""",
|
||||
(symbol, side, quantity, entry_price, leverage, entry_reason, entry_time)
|
||||
(symbol, side, quantity, entry_price, leverage, entry_reason, status, entry_time, entry_order_id)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, 'open', %s, %s)""",
|
||||
(symbol, side, quantity, entry_price, leverage, entry_reason, entry_time, entry_order_id)
|
||||
)
|
||||
return db.execute_one("SELECT LAST_INSERT_ID() as id")['id']
|
||||
|
||||
@staticmethod
|
||||
def update_exit(trade_id, exit_price, exit_reason, pnl, pnl_percent):
|
||||
"""更新平仓信息(使用北京时间)"""
|
||||
def update_exit(trade_id, exit_price, exit_reason, pnl, pnl_percent, exit_order_id=None):
|
||||
"""更新平仓信息(使用北京时间)
|
||||
|
||||
Args:
|
||||
trade_id: 交易记录ID
|
||||
exit_price: 出场价
|
||||
exit_reason: 平仓原因
|
||||
pnl: 盈亏
|
||||
pnl_percent: 盈亏百分比
|
||||
exit_order_id: 币安平仓订单号(可选,用于对账)
|
||||
"""
|
||||
exit_time = get_beijing_time()
|
||||
db.execute_update(
|
||||
"""UPDATE trades
|
||||
SET exit_price = %s, exit_time = %s,
|
||||
exit_reason = %s, pnl = %s, pnl_percent = %s, status = 'closed'
|
||||
exit_reason = %s, pnl = %s, pnl_percent = %s, status = 'closed',
|
||||
exit_order_id = %s
|
||||
WHERE id = %s""",
|
||||
(exit_price, exit_time, exit_reason, pnl, pnl_percent, trade_id)
|
||||
(exit_price, exit_time, exit_reason, pnl, pnl_percent, exit_order_id, trade_id)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_by_entry_order_id(entry_order_id):
|
||||
"""根据开仓订单号获取交易记录"""
|
||||
return db.execute_one(
|
||||
"SELECT * FROM trades WHERE entry_order_id = %s",
|
||||
(entry_order_id,)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_by_exit_order_id(exit_order_id):
|
||||
"""根据平仓订单号获取交易记录"""
|
||||
return db.execute_one(
|
||||
"SELECT * FROM trades WHERE exit_order_id = %s",
|
||||
(exit_order_id,)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -365,6 +365,12 @@
|
|||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.order-id {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.trade-card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
|||
|
|
@ -250,6 +250,8 @@ const TradeList = () => {
|
|||
<th>盈亏比例</th>
|
||||
<th>状态</th>
|
||||
<th>平仓类型</th>
|
||||
<th>开仓订单号</th>
|
||||
<th>平仓订单号</th>
|
||||
<th>入场时间</th>
|
||||
<th>平仓时间</th>
|
||||
</tr>
|
||||
|
|
@ -304,6 +306,8 @@ const TradeList = () => {
|
|||
<span className={`status ${trade.status}`}>{trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}</span>
|
||||
</td>
|
||||
<td>{trade.exit_reason_display || '-'}</td>
|
||||
<td className="order-id">{trade.entry_order_id || '-'}</td>
|
||||
<td className="order-id">{trade.exit_order_id || '-'}</td>
|
||||
<td>{formatTime(trade.entry_time)}</td>
|
||||
<td>{trade.exit_time ? formatTime(trade.exit_time) : '-'}</td>
|
||||
</tr>
|
||||
|
|
@ -385,8 +389,20 @@ const TradeList = () => {
|
|||
<span className="trade-card-value">{trade.exit_reason_display}</span>
|
||||
</div>
|
||||
)}
|
||||
{trade.entry_order_id && (
|
||||
<div className="trade-card-field">
|
||||
<span className="trade-card-label">开仓订单号</span>
|
||||
<span className="trade-card-value order-id">{trade.entry_order_id}</span>
|
||||
</div>
|
||||
)}
|
||||
{trade.exit_order_id && (
|
||||
<div className="trade-card-field">
|
||||
<span className="trade-card-label">平仓订单号</span>
|
||||
<span className="trade-card-value order-id">{trade.exit_order_id}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="trade-card-footer">
|
||||
<div className="trade-card-footer">
|
||||
<div className="trade-time-item">
|
||||
<span className="time-label">入场:</span>
|
||||
<span>{formatTime(trade.entry_time)}</span>
|
||||
|
|
|
|||
|
|
@ -179,6 +179,11 @@ class PositionManager:
|
|||
)
|
||||
|
||||
if order:
|
||||
# 获取开仓订单号
|
||||
entry_order_id = order.get('orderId')
|
||||
if entry_order_id:
|
||||
logger.info(f"{symbol} [开仓] 币安订单号: {entry_order_id}")
|
||||
|
||||
# 记录到数据库
|
||||
trade_id = None
|
||||
if DB_AVAILABLE and Trade:
|
||||
|
|
@ -190,9 +195,10 @@ class PositionManager:
|
|||
quantity=quantity,
|
||||
entry_price=entry_price,
|
||||
leverage=leverage,
|
||||
entry_reason=entry_reason
|
||||
entry_reason=entry_reason,
|
||||
entry_order_id=entry_order_id # 保存币安订单号
|
||||
)
|
||||
logger.info(f"✓ {symbol} 交易记录已保存到数据库 (ID: {trade_id})")
|
||||
logger.info(f"✓ {symbol} 交易记录已保存到数据库 (ID: {trade_id}, 订单号: {entry_order_id})")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 保存交易记录到数据库失败: {e}")
|
||||
logger.error(f" 错误类型: {type(e).__name__}")
|
||||
|
|
@ -299,12 +305,14 @@ class PositionManager:
|
|||
pnl = (entry_price - exit_price) * quantity
|
||||
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
|
||||
|
||||
# 同步平仓时没有订单号,设为None
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price,
|
||||
exit_reason=reason,
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=None # 同步平仓时没有订单号
|
||||
)
|
||||
logger.info(f"{symbol} [平仓] ✓ 数据库状态已更新")
|
||||
except Exception as e:
|
||||
|
|
@ -415,12 +423,18 @@ class PositionManager:
|
|||
pnl = (entry_price - exit_price_float) * quantity_float
|
||||
pnl_percent = ((entry_price - exit_price_float) / entry_price) * 100
|
||||
|
||||
# 获取平仓订单号
|
||||
exit_order_id = order.get('orderId')
|
||||
if exit_order_id:
|
||||
logger.info(f"{symbol} [平仓] 币安订单号: {exit_order_id}")
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price_float,
|
||||
exit_reason=reason,
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=exit_order_id # 保存币安平仓订单号
|
||||
)
|
||||
logger.info(
|
||||
f"{symbol} [平仓] ✓ 数据库记录已更新 "
|
||||
|
|
@ -916,12 +930,20 @@ class PositionManager:
|
|||
pnl = (entry_price - exit_price) * quantity
|
||||
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
|
||||
|
||||
# 从历史订单中获取平仓订单号
|
||||
exit_order_id = None
|
||||
if close_orders:
|
||||
exit_order_id = close_orders[0].get('orderId')
|
||||
if exit_order_id:
|
||||
logger.info(f"{symbol} [状态同步] 找到平仓订单号: {exit_order_id}")
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price,
|
||||
exit_reason='sync', # 标记为同步更新
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=exit_order_id # 保存币安平仓订单号
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user