a
This commit is contained in:
parent
449ad01ede
commit
48f6ab4fea
|
|
@ -223,25 +223,38 @@ async def get_trade_stats(
|
|||
trades = Trade.get_all(start_timestamp, end_timestamp, symbol, None, account_id=account_id)
|
||||
closed_trades = [t for t in trades if t['status'] == 'closed']
|
||||
|
||||
# 辅助函数:计算净盈亏(优先使用 realized_pnl - commission)
|
||||
def get_net_pnl(t):
|
||||
pnl = float(t.get('pnl') or 0)
|
||||
realized = t.get('realized_pnl')
|
||||
if realized is not None:
|
||||
pnl = float(realized)
|
||||
commission = float(t.get('commission') or 0)
|
||||
commission_asset = t.get('commission_asset')
|
||||
# 如果手续费是 USDT,则扣除
|
||||
if commission_asset == 'USDT':
|
||||
pnl -= commission
|
||||
return pnl
|
||||
|
||||
# 排除0盈亏的订单(abs(pnl) < 0.01 USDT视为0盈亏),这些订单不应该影响胜率统计
|
||||
ZERO_PNL_THRESHOLD = 0.01 # 0.01 USDT的阈值,小于此值视为0盈亏
|
||||
meaningful_trades = [t for t in closed_trades if abs(float(t['pnl'])) >= ZERO_PNL_THRESHOLD]
|
||||
zero_pnl_trades = [t for t in closed_trades if abs(float(t['pnl'])) < ZERO_PNL_THRESHOLD]
|
||||
meaningful_trades = [t for t in closed_trades if abs(get_net_pnl(t)) >= ZERO_PNL_THRESHOLD]
|
||||
zero_pnl_trades = [t for t in closed_trades if abs(get_net_pnl(t)) < ZERO_PNL_THRESHOLD]
|
||||
|
||||
# 只统计有意义的交易(排除0盈亏)的胜率
|
||||
win_trades = [t for t in meaningful_trades if float(t['pnl']) > 0]
|
||||
loss_trades = [t for t in meaningful_trades if float(t['pnl']) < 0]
|
||||
win_trades = [t for t in meaningful_trades if get_net_pnl(t) > 0]
|
||||
loss_trades = [t for t in meaningful_trades if get_net_pnl(t) < 0]
|
||||
|
||||
# 盈利/亏损均值(用于观察是否接近 3:1)
|
||||
avg_win_pnl = sum(float(t["pnl"]) for t in win_trades) / len(win_trades) if win_trades else 0.0
|
||||
avg_win_pnl = sum(get_net_pnl(t) for t in win_trades) / len(win_trades) if win_trades else 0.0
|
||||
avg_loss_pnl_abs = (
|
||||
sum(abs(float(t["pnl"])) for t in loss_trades) / len(loss_trades) if loss_trades else 0.0
|
||||
sum(abs(get_net_pnl(t)) for t in loss_trades) / len(loss_trades) if loss_trades else 0.0
|
||||
)
|
||||
win_loss_ratio = (avg_win_pnl / avg_loss_pnl_abs) if avg_loss_pnl_abs > 0 else None
|
||||
|
||||
# 实际盈亏比(所有盈利单的总盈利 / 所有亏损单的总亏损,必须 > 1.5,目标 2.5-3.0)
|
||||
total_win_pnl = sum(float(t["pnl"]) for t in win_trades) if win_trades else 0.0
|
||||
total_loss_pnl_abs = sum(abs(float(t["pnl"])) for t in loss_trades) if loss_trades else 0.0
|
||||
total_win_pnl = sum(get_net_pnl(t) for t in win_trades) if win_trades else 0.0
|
||||
total_loss_pnl_abs = sum(abs(get_net_pnl(t)) for t in loss_trades) if loss_trades else 0.0
|
||||
actual_profit_loss_ratio = (total_win_pnl / total_loss_pnl_abs) if total_loss_pnl_abs > 0 else None
|
||||
|
||||
# 盈利因子(总盈利金额 / 总亏损金额,必须 > 1.1,目标 1.5+)
|
||||
|
|
@ -286,8 +299,8 @@ async def get_trade_stats(
|
|||
"win_trades": len(win_trades),
|
||||
"loss_trades": len(loss_trades),
|
||||
"win_rate": len(win_trades) / len(meaningful_trades) * 100 if meaningful_trades else 0, # 基于有意义的交易计算胜率
|
||||
"total_pnl": sum(float(t['pnl']) for t in closed_trades),
|
||||
"avg_pnl": sum(float(t['pnl']) for t in closed_trades) / len(closed_trades) if closed_trades else 0,
|
||||
"total_pnl": sum(get_net_pnl(t) for t in closed_trades),
|
||||
"avg_pnl": sum(get_net_pnl(t) for t in closed_trades) / len(closed_trades) if closed_trades else 0,
|
||||
# 额外统计:盈利单均值 vs 亏损单均值(绝对值)以及比值(目标 3:1)
|
||||
"avg_win_pnl": avg_win_pnl,
|
||||
"avg_loss_pnl_abs": avg_loss_pnl_abs,
|
||||
|
|
|
|||
|
|
@ -1151,17 +1151,7 @@ const GlobalConfig = () => {
|
|||
<section className="global-section system-section">
|
||||
<div className="system-header">
|
||||
<h3>系统控制</h3>
|
||||
<div className="system-status">
|
||||
<span className={`system-status-badge ${systemStatus?.running ? 'running' : 'stopped'}`}>
|
||||
交易系统 {systemStatus?.running ? '运行中' : '未运行'}
|
||||
</span>
|
||||
{systemStatus?.pid ? <span className="system-status-meta">PID: {systemStatus.pid}</span> : null}
|
||||
|
||||
<span className={`system-status-badge ${backendStatus?.running ? 'running' : 'stopped'}`} style={{ marginLeft: '10px' }}>
|
||||
后端服务 {backendStatus?.running ? '运行中' : '未知'}
|
||||
</span>
|
||||
{backendStatus?.pid ? <span className="system-status-meta">PID: {backendStatus.pid}</span> : null}
|
||||
</div>
|
||||
{/* 交易系统状态展示已移除 */}
|
||||
</div>
|
||||
|
||||
<div className="system-control-group">
|
||||
|
|
|
|||
|
|
@ -480,8 +480,20 @@ const TradeList = () => {
|
|||
? parseFloat(trade.margin_usdt)
|
||||
: (leverage > 0 ? notional / leverage : 0)
|
||||
|
||||
// 计算盈亏比例(盈亏/保证金)
|
||||
const pnl = parseFloat(trade.pnl || 0)
|
||||
// 计算盈亏(优先使用币安实际结算盈亏 realized_pnl - commission)
|
||||
let pnl = parseFloat(trade.pnl || 0)
|
||||
const realizedPnl = trade.realized_pnl !== undefined && trade.realized_pnl !== null ? parseFloat(trade.realized_pnl) : null
|
||||
const commission = trade.commission !== undefined && trade.commission !== null ? parseFloat(trade.commission) : 0
|
||||
const commissionAsset = trade.commission_asset || 'USDT'
|
||||
|
||||
if (realizedPnl !== null) {
|
||||
pnl = realizedPnl
|
||||
// 如果手续费是 USDT,则从盈亏中扣除(得到净值)
|
||||
if (commissionAsset === 'USDT') {
|
||||
pnl -= commission
|
||||
}
|
||||
}
|
||||
|
||||
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
|
||||
|
||||
// 格式化时间为北京时间
|
||||
|
|
@ -566,7 +578,20 @@ const TradeList = () => {
|
|||
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)
|
||||
|
||||
// 计算盈亏(优先使用币安实际结算盈亏 realized_pnl - commission)
|
||||
let pnl = parseFloat(trade.pnl || 0)
|
||||
const realizedPnl = trade.realized_pnl !== undefined && trade.realized_pnl !== null ? parseFloat(trade.realized_pnl) : null
|
||||
const commission = trade.commission !== undefined && trade.commission !== null ? parseFloat(trade.commission) : 0
|
||||
const commissionAsset = trade.commission_asset || 'USDT'
|
||||
|
||||
if (realizedPnl !== null) {
|
||||
pnl = realizedPnl
|
||||
if (commissionAsset === 'USDT') {
|
||||
pnl -= commission
|
||||
}
|
||||
}
|
||||
|
||||
const pnlPercent = margin > 0 ? (pnl / margin) * 100 : 0
|
||||
|
||||
// 格式化时间为北京时间
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user