feat(position_manager, database): 添加开仓时间记录功能以优化交易记录

在 `position_manager.py` 中引入了真实开仓时间的获取逻辑,确保在补建交易记录时使用币安的实际开仓时间,避免时间显示为“当前时间”。同时,在 `models.py` 中更新了 `Trade` 类,新增 `entry_time` 参数以存储开仓时间,提升了交易记录的准确性与分析能力。
This commit is contained in:
薇薇安 2026-02-20 00:51:31 +08:00
parent 2c5524bdcf
commit 9299d70a31
2 changed files with 49 additions and 5 deletions

View File

@ -573,10 +573,12 @@ class Trade:
account_id: int = None,
entry_context=None,
status: str = "open",
entry_time=None,
):
"""创建交易记录(使用北京时间)
Args:
entry_time: 开仓时间Unix 时间戳秒 None 表示当前北京时间补建/同步时建议从币安订单或成交取真实时间
symbol: 交易对
status: 状态默认 "open"先落库等 WS 成交时可传 "pending"
side: 方向
@ -595,7 +597,16 @@ class Trade:
margin_usdt: 保证金USDT可选
entry_context: 入场思路/过程dict将存为 JSON信号强度市场状态趋势过滤通过情况等便于事后分析策略执行效果
"""
entry_time = get_beijing_time()
if entry_time is not None:
try:
if hasattr(entry_time, "timestamp"):
entry_time = int(entry_time.timestamp())
else:
entry_time = int(float(entry_time))
except (TypeError, ValueError):
entry_time = get_beijing_time()
else:
entry_time = get_beijing_time()
# 自动计算 notional/margin若调用方没传
try:

View File

@ -2626,6 +2626,7 @@ class PositionManager:
'initialStopLoss': stop_loss_price,
'leverage': leverage,
'entryReason': trade.get('entry_reason') or 'db_sync',
'entryTime': trade.get('entry_time'),
'atr': trade.get('atr'),
'maxProfit': 0.0,
'trailingStopActivated': False,
@ -3374,6 +3375,24 @@ class PositionManager:
continue
except Exception:
pass
# 从币安取真实开仓时间,避免补建后开仓时间全是“当前时间”
entry_time_ts = None
if entry_order_id:
try:
oi = await self.client.client.futures_get_order(symbol=symbol, orderId=int(entry_order_id), recvWindow=20000)
if oi and oi.get("time"):
entry_time_ts = int(oi["time"]) // 1000
except Exception:
pass
if entry_time_ts is None:
try:
recent = await self.client.get_recent_trades(symbol, limit=100)
same_side = [t for t in (recent or []) if str(t.get("side", "")).upper() == side]
if same_side:
same_side.sort(key=lambda x: int(x.get("time", 0)))
entry_time_ts = int(same_side[0].get("time", 0)) // 1000
except Exception:
pass
trade_id = Trade.create(
symbol=symbol,
side=side,
@ -3386,6 +3405,7 @@ class PositionManager:
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,
entry_time=entry_time_ts,
)
logger.info(f"{symbol} [状态同步] 已补建交易记录 (ID: {trade_id}, orderId: {entry_order_id or '-'}, entry_reason={entry_reason_sync})")
ticker = await self.client.get_ticker_24h(symbol)
@ -3406,6 +3426,7 @@ class PositionManager:
"changePercent": 0, "orderId": entry_order_id, "tradeId": trade_id,
"stopLoss": stop_loss_price, "takeProfit": take_profit_price, "initialStopLoss": stop_loss_price,
"leverage": lev, "entryReason": entry_reason_sync, "atr": None, "maxProfit": 0.0, "trailingStopActivated": False,
"entryTime": entry_time_ts if entry_time_ts is not None else get_beijing_time(),
}
self.active_positions[symbol] = position_info
# 补建后立即在交易所挂/修正止损止盈(替换可能存在的异常远止损、缺止盈等)
@ -3447,8 +3468,9 @@ class PositionManager:
f"{symbol} [状态同步] 检测到手动开仓,创建数据库记录... "
f"({side} {quantity:.4f} @ {entry_price:.4f})"
)
# 尽量从币安成交取 entry_order_idlimit=100、空时重试一次,便于订单记录/统计对账
# 尽量从币安成交取 entry_order_id 与真实开仓时间limit=100、空时重试一次
entry_order_id = None
entry_time_ts = None
try:
recent = await self.client.get_recent_trades(symbol, limit=100)
if not recent:
@ -3459,9 +3481,19 @@ class PositionManager:
if same_side:
same_side.sort(key=lambda x: int(x.get('time', 0)), reverse=True)
entry_order_id = same_side[0].get('orderId')
# 开仓时间取同方向最早一笔成交oldest避免显示为“当前时间”
same_side_asc = sorted(same_side, key=lambda x: int(x.get('time', 0)))
entry_time_ts = int(same_side_asc[0].get('time', 0)) // 1000
except Exception:
pass
# 创建数据库记录(显式传入 account_id避免多账号混用
if entry_time_ts is None and entry_order_id:
try:
oi = await self.client.client.futures_get_order(symbol=symbol, orderId=int(entry_order_id), recvWindow=20000)
if oi and oi.get('time'):
entry_time_ts = int(oi['time']) // 1000
except Exception:
pass
# 创建数据库记录(显式传入 account_id、真实开仓时间
trade_id = Trade.create(
symbol=symbol,
side=side,
@ -3473,6 +3505,7 @@ class PositionManager:
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,
entry_time=entry_time_ts,
)
logger.info(f"{symbol} [状态同步] ✓ 数据库记录已创建 (ID: {trade_id})")
@ -3510,14 +3543,14 @@ class PositionManager:
'quantity': quantity,
'entryPrice': entry_price,
'changePercent': 0, # 手动开仓,无法计算涨跌幅
'orderId': None,
'orderId': entry_order_id,
'tradeId': trade_id,
'stopLoss': stop_loss_price,
'takeProfit': take_profit_price,
'initialStopLoss': stop_loss_price,
'leverage': leverage,
'entryReason': 'manual_entry',
'entryTime': get_beijing_time(), # 补全入场时间
'entryTime': entry_time_ts if entry_time_ts is not None else get_beijing_time(), # 真实开仓时间(来自币安成交/订单)
'atr': None,
'maxProfit': 0.0,
'trailingStopActivated': False