+
整体统计
+
总交易数:{stats.total_trades}
+
胜率:{stats.win_rate.toFixed(2)}%
+
总盈亏:{stats.total_pnl.toFixed(2)} USDT
+
平均盈亏:{stats.avg_pnl.toFixed(2)} USDT
+
平均持仓时长(分钟):{stats.avg_duration_minutes ? Number(stats.avg_duration_minutes).toFixed(0) : 0}
+
平仓原因(有意义交易):
+
{(() => {
const m = stats.exit_reason_counts || {}
const stopLoss = Number(m.stop_loss || 0)
@@ -339,267 +268,340 @@ const TradeList = () => {
})()}
+
平均盈利 / 平均亏损(期望 3:1):{Number(stats.avg_win_loss_ratio || 0).toFixed(2)} : 1
+
总交易量(名义):{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT
+
+
+
总交易数
+
{stats.total_trades}
+
+ {stats.meaningful_trades !== undefined && (
+ <>(有意义: {stats.meaningful_trades},0盈亏: {stats.zero_pnl_trades || 0})>
)}
- {"avg_win_pnl" in stats && "avg_loss_pnl_abs" in stats && Number(stats.total_pnl || 0) > 0 && (
-
-
平均盈利 / 平均亏损(期望 3:1)
-
= 3 ? 'positive' : ''
- }`}
- >
- {typeof stats.avg_win_loss_ratio === 'number'
- ? `${stats.avg_win_loss_ratio.toFixed(2)} : 1`
- : '—'}
-
-
- +{Number(stats.avg_win_pnl || 0).toFixed(2)} / -{Number(stats.avg_loss_pnl_abs || 0).toFixed(2)} USDT
-
-
- )}
- {"total_notional_usdt" in stats && (
-
-
总交易量(名义)
-
{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT
-
- (口径:入场价×数量)
-
-
- )}
+ {stats.meaningful_trades === undefined && <>(已平仓的完整交易)>}
+
+
+
+
胜率
+
{stats.win_rate.toFixed(2)}%
+
+ {stats.meaningful_trades !== undefined && <>(已排除0盈亏订单)>}
+
+
+
+
总盈亏
+
= 0 ? 'positive' : 'negative'}`}>
+ {stats.total_pnl.toFixed(2)} USDT
+
+
+
+
平均盈亏
+
= 0 ? 'positive' : 'negative'}`}>
+ {stats.avg_pnl.toFixed(2)} USDT
+
+
+ {"avg_duration_minutes" in stats && stats.avg_duration_minutes !== null && stats.avg_duration_minutes !== undefined && (
+
+
平均持仓时长(分钟)
+
{Number(stats.avg_duration_minutes || 0).toFixed(0)}
+
+ (仅统计“有意义交易”;优先使用 duration_minutes,缺失时用 exit_time-entry_time 计算)
+
)}
-
- {trades.length === 0 ? (
-
暂无交易记录
- ) : (
- <>
- {/* 桌面端表格 */}
-
-
-
- | 交易ID |
- 交易对 |
- 方向 |
- 数量 |
- 名义 |
- 保证金 |
- 入场价 |
- 出场价 |
- 盈亏 |
- 盈亏比例 |
- 状态 |
- 平仓类型 |
- 币安订单号 |
- 入场时间 |
- 平仓时间 |
-
-
-
- {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 (
-
- | #{trade.id} |
- {trade.symbol} |
- {trade.side} |
- {parseFloat(trade.quantity).toFixed(4)} |
- {notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT |
- {margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT |
- {parseFloat(trade.entry_price).toFixed(4)} |
- {trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'} |
- = 0 ? 'positive' : 'negative'}>
- {pnl.toFixed(2)} USDT
- |
- = 0 ? 'positive' : 'negative'}>
- {pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
- |
-
- {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
- |
- {trade.exit_reason_display || '-'} |
- {formatOrderIds()} |
- {formatTime(trade.entry_time)} |
- {trade.exit_time ? formatTime(trade.exit_time) : '-'} |
-
- )
- })}
-
-
-
- {/* 移动端卡片 */}
-
- {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 (
-
-
- {trade.symbol}
- 交易ID: #{trade.id}
-
- {trade.side === 'BUY' ? '买入' : '卖出'} · {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
-
-
-
-
- 数量
- {parseFloat(trade.quantity).toFixed(4)}
-
-
- 名义
- {notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT
-
-
- 保证金
- {margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT
-
-
- 入场价
- {parseFloat(trade.entry_price).toFixed(4)}
-
-
- 出场价
- {trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}
-
-
- 盈亏
- = 0 ? 'positive' : 'negative'}`}>
- {pnl.toFixed(2)} USDT
-
-
-
- 盈亏比例
- = 0 ? 'positive' : 'negative'}`}>
- {pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
-
-
- {trade.exit_reason_display && (
-
- 平仓类型
- {trade.exit_reason_display}
-
- )}
- {(trade.entry_order_id || trade.exit_order_id) && (
-
- 币安订单号
-
- {trade.entry_order_id ? `开仓: ${trade.entry_order_id}` : ''}
- {trade.entry_order_id && trade.exit_order_id ? ' / ' : ''}
- {trade.exit_order_id ? `平仓: ${trade.exit_order_id}` : ''}
-
-
- )}
-
-
-
- 入场:
- {formatTime(trade.entry_time)}
-
- {trade.exit_time && (
-
- 平仓:
- {formatTime(trade.exit_time)}
-
- )}
-
-
- )
- })}
+ {"exit_reason_counts" in stats && stats.exit_reason_counts && (
+
+
平仓原因(有意义交易)
+
+ {(() => {
+ 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(' / ') : '—'
+ })()}
- >
+
+ )}
+ {"avg_win_pnl" in stats && "avg_loss_pnl_abs" in stats && Number(stats.total_pnl || 0) > 0 && (
+
+
平均盈利 / 平均亏损(期望 3:1)
+
= 3 ? 'positive' : ''
+ }`}
+ >
+ {typeof stats.avg_win_loss_ratio === 'number'
+ ? `${stats.avg_win_loss_ratio.toFixed(2)} : 1`
+ : '—'}
+
+
+ +{Number(stats.avg_win_pnl || 0).toFixed(2)} / -{Number(stats.avg_loss_pnl_abs || 0).toFixed(2)} USDT
+
+
+ )}
+ {"total_notional_usdt" in stats && (
+
+
总交易量(名义)
+
{Number(stats.total_notional_usdt || 0).toFixed(2)} USDT
+
+ (口径:入场价×数量)
+
+
)}
)
}
+{
+ trades.length === 0 ? (
+
暂无交易记录
+ ) : (
+ <>
+ {/* 桌面端表格 */}
+
+
+
+ | 交易ID |
+ 交易对 |
+ 方向 |
+ 数量 |
+ 名义 |
+ 保证金 |
+ 入场价 |
+ 出场价 |
+ 盈亏 |
+ 盈亏比例 |
+ 状态 |
+ 平仓类型 |
+ 币安订单号 |
+ 入场时间 |
+ 平仓时间 |
+
+
+
+ {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 (
+
+ | #{trade.id} |
+ {trade.symbol} |
+ {trade.side} |
+ {parseFloat(trade.quantity).toFixed(4)} |
+ {notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT |
+ {margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT |
+ {parseFloat(trade.entry_price).toFixed(4)} |
+ {trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'} |
+ = 0 ? 'positive' : 'negative'}>
+ {pnl.toFixed(2)} USDT
+ |
+ = 0 ? 'positive' : 'negative'}>
+ {pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
+ |
+
+ {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
+ |
+ {trade.exit_reason_display || '-'} |
+ {formatOrderIds()} |
+ {formatTime(trade.entry_time)} |
+ {trade.exit_time ? formatTime(trade.exit_time) : '-'} |
+
+ )
+ })}
+
+
+
+ {/* 移动端卡片 */}
+
+ {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 (
+
+
+ {trade.symbol}
+ 交易ID: #{trade.id}
+
+ {trade.side === 'BUY' ? '买入' : '卖出'} · {trade.status === 'open' ? '持仓中' : trade.status === 'closed' ? '已平仓' : '已取消'}
+
+
+
+
+ 数量
+ {parseFloat(trade.quantity).toFixed(4)}
+
+
+ 名义
+ {notional >= 0.01 ? notional.toFixed(2) : notional.toFixed(4)} USDT
+
+
+ 保证金
+ {margin >= 0.01 ? margin.toFixed(2) : margin.toFixed(4)} USDT
+
+
+ 入场价
+ {parseFloat(trade.entry_price).toFixed(4)}
+
+
+ 出场价
+ {trade.exit_price ? parseFloat(trade.exit_price).toFixed(4) : '-'}
+
+
+ 盈亏
+ = 0 ? 'positive' : 'negative'}`}>
+ {pnl.toFixed(2)} USDT
+
+
+
+ 盈亏比例
+ = 0 ? 'positive' : 'negative'}`}>
+ {pnlPercent >= 0 ? '+' : ''}{pnlPercent.toFixed(2)}%
+
+
+ {trade.exit_reason_display && (
+
+ 平仓类型
+ {trade.exit_reason_display}
+
+ )}
+ {(trade.entry_order_id || trade.exit_order_id) && (
+
+ 币安订单号
+
+ {trade.entry_order_id ? `开仓: ${trade.entry_order_id}` : ''}
+ {trade.entry_order_id && trade.exit_order_id ? ' / ' : ''}
+ {trade.exit_order_id ? `平仓: ${trade.exit_order_id}` : ''}
+
+
+ )}
+
+
+
+ 入场:
+ {formatTime(trade.entry_time)}
+
+ {trade.exit_time && (
+
+ 平仓:
+ {formatTime(trade.exit_time)}
+
+ )}
+
+
+ )
+ })}
+
+ >
+)
+}
+