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:
|
if entry_price > 0 and position_value > 0:
|
||||||
pnl_percent = (unrealized_pnl / position_value) * 100
|
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({
|
formatted_positions.append({
|
||||||
"symbol": pos.get('symbol'),
|
"symbol": pos.get('symbol'),
|
||||||
"side": "BUY" if position_amt > 0 else "SELL",
|
"side": "BUY" if position_amt > 0 else "SELL",
|
||||||
"quantity": abs(position_amt),
|
"quantity": abs(position_amt),
|
||||||
"entry_price": entry_price,
|
"entry_price": entry_price,
|
||||||
|
"entry_value_usdt": entry_value_usdt, # 开仓时的USDT数量
|
||||||
"mark_price": mark_price,
|
"mark_price": mark_price,
|
||||||
"pnl": unrealized_pnl,
|
"pnl": unrealized_pnl,
|
||||||
"pnl_percent": pnl_percent,
|
"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)} 个有效持仓")
|
logger.info(f"格式化后 {len(formatted_positions)} 个有效持仓")
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,19 @@ async def get_dashboard_data():
|
||||||
logger.warning(f"获取实时持仓失败 (HTTP {e.status_code}): {positions_error}")
|
logger.warning(f"获取实时持仓失败 (HTTP {e.status_code}): {positions_error}")
|
||||||
# 回退到数据库记录
|
# 回退到数据库记录
|
||||||
try:
|
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)} 个持仓")
|
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
|
||||||
except Exception as db_error:
|
except Exception as db_error:
|
||||||
logger.error(f"从数据库获取持仓记录失败: {db_error}")
|
logger.error(f"从数据库获取持仓记录失败: {db_error}")
|
||||||
|
|
@ -111,7 +123,19 @@ async def get_dashboard_data():
|
||||||
logger.warning(f"获取实时持仓失败: {positions_error}", exc_info=True)
|
logger.warning(f"获取实时持仓失败: {positions_error}", exc_info=True)
|
||||||
# 回退到数据库记录
|
# 回退到数据库记录
|
||||||
try:
|
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)} 个持仓")
|
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
|
||||||
except Exception as db_error:
|
except Exception as db_error:
|
||||||
logger.error(f"从数据库获取持仓记录失败: {db_error}")
|
logger.error(f"从数据库获取持仓记录失败: {db_error}")
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,12 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry-time {
|
||||||
|
color: #999;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.trade-pnl {
|
.trade-pnl {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
|
||||||
|
|
@ -70,30 +70,69 @@ const StatsDashboard = () => {
|
||||||
<h3>当前持仓</h3>
|
<h3>当前持仓</h3>
|
||||||
{openTrades.length > 0 ? (
|
{openTrades.length > 0 ? (
|
||||||
<div className="trades-list">
|
<div className="trades-list">
|
||||||
{openTrades.map((trade, index) => (
|
{openTrades.map((trade, index) => {
|
||||||
<div key={trade.id || trade.symbol || index} className="trade-item">
|
// 计算开仓USDT数量(如果后端没有提供,则计算)
|
||||||
<div className="trade-symbol">{trade.symbol}</div>
|
const entryValueUsdt = trade.entry_value_usdt !== undefined
|
||||||
<div className={`trade-side ${trade.side === 'BUY' ? 'buy' : 'sell'}`}>
|
? trade.entry_value_usdt
|
||||||
{trade.side}
|
: (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>
|
||||||
<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>暂无持仓</div>
|
<div>暂无持仓</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user