a
This commit is contained in:
parent
244ef6f4ba
commit
06272b6922
|
|
@ -6,7 +6,7 @@ const TradeList = () => {
|
|||
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('')
|
||||
|
|
@ -19,13 +19,13 @@ const TradeList = () => {
|
|||
|
||||
useEffect(() => {
|
||||
loadData()
|
||||
|
||||
|
||||
// 监听账号切换事件,自动重新加载数据
|
||||
const handleAccountChange = () => {
|
||||
loadData()
|
||||
}
|
||||
window.addEventListener('ats:account:changed', handleAccountChange)
|
||||
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('ats:account:changed', handleAccountChange)
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ const TradeList = () => {
|
|||
const params = {
|
||||
limit: 100
|
||||
}
|
||||
|
||||
|
||||
// 如果使用快速时间段筛选
|
||||
if (!useCustomDate && period) {
|
||||
params.period = period
|
||||
|
|
@ -46,12 +46,12 @@ const TradeList = () => {
|
|||
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)
|
||||
|
|
@ -96,49 +96,49 @@ const TradeList = () => {
|
|||
<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
|
||||
<button
|
||||
className={period === 'today' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('today')}
|
||||
>
|
||||
今天
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === 'week' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('week')}
|
||||
>
|
||||
本周
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === 'month' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('month')}
|
||||
>
|
||||
本月
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === '1d' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('1d')}
|
||||
>
|
||||
最近1天
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === '7d' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('7d')}
|
||||
>
|
||||
最近7天
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === '30d' ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange('30d')}
|
||||
>
|
||||
最近30天
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
className={period === null && !useCustomDate ? 'active' : ''}
|
||||
onClick={() => handlePeriodChange(null)}
|
||||
>
|
||||
|
|
@ -149,8 +149,8 @@ const TradeList = () => {
|
|||
|
||||
<div className="filter-section">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={useCustomDate}
|
||||
onChange={handleCustomDateToggle}
|
||||
/>
|
||||
|
|
@ -248,14 +248,34 @@ const TradeList = () => {
|
|||
<div className="stat-value">总盈亏:{stats.total_pnl.toFixed(2)} USDT</div>
|
||||
<div className="stat-value">平均盈亏:{stats.avg_pnl.toFixed(2)} USDT</div>
|
||||
<div className="stat-value">平均持仓时长(分钟):{Number(stats.avg_duration_minutes || 0).toFixed(0)}</div>
|
||||
<div className="stat-value">平仓原因(有意义交易):{stats.exit_reason_counts.join(' / ')}</div>
|
||||
<div className="stat-value">平仓原因(有意义交易):
|
||||
<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>
|
||||
<div className="stat-value">平均盈利 / 平均亏损(期望 3:1):{stats.avg_win_loss_ratio.toFixed(2)} : 1</div>
|
||||
<div className="stat-value">总交易量(名义):{stats.total_notional_usdt.toFixed(2)} USDT</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
{stats && (
|
||||
<div className="stats-summary">
|
||||
<div className="stat-card">
|
||||
|
|
@ -324,9 +344,8 @@ const TradeList = () => {
|
|||
<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' : ''
|
||||
}`}
|
||||
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`
|
||||
|
|
@ -380,19 +399,19 @@ const TradeList = () => {
|
|||
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))
|
||||
)
|
||||
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) => {
|
||||
|
|
@ -418,7 +437,7 @@ const TradeList = () => {
|
|||
return String(timeValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 格式化订单号显示
|
||||
const formatOrderIds = () => {
|
||||
const entry = trade.entry_order_id || '-'
|
||||
|
|
@ -429,7 +448,7 @@ const TradeList = () => {
|
|||
}
|
||||
return entry !== '-' ? `开仓: ${entry}` : `平仓: ${exit}`
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<tr key={trade.id}>
|
||||
<td style={{ fontSize: '12px', color: '#999' }}>#{trade.id}</td>
|
||||
|
|
@ -458,7 +477,7 @@ const TradeList = () => {
|
|||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
{/* 移动端卡片 */}
|
||||
<div className="trades-cards">
|
||||
{trades.map(trade => {
|
||||
|
|
@ -466,17 +485,17 @@ const TradeList = () => {
|
|||
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))
|
||||
)
|
||||
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) => {
|
||||
|
|
@ -501,7 +520,7 @@ const TradeList = () => {
|
|||
return String(timeValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div key={trade.id} className="trade-card">
|
||||
<div className="trade-card-header">
|
||||
|
|
@ -562,17 +581,17 @@ const TradeList = () => {
|
|||
)}
|
||||
</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>
|
||||
<span className="time-label">入场:</span>
|
||||
<span>{formatTime(trade.entry_time)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{trade.exit_time && (
|
||||
<div className="trade-time-item">
|
||||
<span className="time-label">平仓:</span>
|
||||
<span>{formatTime(trade.exit_time)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user