This commit is contained in:
薇薇安 2026-01-22 20:30:02 +08:00
parent 078576ddf8
commit d914b64294

View File

@ -240,366 +240,374 @@ const TradeList = () => {
{
stats && (
<div>
<div>整体统计</div>
<div>总交易数{stats.total_trades}</div>
<div>胜率{stats.win_rate.toFixed(2)}%</div>
<div>总盈亏{stats.total_pnl.toFixed(2)} USDT</div>
<div>平均盈亏{stats.avg_pnl.toFixed(2)} USDT</div>
<div>平均持仓时长分钟{stats.avg_duration_minutes ? Number(stats.avg_duration_minutes).toFixed(0) : 0}</div>
<div>平仓原因有意义交易
<div style={{ fontSize: '1.1rem' }}>
{(() => {
const m = stats.exit_reason_counts || {}
const stopLoss = Number(m.stop_loss || 0)
const takeProfit = Number(m.take_profit || 0)
const trailing = Number(m.trailing_stop || 0)
const manual = Number(m.manual || 0)
const sync = Number(m.sync || 0)
const other = Number(m.unknown || 0)
const parts = []
if (stopLoss) parts.push(`止损 ${stopLoss}`)
if (takeProfit) parts.push(`止盈 ${takeProfit}`)
if (trailing) parts.push(`移动止损 ${trailing}`)
if (manual) parts.push(`手动 ${manual}`)
if (sync) parts.push(`同步 ${sync}`)
if (other) parts.push(`其他 ${other}`)
return parts.length ? parts.join(' / ') : '—'
})()}
<div >
<div style={{ fontSize: '1.1rem' }}>整体统计</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<div style={{ fontSize: '1.1rem' }}>总交易数{stats.total_trades} </div>
<div style={{ fontSize: '1.1rem' }}>胜率{stats.win_rate.toFixed(2)}%</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<div style={{ fontSize: '1.1rem' }}>总盈亏{stats.total_pnl.toFixed(2)} USDT</div>
<div style={{ fontSize: '1.1rem' }}>平均盈亏{stats.avg_pnl.toFixed(2)} USDT</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<div style={{ fontSize: '1.1rem' }}>平均持仓时长分钟{stats.avg_duration_minutes ? Number(stats.avg_duration_minutes).toFixed(0) : 0}</div>
<div style={{ fontSize: '1.1rem' }}>平仓原因有意义交易
<div style={{ fontSize: '1.1rem' }}>
{(() => {
const m = stats.exit_reason_counts || {}
const stopLoss = Number(m.stop_loss || 0)
const takeProfit = Number(m.take_profit || 0)
const trailing = Number(m.trailing_stop || 0)
const manual = Number(m.manual || 0)
const sync = Number(m.sync || 0)
const other = Number(m.unknown || 0)
const parts = []
if (stopLoss) parts.push(`止损 ${stopLoss}`)
if (takeProfit) parts.push(`止盈 ${takeProfit}`)
if (trailing) parts.push(`移动止损 ${trailing}`)
if (manual) parts.push(`手动 ${manual}`)
if (sync) parts.push(`同步 ${sync}`)
if (other) parts.push(`其他 ${other}`)
return parts.length ? parts.join(' / ') : '—'
})()}
</div>
</div>
</div>
<div className="stat-value">平均盈利 / 平均亏损期望 3:1{Number(stats.avg_win_loss_ratio || 0).toFixed(2)} : 1</div>
<div className="stat-value">总交易量名义{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT</div>
</div>
)
}
{
stats && (
<div className="stats-summary">
<div className="stat-card">
<div className="stat-label">总交易数</div>
<div className="stat-value">{stats.total_trades}</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
{stats.meaningful_trades !== undefined && (
<>有意义: {stats.meaningful_trades}0盈亏: {stats.zero_pnl_trades || 0}</>
)}
{stats.meaningful_trades === undefined && <>已平仓的完整交易</>}
</div>
</div>
<div className="stat-card">
<div className="stat-label">胜率</div>
<div className="stat-value">{stats.win_rate.toFixed(2)}%</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
{stats.meaningful_trades !== undefined && <>已排除0盈亏订单</>}
</div>
</div>
<div className="stat-card">
<div className="stat-label">总盈亏</div>
<div className={`stat-value ${stats.total_pnl >= 0 ? 'positive' : 'negative'}`}>
{stats.total_pnl.toFixed(2)} USDT
</div>
</div>
<div className="stat-card">
<div className="stat-label">平均盈亏</div>
<div className={`stat-value ${stats.avg_pnl >= 0 ? 'positive' : 'negative'}`}>
{stats.avg_pnl.toFixed(2)} USDT
</div>
</div>
{"avg_duration_minutes" in stats && stats.avg_duration_minutes !== null && stats.avg_duration_minutes !== undefined && (
<div className="stat-card">
<div className="stat-label">平均持仓时长分钟</div>
<div className="stat-value">{Number(stats.avg_duration_minutes || 0).toFixed(0)}</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
仅统计有意义交易优先使用 duration_minutes缺失时用 exit_time-entry_time 计算
</div>
</div>
)}
{"exit_reason_counts" in stats && stats.exit_reason_counts && (
<div className="stat-card">
<div className="stat-label">平仓原因有意义交易</div>
<div className="stat-value" style={{ fontSize: '1.1rem' }}>
{(() => {
const m = stats.exit_reason_counts || {}
const stopLoss = Number(m.stop_loss || 0)
const takeProfit = Number(m.take_profit || 0)
const trailing = Number(m.trailing_stop || 0)
const manual = Number(m.manual || 0)
const sync = Number(m.sync || 0)
const other = Number(m.unknown || 0)
const parts = []
if (stopLoss) parts.push(`止损 ${stopLoss}`)
if (takeProfit) parts.push(`止盈 ${takeProfit}`)
if (trailing) parts.push(`移动止损 ${trailing}`)
if (manual) parts.push(`手动 ${manual}`)
if (sync) parts.push(`同步 ${sync}`)
if (other) parts.push(`其他 ${other}`)
return parts.length ? parts.join(' / ') : '—'
})()}
</div>
</div>
)}
{"avg_win_pnl" in stats && "avg_loss_pnl_abs" in stats && Number(stats.total_pnl || 0) > 0 && (
<div className="stat-card">
<div className="stat-label">平均盈利 / 平均亏损期望 3:1</div>
<div
className={`stat-value ${typeof stats.avg_win_loss_ratio === 'number' && stats.avg_win_loss_ratio >= 3 ? 'positive' : ''
}`}
>
{typeof stats.avg_win_loss_ratio === 'number'
? `${stats.avg_win_loss_ratio.toFixed(2)} : 1`
: '—'}
</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
+{Number(stats.avg_win_pnl || 0).toFixed(2)} / -{Number(stats.avg_loss_pnl_abs || 0).toFixed(2)} USDT
</div>
</div>
)}
{"total_notional_usdt" in stats && (
<div className="stat-card">
<div className="stat-label">总交易量名义</div>
<div className="stat-value">{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
口径入场价×数量
</div>
</div>
)}
</div>
)
}
{
trades.length === 0 ? (
<div className="no-data">暂无交易记录</div>
) : (
<>
{/* 桌面端表格 */}
<table className="trades-table">
<thead>
<tr>
<th>交易ID</th>
<th>交易对</th>
<th>方向</th>
<th>数量</th>
<th>名义</th>
<th>保证金</th>
<th>入场价</th>
<th>出场价</th>
<th>盈亏</th>
<th>盈亏比例</th>
<th>状态</th>
<th>平仓类型</th>
<th>币安订单号</th>
<th>入场时间</th>
<th>平仓时间</th>
</tr>
</thead>
<tbody>
{trades.map(trade => {
// /使notional_usdt / margin_usdt退
const notional = trade.notional_usdt !== undefined && trade.notional_usdt !== null
? parseFloat(trade.notional_usdt)
: (
trade.entry_value_usdt !== undefined && trade.entry_value_usdt !== null
? parseFloat(trade.entry_value_usdt)
: (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))
)
const leverage = parseFloat(trade.leverage || 10)
const margin = trade.margin_usdt !== undefined && trade.margin_usdt !== null
? parseFloat(trade.margin_usdt)
: (leverage > 0 ? notional / leverage : 0)
// /
const pnl = parseFloat(trade.pnl || 0)
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
//
// Unix
const formatTime = (timeValue) => {
if (!timeValue) return '-'
try {
let date
// Unix
if (typeof timeValue === 'number') {
date = new Date(timeValue * 1000)
} else {
date = new Date(timeValue)
}
if (isNaN(date.getTime())) return String(timeValue)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Asia/Shanghai'
})
} catch (e) {
return String(timeValue)
}
}
//
const formatOrderIds = () => {
const entry = trade.entry_order_id || '-'
const exit = trade.exit_order_id || '-'
if (entry === '-' && exit === '-') return '-'
if (entry !== '-' && exit !== '-') {
return `开仓: ${entry} / 平仓: ${exit}`
}
return entry !== '-' ? `开仓: ${entry}` : `平仓: ${exit}`
}
return (
<tr key={trade.id}>
<td style={{ fontSize: '12px', color: '#999' }}>#{trade.id}</td>
<td>{trade.symbol}</td>
<td className={trade.side === 'BUY' ? 'buy' : 'sell'}>{trade.side}</td>
<td>{parseFloat(trade.quantity).toFixed(4)}</td>
<td>{notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT</td>
<td>{margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT</td>
<td>{parseFloat(trade.entry_price).toFixed(4)}</td>
<td>{trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}</td>
<td className={pnl >= 0 ? 'positive' : 'negative'}>
{pnl.toFixed(2)} USDT
</td>
<td className={pnlPercent >= 0 ? 'positive' : 'negative'}>
{pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
</td>
<td>
<span className={`status ${trade.status}`}>{trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}</span>
</td>
<td>{trade.exit_reason_display || '-'}</td>
<td className="order-id" style={{ fontSize: '12px' }}>{formatOrderIds()}</td>
<td>{formatTime(trade.entry_time)}</td>
<td>{trade.exit_time ? formatTime(trade.exit_time) : '-'}</td>
</tr>
)
})}
</tbody>
</table>
{/* 移动端卡片 */}
<div className="trades-cards">
{trades.map(trade => {
// /
const notional = trade.notional_usdt !== undefined && trade.notional_usdt !== null
? parseFloat(trade.notional_usdt)
: (
trade.entry_value_usdt !== undefined && trade.entry_value_usdt !== null
? parseFloat(trade.entry_value_usdt)
: (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))
)
const leverage = parseFloat(trade.leverage || 10)
const margin = trade.margin_usdt !== undefined && trade.margin_usdt !== null
? parseFloat(trade.margin_usdt)
: (leverage > 0 ? notional / leverage : 0)
const pnl = parseFloat(trade.pnl || 0)
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
//
// Unix
const formatTime = (timeValue) => {
if (!timeValue) return '-'
try {
let date
// Unix
if (typeof timeValue === 'number') {
date = new Date(timeValue * 1000)
} else {
date = new Date(timeValue)
}
if (isNaN(date.getTime())) return String(timeValue)
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Asia/Shanghai'
})
} catch (e) {
return String(timeValue)
}
}
return (
<div key={trade.id} className="trade-card">
<div className="trade-card-header">
<span className="trade-card-symbol">{trade.symbol}</span>
<span style={{ fontSize: '11px', color: '#999', marginLeft: '8px' }}>交易ID: #{trade.id}</span>
<span className={`trade-card-side ${trade.side === 'BUY' ? 'buy' : 'sell'} status ${trade.status}`}>
{trade.side === 'BUY' ? '买入' : '卖出'} · {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
</span>
</div>
<div className="trade-card-body">
<div className="trade-card-field">
<span className="trade-card-label">数量</span>
<span className="trade-card-value">{parseFloat(trade.quantity).toFixed(4)}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">名义</span>
<span className="trade-card-value">{notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">保证金</span>
<span className="trade-card-value">{margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">入场价</span>
<span className="trade-card-value">{parseFloat(trade.entry_price).toFixed(4)}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">出场价</span>
<span className="trade-card-value">{trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">盈亏</span>
<span className={`trade-card-value ${pnl >= 0 ? 'positive' : 'negative'}`}>
{pnl.toFixed(2)} USDT
</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">盈亏比例</span>
<span className={`trade-card-value ${pnlPercent >= 0 ? 'positive' : 'negative'}`}>
{pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
</span>
</div>
{trade.exit_reason_display && (
<div className="trade-card-field">
<span className="trade-card-label">平仓类型</span>
<span className="trade-card-value">{trade.exit_reason_display}</span>
</div>
)}
{(trade.entry_order_id || trade.exit_order_id) && (
<div className="trade-card-field">
<span className="trade-card-label">币安订单号</span>
<span className="trade-card-value order-id" style={{ fontSize: '12px' }}>
{trade.entry_order_id ? `开仓: ${trade.entry_order_id}` : ''}
{trade.entry_order_id && trade.exit_order_id ? ' / ' : ''}
{trade.exit_order_id ? `平仓: ${trade.exit_order_id}` : ''}
</span>
</div>
)}
</div>
<div className="trade-card-footer">
<div className="trade-time-item">
<span className="time-label">入场:</span>
<span>{formatTime(trade.entry_time)}</span>
</div>
{trade.exit_time && (
<div className="trade-time-item">
<span className="time-label">平仓:</span>
<span>{formatTime(trade.exit_time)}</span>
</div>
)}
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
<div style={{ fontSize: '1.1rem' }}>平均盈利 / 平均亏损期望 3:1{Number(stats.avg_win_loss_ratio || 0).toFixed(2)} : 1</div>
<div style={{ fontSize: '1.1rem' }}>总交易量名义{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT</div>
</div>
</div>
)
})}
</div>
</>
)
}
}
{
stats && (
<div className="stats-summary">
<div className="stat-card">
<div className="stat-label">总交易数</div>
<div className="stat-value">{stats.total_trades}</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
{stats.meaningful_trades !== undefined && (
<>有意义: {stats.meaningful_trades}0盈亏: {stats.zero_pnl_trades || 0}</>
)}
{stats.meaningful_trades === undefined && <>已平仓的完整交易</>}
</div>
</div>
<div className="stat-card">
<div className="stat-label">胜率</div>
<div className="stat-value">{stats.win_rate.toFixed(2)}%</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
{stats.meaningful_trades !== undefined && <>已排除0盈亏订单</>}
</div>
</div>
<div className="stat-card">
<div className="stat-label">总盈亏</div>
<div className={`stat-value ${stats.total_pnl >= 0 ? 'positive' : 'negative'}`}>
{stats.total_pnl.toFixed(2)} USDT
</div>
</div>
<div className="stat-card">
<div className="stat-label">平均盈亏</div>
<div className={`stat-value ${stats.avg_pnl >= 0 ? 'positive' : 'negative'}`}>
{stats.avg_pnl.toFixed(2)} USDT
</div>
</div>
{"avg_duration_minutes" in stats && stats.avg_duration_minutes !== null && stats.avg_duration_minutes !== undefined && (
<div className="stat-card">
<div className="stat-label">平均持仓时长分钟</div>
<div className="stat-value">{Number(stats.avg_duration_minutes || 0).toFixed(0)}</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
仅统计有意义交易优先使用 duration_minutes缺失时用 exit_time-entry_time 计算
</div>
</div>
)}
{"exit_reason_counts" in stats && stats.exit_reason_counts && (
<div className="stat-card">
<div className="stat-label">平仓原因有意义交易</div>
<div className="stat-value" style={{ fontSize: '1.1rem' }}>
{(() => {
const m = stats.exit_reason_counts || {}
const stopLoss = Number(m.stop_loss || 0)
const takeProfit = Number(m.take_profit || 0)
const trailing = Number(m.trailing_stop || 0)
const manual = Number(m.manual || 0)
const sync = Number(m.sync || 0)
const other = Number(m.unknown || 0)
const parts = []
if (stopLoss) parts.push(`止损 ${stopLoss}`)
if (takeProfit) parts.push(`止盈 ${takeProfit}`)
if (trailing) parts.push(`移动止损 ${trailing}`)
if (manual) parts.push(`手动 ${manual}`)
if (sync) parts.push(`同步 ${sync}`)
if (other) parts.push(`其他 ${other}`)
return parts.length ? parts.join(' / ') : '—'
})()}
</div>
</div>
)}
{"avg_win_pnl" in stats && "avg_loss_pnl_abs" in stats && Number(stats.total_pnl || 0) > 0 && (
<div className="stat-card">
<div className="stat-label">平均盈利 / 平均亏损期望 3:1</div>
<div
className={`stat-value ${typeof stats.avg_win_loss_ratio === 'number' && stats.avg_win_loss_ratio >= 3 ? 'positive' : ''
}`}
>
{typeof stats.avg_win_loss_ratio === 'number'
? `${stats.avg_win_loss_ratio.toFixed(2)} : 1`
: '—'}
</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
+{Number(stats.avg_win_pnl || 0).toFixed(2)} / -{Number(stats.avg_loss_pnl_abs || 0).toFixed(2)} USDT
</div>
</div>
)}
{"total_notional_usdt" in stats && (
<div className="stat-card">
<div className="stat-label">总交易量名义</div>
<div className="stat-value">{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT</div>
<div style={{ fontSize: '12px', color: '#999', marginTop: '4px' }}>
口径入场价×数量
</div>
</div>
)}
</div>
)
}
{
trades.length === 0 ? (
<div className="no-data">暂无交易记录</div>
) : (
<>
{/* 桌面端表格 */}
<table className="trades-table">
<thead>
<tr>
<th>交易ID</th>
<th>交易对</th>
<th>方向</th>
<th>数量</th>
<th>名义</th>
<th>保证金</th>
<th>入场价</th>
<th>出场价</th>
<th>盈亏</th>
<th>盈亏比例</th>
<th>状态</th>
<th>平仓类型</th>
<th>币安订单号</th>
<th>入场时间</th>
<th>平仓时间</th>
</tr>
</thead>
<tbody>
{trades.map(trade => {
// /使notional_usdt / margin_usdt退
const notional = trade.notional_usdt !== undefined && trade.notional_usdt !== null
? parseFloat(trade.notional_usdt)
: (
trade.entry_value_usdt !== undefined && trade.entry_value_usdt !== null
? parseFloat(trade.entry_value_usdt)
: (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))
)
const leverage = parseFloat(trade.leverage || 10)
const margin = trade.margin_usdt !== undefined && trade.margin_usdt !== null
? parseFloat(trade.margin_usdt)
: (leverage > 0 ? notional / leverage : 0)
// /
const pnl = parseFloat(trade.pnl || 0)
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
//
// Unix
const formatTime = (timeValue) => {
if (!timeValue) return '-'
try {
let date
// Unix
if (typeof timeValue === 'number') {
date = new Date(timeValue * 1000)
} else {
date = new Date(timeValue)
}
if (isNaN(date.getTime())) return String(timeValue)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Asia/Shanghai'
})
} catch (e) {
return String(timeValue)
}
}
//
const formatOrderIds = () => {
const entry = trade.entry_order_id || '-'
const exit = trade.exit_order_id || '-'
if (entry === '-' && exit === '-') return '-'
if (entry !== '-' && exit !== '-') {
return `开仓: ${entry} / 平仓: ${exit}`
}
return entry !== '-' ? `开仓: ${entry}` : `平仓: ${exit}`
}
return (
<tr key={trade.id}>
<td style={{ fontSize: '12px', color: '#999' }}>#{trade.id}</td>
<td>{trade.symbol}</td>
<td className={trade.side === 'BUY' ? 'buy' : 'sell'}>{trade.side}</td>
<td>{parseFloat(trade.quantity).toFixed(4)}</td>
<td>{notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT</td>
<td>{margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT</td>
<td>{parseFloat(trade.entry_price).toFixed(4)}</td>
<td>{trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}</td>
<td className={pnl >= 0 ? 'positive' : 'negative'}>
{pnl.toFixed(2)} USDT
</td>
<td className={pnlPercent >= 0 ? 'positive' : 'negative'}>
{pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
</td>
<td>
<span className={`status ${trade.status}`}>{trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}</span>
</td>
<td>{trade.exit_reason_display || '-'}</td>
<td className="order-id" style={{ fontSize: '12px' }}>{formatOrderIds()}</td>
<td>{formatTime(trade.entry_time)}</td>
<td>{trade.exit_time ? formatTime(trade.exit_time) : '-'}</td>
</tr>
)
})}
</tbody>
</table>
{/* 移动端卡片 */}
<div className="trades-cards">
{trades.map(trade => {
// /
const notional = trade.notional_usdt !== undefined && trade.notional_usdt !== null
? parseFloat(trade.notional_usdt)
: (
trade.entry_value_usdt !== undefined && trade.entry_value_usdt !== null
? parseFloat(trade.entry_value_usdt)
: (parseFloat(trade.quantity || 0) * parseFloat(trade.entry_price || 0))
)
const leverage = parseFloat(trade.leverage || 10)
const margin = trade.margin_usdt !== undefined && trade.margin_usdt !== null
? parseFloat(trade.margin_usdt)
: (leverage > 0 ? notional / leverage : 0)
const pnl = parseFloat(trade.pnl || 0)
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
//
// Unix
const formatTime = (timeValue) => {
if (!timeValue) return '-'
try {
let date
// Unix
if (typeof timeValue === 'number') {
date = new Date(timeValue * 1000)
} else {
date = new Date(timeValue)
}
if (isNaN(date.getTime())) return String(timeValue)
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Asia/Shanghai'
})
} catch (e) {
return String(timeValue)
}
}
return (
<div key={trade.id} className="trade-card">
<div className="trade-card-header">
<span className="trade-card-symbol">{trade.symbol}</span>
<span style={{ fontSize: '11px', color: '#999', marginLeft: '8px' }}>交易ID: #{trade.id}</span>
<span className={`trade-card-side ${trade.side === 'BUY' ? 'buy' : 'sell'} status ${trade.status}`}>
{trade.side === 'BUY' ? '买入' : '卖出'} · {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
</span>
</div>
<div className="trade-card-body">
<div className="trade-card-field">
<span className="trade-card-label">数量</span>
<span className="trade-card-value">{parseFloat(trade.quantity).toFixed(4)}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">名义</span>
<span className="trade-card-value">{notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">保证金</span>
<span className="trade-card-value">{margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">入场价</span>
<span className="trade-card-value">{parseFloat(trade.entry_price).toFixed(4)}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">出场价</span>
<span className="trade-card-value">{trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">盈亏</span>
<span className={`trade-card-value ${pnl >= 0 ? 'positive' : 'negative'}`}>
{pnl.toFixed(2)} USDT
</span>
</div>
<div className="trade-card-field">
<span className="trade-card-label">盈亏比例</span>
<span className={`trade-card-value ${pnlPercent >= 0 ? 'positive' : 'negative'}`}>
{pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
</span>
</div>
{trade.exit_reason_display && (
<div className="trade-card-field">
<span className="trade-card-label">平仓类型</span>
<span className="trade-card-value">{trade.exit_reason_display}</span>
</div>
)}
{(trade.entry_order_id || trade.exit_order_id) && (
<div className="trade-card-field">
<span className="trade-card-label">币安订单号</span>
<span className="trade-card-value order-id" style={{ fontSize: '12px' }}>
{trade.entry_order_id ? `开仓: ${trade.entry_order_id}` : ''}
{trade.entry_order_id && trade.exit_order_id ? ' / ' : ''}
{trade.exit_order_id ? `平仓: ${trade.exit_order_id}` : ''}
</span>
</div>
)}
</div>
<div className="trade-card-footer">
<div className="trade-time-item">
<span className="time-label">入场:</span>
<span>{formatTime(trade.entry_time)}</span>
</div>
{trade.exit_time && (
<div className="trade-time-item">
<span className="time-label">平仓:</span>
<span>{formatTime(trade.exit_time)}</span>
</div>
)}
</div>
</div>
)
})}
</div>
</>
)
}
</div >
)
}