auto_trade_sys/frontend/src/components/TradeList.jsx
薇薇安 d9830f395b a
2026-01-13 22:12:24 +08:00

245 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { api } from '../services/api'
import './TradeList.css'
const TradeList = () => {
const [trades, setTrades] = useState([])
const [stats, setStats] = useState(null)
const [loading, setLoading] = useState(true)
// 筛选状态
const [period, setPeriod] = useState(null) // '1d', '7d', '30d', null
const [startDate, setStartDate] = useState('')
const [endDate, setEndDate] = useState('')
const [symbol, setSymbol] = useState('')
const [status, setStatus] = useState('')
const [useCustomDate, setUseCustomDate] = useState(false)
useEffect(() => {
loadData()
}, [])
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
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>
{/* 筛选面板 */}
<div className="filter-panel">
<div className="filter-section">
<label>快速筛选</label>
<div className="period-buttons">
<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={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 className="stats-summary">
<div className="stat-card">
<div className="stat-label">总交易数</div>
<div className="stat-value">{stats.total_trades}</div>
</div>
<div className="stat-card">
<div className="stat-label">胜率</div>
<div className="stat-value">{stats.win_rate.toFixed(2)}%</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>
</div>
)}
{trades.length === 0 ? (
<div className="no-data">暂无交易记录</div>
) : (
<table className="trades-table">
<thead>
<tr>
<th>交易对</th>
<th>方向</th>
<th>数量</th>
<th>入场价</th>
<th>出场价</th>
<th>盈亏</th>
<th>状态</th>
<th>时间</th>
</tr>
</thead>
<tbody>
{trades.map(trade => (
<tr key={trade.id}>
<td>{trade.symbol}</td>
<td className={trade.side === 'BUY' ? 'buy' : 'sell'}>{trade.side}</td>
<td>{parseFloat(trade.quantity).toFixed(4)}</td>
<td>{parseFloat(trade.entry_price).toFixed(4)}</td>
<td>{trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}</td>
<td className={parseFloat(trade.pnl) >= 0 ? 'positive' : 'negative'}>
{parseFloat(trade.pnl).toFixed(2)} USDT
</td>
<td>
<span className={`status ${trade.status}`}>{trade.status}</span>
</td>
<td>{new Date(trade.entry_time).toLocaleString('zh-CN')}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
)
}
export default TradeList