添加 client_order_id 支持,确保在交易记录中与币安自定义订单号一致

This commit is contained in:
薇薇安 2026-02-14 19:24:27 +08:00
parent c53c5fc64a
commit 11cd55ff7b
6 changed files with 32 additions and 0 deletions

View File

@ -1821,6 +1821,7 @@ async def sync_positions(
entry_order_id = same_side[0].get('orderId')
except Exception as e:
logger.debug(f"获取 {symbol} 成交记录失败: {e}")
client_order_id = None
if system_order_prefix:
if not entry_order_id:
logger.debug(f" {symbol} 无法获取开仓订单号,跳过补建")
@ -1828,6 +1829,7 @@ async def sync_positions(
try:
order_info = await client.client.futures_get_order(symbol=symbol, orderId=int(entry_order_id), recvWindow=20000)
cid = (order_info or {}).get("clientOrderId") or ""
client_order_id = cid or None
if not cid.startswith(system_order_prefix):
logger.debug(f" {symbol} 开仓订单 clientOrderId={cid!r} 非系统前缀,跳过补建")
continue
@ -1868,6 +1870,7 @@ async def sync_positions(
leverage=leverage,
entry_reason='sync_recovered',
entry_order_id=entry_order_id,
client_order_id=client_order_id,
notional_usdt=notional,
margin_usdt=(notional / leverage) if leverage > 0 else None,
account_id=account_id,

View File

@ -0,0 +1,5 @@
-- 为 trades 表增加「自定义订单号」字段,用于存储币安 clientOrderId便于在订单记录中核对系统单
-- 若已存在该列可跳过本句
ALTER TABLE trades ADD COLUMN client_order_id VARCHAR(64) NULL COMMENT '币安自定义订单号 clientOrderId系统单格式: 前缀_时间戳_随机' AFTER entry_order_id;
-- 可选:为按自定义订单号查询建索引(若已存在可跳过)
-- CREATE INDEX idx_client_order_id ON trades (client_order_id);

View File

@ -440,6 +440,7 @@ class Trade:
leverage=10,
entry_reason=None,
entry_order_id=None,
client_order_id=None,
stop_loss_price=None,
take_profit_price=None,
take_profit_1=None,
@ -460,6 +461,7 @@ class Trade:
leverage: 杠杆
entry_reason: 入场原因简短文本
entry_order_id: 币安开仓订单号可选用于对账
client_order_id: 币安自定义订单号 clientOrderId可选用于在订单记录中核对系统单
stop_loss_price: 实际使用的止损价格考虑了ATR等动态计算
take_profit_price: 实际使用的止盈价格考虑了ATR等动态计算
take_profit_1: 第一目标止盈价可选
@ -503,6 +505,10 @@ class Trade:
columns.append("entry_order_id")
values.append(entry_order_id)
if _has_column("client_order_id"):
columns.append("client_order_id")
values.append(client_order_id)
if _has_column("notional_usdt"):
columns.append("notional_usdt")
values.append(notional_usdt)

View File

@ -433,6 +433,10 @@ const GlobalConfig = () => {
ATR_STOP_LOSS_MULTIPLIER: { value: 3.0, type: 'number', category: 'risk', description: 'ATR 止损倍数2026-02-123 减少噪音止损)。' },
USE_FIXED_RISK_SIZING: { value: true, type: 'boolean', category: 'risk', description: '是否使用固定风险仓位计算(基于止损距离和账户余额)。' },
FIXED_RISK_PERCENT: { value: 0.03, type: 'number', category: 'risk', description: '每笔交易固定风险百分比(如 0.01=1%)。' },
// /
SYNC_RECOVER_MISSING_POSITIONS: { value: true, type: 'boolean', category: 'position', description: '同步时补建「币安有仓、DB 无记录」的交易记录。' },
SYNC_RECOVER_ONLY_WHEN_HAS_SLTP: { value: true, type: 'boolean', category: 'position', description: '仅当该持仓存在止损/止盈单时才补建(未配置 SYSTEM_ORDER_ID_PREFIX 时生效)。' },
SYSTEM_ORDER_ID_PREFIX: { value: 'SYS', type: 'string', category: 'position', description: '系统单标识:下单时写入 newClientOrderId 前缀,同步时仅对「开仓订单 clientOrderId 以此前缀开头」的持仓补建;设空则用「是否有止损止盈单」判断。' },
}
const loadConfigs = async () => {

View File

@ -118,6 +118,7 @@ const TradeList = () => {
状态: trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消',
平仓类型: trade.exit_reason_display || '-',
开仓订单号: trade.entry_order_id || '-',
自定义订单号: trade.client_order_id || '-',
平仓订单号: trade.exit_order_id || '-',
入场时间: trade.entry_time,
平仓时间: trade.exit_time || null,
@ -634,6 +635,7 @@ const TradeList = () => {
<th>状态</th>
<th>平仓类型</th>
<th>币安订单号</th>
<th>自定义订单号</th>
<th>入场时间</th>
<th>平仓时间</th>
</tr>
@ -731,6 +733,7 @@ const TradeList = () => {
</td>
<td>{trade.exit_reason_display || '-'}</td>
<td className="order-id" style={{ fontSize: '12px' }}>{formatOrderIds()}</td>
<td className="order-id" style={{ fontSize: '12px' }} title={trade.client_order_id || ''}>{trade.client_order_id || '-'}</td>
<td>{formatTime(trade.entry_time)}</td>
<td>{trade.exit_time ? formatTime(trade.exit_time) : '-'}</td>
</tr>
@ -861,6 +864,12 @@ const TradeList = () => {
</span>
</div>
)}
{trade.client_order_id && (
<div className="trade-card-field">
<span className="trade-card-label">自定义订单号</span>
<span className="trade-card-value order-id" style={{ fontSize: '12px' }} title={trade.client_order_id}>{trade.client_order_id}</span>
</div>
)}
</div>
<div className="trade-card-footer">
<div className="trade-time-item">

View File

@ -678,6 +678,7 @@ class PositionManager:
if DB_AVAILABLE and Trade:
try:
logger.info(f"正在保存 {symbol} 交易记录到数据库...")
client_order_id = (order.get("clientOrderId") if order else None) or None
trade_id = Trade.create(
symbol=symbol,
side=side,
@ -686,6 +687,7 @@ class PositionManager:
leverage=leverage,
entry_reason=entry_reason,
entry_order_id=entry_order_id, # 保存币安订单号
client_order_id=client_order_id, # 自定义订单号,便于在订单记录中核对系统单
stop_loss_price=stop_loss_price,
take_profit_price=take_profit_price,
take_profit_1=take_profit_1,
@ -2834,6 +2836,7 @@ class PositionManager:
entry_order_id = same_side[0].get("orderId")
except Exception:
pass
client_order_id_sync = None
if system_order_prefix:
if not entry_order_id:
logger.debug(f" {symbol} 无法获取开仓订单号,跳过补建")
@ -2841,6 +2844,7 @@ class PositionManager:
try:
order_info = await self.client.client.futures_get_order(symbol=symbol, orderId=int(entry_order_id), recvWindow=20000)
cid = (order_info or {}).get("clientOrderId") or ""
client_order_id_sync = cid or None
if not cid.startswith(system_order_prefix):
logger.debug(f" {symbol} 开仓订单 clientOrderId={cid!r} 非系统前缀,跳过补建")
continue
@ -2864,6 +2868,7 @@ class PositionManager:
leverage=binance_position.get("leverage", 10),
entry_reason="sync_recovered",
entry_order_id=entry_order_id,
client_order_id=client_order_id_sync,
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,