a
This commit is contained in:
parent
fb3b973d78
commit
57e2f81b21
|
|
@ -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)} 个有效持仓")
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user