601 lines
26 KiB
JavaScript
601 lines
26 KiB
JavaScript
import React, { useState, useEffect } from 'react'
|
||
import { useSelector } from 'react-redux'
|
||
import { api } from '../services/api'
|
||
import { selectAccountId } from '../store/appSlice'
|
||
import './TradeList.css'
|
||
|
||
const TradeList = () => {
|
||
const accountId = useSelector(selectAccountId) // 从 Redux 获取当前账号ID
|
||
const [trades, setTrades] = useState([])
|
||
const [stats, setStats] = useState(null)
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
// 筛选状态
|
||
const [period, setPeriod] = useState('today') // '1d', '7d', '30d', 'today', 'week', 'month', null
|
||
const [startDate, setStartDate] = useState('')
|
||
const [endDate, setEndDate] = useState('')
|
||
const [symbol, setSymbol] = useState('')
|
||
const [status, setStatus] = useState('')
|
||
const [useCustomDate, setUseCustomDate] = useState(false)
|
||
const [tradeType, setTradeType] = useState('')
|
||
const [exitReason, setExitReason] = useState('')
|
||
|
||
useEffect(() => {
|
||
loadData()
|
||
}, [accountId]) // 当 accountId 变化时重新加载
|
||
|
||
const loadData = async () => {
|
||
setLoading(true)
|
||
try {
|
||
const params = {
|
||
limit: 100
|
||
}
|
||
|
||
// 如果使用快速时间段筛选
|
||
if (!useCustomDate && period) {
|
||
params.period = period
|
||
} else if (useCustomDate) {
|
||
// 使用自定义日期
|
||
if (startDate) params.start_date = startDate
|
||
if (endDate) params.end_date = endDate
|
||
}
|
||
|
||
if (symbol) params.symbol = symbol
|
||
if (status) params.status = status
|
||
if (tradeType) params.trade_type = tradeType
|
||
if (exitReason) params.exit_reason = exitReason
|
||
|
||
const [tradesData, statsData] = await Promise.all([
|
||
api.getTrades(params),
|
||
api.getTradeStats(params)
|
||
])
|
||
setTrades(tradesData.trades || [])
|
||
setStats(statsData)
|
||
} catch (error) {
|
||
console.error('Failed to load trades:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const handlePeriodChange = (newPeriod) => {
|
||
setPeriod(newPeriod)
|
||
setUseCustomDate(false)
|
||
setStartDate('')
|
||
setEndDate('')
|
||
}
|
||
|
||
const handleCustomDateToggle = () => {
|
||
setUseCustomDate(!useCustomDate)
|
||
if (!useCustomDate) {
|
||
setPeriod(null)
|
||
}
|
||
}
|
||
|
||
const handleReset = () => {
|
||
setPeriod(null)
|
||
setStartDate('')
|
||
setEndDate('')
|
||
setSymbol('')
|
||
setStatus('')
|
||
setUseCustomDate(false)
|
||
}
|
||
|
||
if (loading) return <div className="loading">加载中...</div>
|
||
|
||
return (
|
||
<div className="trade-list">
|
||
<h2>交易记录</h2>
|
||
<p style={{ color: '#666', fontSize: '14px', marginTop: '-10px', marginBottom: '20px' }}>
|
||
说明:每条记录代表一笔完整的交易(开仓+平仓),统计总盈亏时每条记录只计算一次
|
||
</p>
|
||
|
||
{/* 筛选面板 */}
|
||
<div className="filter-panel">
|
||
<div className="filter-section">
|
||
<label>快速筛选:</label>
|
||
<div className="period-buttons">
|
||
<button
|
||
className={period === 'today' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('today')}
|
||
>
|
||
今天
|
||
</button>
|
||
<button
|
||
className={period === 'week' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('week')}
|
||
>
|
||
本周
|
||
</button>
|
||
<button
|
||
className={period === 'month' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('month')}
|
||
>
|
||
本月
|
||
</button>
|
||
<button
|
||
className={period === '1d' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('1d')}
|
||
>
|
||
最近1天
|
||
</button>
|
||
<button
|
||
className={period === '7d' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('7d')}
|
||
>
|
||
最近7天
|
||
</button>
|
||
<button
|
||
className={period === '30d' ? 'active' : ''}
|
||
onClick={() => handlePeriodChange('30d')}
|
||
>
|
||
最近30天
|
||
</button>
|
||
<button
|
||
className={period === null && !useCustomDate ? 'active' : ''}
|
||
onClick={() => handlePeriodChange(null)}
|
||
>
|
||
全部
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="filter-section">
|
||
<label>
|
||
<input
|
||
type="checkbox"
|
||
checked={useCustomDate}
|
||
onChange={handleCustomDateToggle}
|
||
/>
|
||
自定义时间段
|
||
</label>
|
||
{useCustomDate && (
|
||
<div className="date-inputs">
|
||
<input
|
||
type="date"
|
||
value={startDate}
|
||
onChange={(e) => setStartDate(e.target.value)}
|
||
placeholder="开始日期"
|
||
/>
|
||
<span>至</span>
|
||
<input
|
||
type="date"
|
||
value={endDate}
|
||
onChange={(e) => setEndDate(e.target.value)}
|
||
placeholder="结束日期"
|
||
min={startDate}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="filter-section">
|
||
<label>交易对:</label>
|
||
<input
|
||
type="text"
|
||
value={symbol}
|
||
onChange={(e) => setSymbol(e.target.value)}
|
||
placeholder="如: BTCUSDT"
|
||
style={{ width: '150px' }}
|
||
/>
|
||
</div>
|
||
<div className="filter-section">
|
||
<label>交易类型:</label>
|
||
<select
|
||
value={tradeType}
|
||
onChange={(e) => setTradeType(e.target.value)}
|
||
style={{ width: '120px' }}
|
||
>
|
||
<option value="">全部</option>
|
||
<option value="buy">买入</option>
|
||
<option value="sell">卖出</option>
|
||
</select>
|
||
</div>
|
||
<div className="filter-section">
|
||
<label>平仓原因:</label>
|
||
<select
|
||
value={exitReason}
|
||
onChange={(e) => setExitReason(e.target.value)}
|
||
style={{ width: '120px' }}
|
||
>
|
||
<option value="">全部</option>
|
||
<option value="stop_loss">止损</option>
|
||
<option value="take_profit">止盈</option>
|
||
<option value="trailing_stop">移动止损</option>
|
||
<option value="manual">手动平仓</option>
|
||
<option value="sync">同步平仓</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="filter-section">
|
||
<label>状态:</label>
|
||
<select
|
||
value={status}
|
||
onChange={(e) => setStatus(e.target.value)}
|
||
style={{ width: '120px' }}
|
||
>
|
||
<option value="">全部</option>
|
||
<option value="open">持仓中</option>
|
||
<option value="closed">已平仓</option>
|
||
<option value="cancelled">已取消</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="filter-actions">
|
||
<button className="btn-primary" onClick={loadData}>
|
||
查询
|
||
</button>
|
||
<button className="btn-secondary" onClick={handleReset}>
|
||
重置
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{
|
||
stats && (
|
||
<div >
|
||
<div style={{ fontSize: '1.1rem' }}>整体统计</div>
|
||
<div style={{ fontSize: '1.1rem' }}>总交易数:{stats.total_trades} </div>
|
||
<div style={{ fontSize: '1.1rem' }}>胜率:{stats.win_rate.toFixed(2)}%</div>
|
||
<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 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 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>
|
||
)
|
||
}
|
||
|
||
{
|
||
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 >
|
||
)
|
||
}
|
||
|
||
export default TradeList
|