This commit is contained in:
薇薇安 2026-01-15 09:07:27 +08:00
parent fb3b973d78
commit 57e2f81b21
4 changed files with 114 additions and 26 deletions

View File

@ -267,15 +267,34 @@ async def get_realtime_positions():
if entry_price > 0 and position_value > 0:
pnl_percent = (unrealized_pnl / position_value) * 100
# 计算开仓时的USDT数量
entry_value_usdt = abs(position_amt) * entry_price
# 尝试从数据库获取开仓时间
entry_time = None
try:
from database.models import Trade
db_trades = Trade.get_by_symbol(pos.get('symbol'), status='open')
if db_trades:
# 找到匹配的交易记录通过symbol和entry_price匹配
for db_trade in db_trades:
if abs(float(db_trade.get('entry_price', 0)) - entry_price) < 0.01:
entry_time = db_trade.get('entry_time')
break
except Exception as e:
logger.debug(f"获取开仓时间失败: {e}")
formatted_positions.append({
"symbol": pos.get('symbol'),
"side": "BUY" if position_amt > 0 else "SELL",
"quantity": abs(position_amt),
"entry_price": entry_price,
"entry_value_usdt": entry_value_usdt, # 开仓时的USDT数量
"mark_price": mark_price,
"pnl": unrealized_pnl,
"pnl_percent": pnl_percent,
"leverage": int(pos.get('leverage', 1))
"leverage": int(pos.get('leverage', 1)),
"entry_time": entry_time # 开仓时间
})
logger.info(f"格式化后 {len(formatted_positions)} 个有效持仓")

View File

@ -102,7 +102,19 @@ async def get_dashboard_data():
logger.warning(f"获取实时持仓失败 (HTTP {e.status_code}): {positions_error}")
# 回退到数据库记录
try:
open_trades = Trade.get_all(status='open')[:10]
db_trades = Trade.get_all(status='open')[:10]
# 格式化数据库记录,添加 entry_value_usdt 字段
open_trades = []
for trade in db_trades:
entry_value_usdt = float(trade.get('quantity', 0)) * float(trade.get('entry_price', 0))
formatted_trade = {
**trade,
'entry_value_usdt': entry_value_usdt,
'mark_price': trade.get('entry_price', 0), # 数据库中没有标记价,使用入场价
'pnl': trade.get('pnl', 0),
'pnl_percent': trade.get('pnl_percent', 0)
}
open_trades.append(formatted_trade)
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
except Exception as db_error:
logger.error(f"从数据库获取持仓记录失败: {db_error}")
@ -111,7 +123,19 @@ async def get_dashboard_data():
logger.warning(f"获取实时持仓失败: {positions_error}", exc_info=True)
# 回退到数据库记录
try:
open_trades = Trade.get_all(status='open')[:10]
db_trades = Trade.get_all(status='open')[:10]
# 格式化数据库记录,添加 entry_value_usdt 字段
open_trades = []
for trade in db_trades:
entry_value_usdt = float(trade.get('quantity', 0)) * float(trade.get('entry_price', 0))
formatted_trade = {
**trade,
'entry_value_usdt': entry_value_usdt,
'mark_price': trade.get('entry_price', 0), # 数据库中没有标记价,使用入场价
'pnl': trade.get('pnl', 0),
'pnl_percent': trade.get('pnl_percent', 0)
}
open_trades.append(formatted_trade)
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
except Exception as db_error:
logger.error(f"从数据库获取持仓记录失败: {db_error}")

View File

@ -193,6 +193,12 @@
border-radius: 6px;
}
.entry-time {
color: #999;
font-size: 0.8rem;
font-style: italic;
}
@media (min-width: 768px) {
.trade-pnl {
background: transparent;

View File

@ -70,30 +70,69 @@ const StatsDashboard = () => {
<h3>当前持仓</h3>
{openTrades.length > 0 ? (
<div className="trades-list">
{openTrades.map((trade, index) => (
<div key={trade.id || trade.symbol || index} className="trade-item">
<div className="trade-symbol">{trade.symbol}</div>
<div className={`trade-side ${trade.side === 'BUY' ? 'buy' : 'sell'}`}>
{trade.side}
{openTrades.map((trade, index) => {
// USDT
const entryValueUsdt = trade.entry_value_usdt !== undefined
? trade.entry_value_usdt
: (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))
//
const formatEntryTime = (timeStr) => {
if (!timeStr) return null
try {
const date = new Date(timeStr)
const now = new Date()
const diffMs = now - date
const diffMins = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMs / 3600000)
const diffDays = Math.floor(diffMs / 86400000)
if (diffMins < 1) return '刚刚'
if (diffMins < 60) return `${diffMins}分钟前`
if (diffHours < 24) return `${diffHours}小时前`
if (diffDays < 7) return `${diffDays}天前`
// 7
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
} catch (e) {
return timeStr
}
}
return (
<div key={trade.id || trade.symbol || index} className="trade-item">
<div className="trade-symbol">{trade.symbol}</div>
<div className={`trade-side ${trade.side === 'BUY' ? 'buy' : 'sell'}`}>
{trade.side}
</div>
<div className="trade-info">
<div>开仓金额: {entryValueUsdt.toFixed(2)} USDT</div>
<div>数量: {parseFloat(trade.quantity || 0).toFixed(4)}</div>
<div>入场价: {parseFloat(trade.entry_price || 0).toFixed(4)}</div>
{trade.mark_price && (
<div>标记价: {parseFloat(trade.mark_price).toFixed(4)}</div>
)}
{trade.leverage && (
<div>杠杆: {trade.leverage}x</div>
)}
{trade.entry_time && (
<div className="entry-time">开仓时间: {formatEntryTime(trade.entry_time)}</div>
)}
</div>
<div className={`trade-pnl ${parseFloat(trade.pnl || 0) >= 0 ? 'positive' : 'negative'}`}>
{parseFloat(trade.pnl || 0).toFixed(2)} USDT
{trade.pnl_percent !== undefined && (
<span> ({parseFloat(trade.pnl_percent).toFixed(2)}%)</span>
)}
</div>
</div>
<div className="trade-info">
<div>数量: {parseFloat(trade.quantity || 0).toFixed(4)}</div>
<div>入场价: {parseFloat(trade.entry_price || 0).toFixed(4)}</div>
{trade.mark_price && (
<div>标记价: {parseFloat(trade.mark_price).toFixed(4)}</div>
)}
{trade.leverage && (
<div>杠杆: {trade.leverage}x</div>
)}
</div>
<div className={`trade-pnl ${parseFloat(trade.pnl || 0) >= 0 ? 'positive' : 'negative'}`}>
{parseFloat(trade.pnl || 0).toFixed(2)} USDT
{trade.pnl_percent !== undefined && (
<span> ({parseFloat(trade.pnl_percent).toFixed(2)}%)</span>
)}
</div>
</div>
))}
)
})}
</div>
) : (
<div>暂无持仓</div>