fix(report): 修复持仓收益百分号格式转换

- 使用apply+lambda统一处理百分号格式
- 添加列存在性检查,避免KeyError
- 正确计算盈亏次数
This commit is contained in:
2026-05-11 23:10:31 +08:00
parent fc59836ec3
commit c95ec9bfdb

View File

@@ -94,13 +94,17 @@ class ReportGenerator:
avg_holding_days = df['持仓天数'].mean() if len(df) > 0 else 0
# 盈亏次数基于trades数据
# 转换持仓收益为数值
if df['持仓收益'].dtype == 'object':
returns_num = df['持仓收益'].str.rstrip('%').astype(float)
# 转换持仓收益为数值(统一处理百分号格式)
if '持仓收益' in df.columns:
# 使用通用转换方法
returns_series = df['持仓收益'].apply(
lambda x: float(str(x).rstrip('%')) if pd.notna(x) else 0.0
)
win_count = (returns_series > 0).sum()
loss_count = (returns_series < 0).sum()
else:
returns_num = df['持仓收益']
win_count = (returns_num > 0).sum()
loss_count = len(df) - win_count
win_count = 0
loss_count = 0
return {
'total_return': f"{strategy_metrics['累计收益'] * 100:.2f}",
@@ -108,7 +112,6 @@ class ReportGenerator:
'win_rate': f"{strategy_metrics['日胜率'] * 100:.2f}",
'max_drawdown': f"{strategy_metrics['最大回撤'] * 100:.2f}",
'sharpe_ratio': f"{strategy_metrics['夏普比率']:.2f}",
'total_trades': str(total_trades),
'best_symbol': best_symbol,
'avg_holding_days': f"{avg_holding_days:.1f}",
'win_count': int(win_count),
@@ -215,12 +218,11 @@ class ReportGenerator:
# 盈亏分布 - 从trades数据计算
df = trades_filtered if trades_filtered is not None else self.trades_df
if df['持仓收益'].dtype == 'object':
df = df.copy()
df['持仓收益_num'] = df['持仓收益'].str.rstrip('%').astype(float)
else:
df = df.copy()
df['持仓收益_num'] = df['持仓收益']
df = df.copy()
# 使用通用转换方法处理持仓收益
df['持仓收益_num'] = df['持仓收益'].apply(
lambda x: float(str(x).rstrip('%')) if pd.notna(x) else 0.0
)
positive_returns = df[df['持仓收益_num'] > 0]['持仓收益_num'].tolist()
negative_returns = df[df['持仓收益_num'] <= 0]['持仓收益_num'].tolist()
@@ -255,13 +257,11 @@ class ReportGenerator:
print("⚠️ 未找到净值曲线,重新计算...")
df = trades_filtered if trades_filtered is not None else self.trades_df
# 转换持仓收益为数值
if df['持仓收益'].dtype == 'object':
df = df.copy()
df['持仓收益_num'] = df['持仓收益'].str.rstrip('%').astype(float)
else:
df = df.copy()
df['持仓收益_num'] = df['持仓收益']
# 转换持仓收益为数值(统一处理百分号格式)
df = df.copy()
df['持仓收益_num'] = df['持仓收益'].apply(
lambda x: float(str(x).rstrip('%')) if pd.notna(x) else 0.0
)
df_sorted = df.sort_values('出场日期')
@@ -348,12 +348,17 @@ class ReportGenerator:
kpis = self.calculate_kpis(trades_filtered)
chart_data = self.prepare_chart_data(trades_filtered)
# 准备交易记录
trades_display = trades_filtered.copy()
# 准备交易记录 - 按出场日期倒序排列(最新在前)
trades_display = trades_filtered.sort_values('出场日期', ascending=False).copy()
trades_display['进场日期'] = trades_display['进场日期'].dt.strftime('%Y-%m-%d')
trades_display['出场日期'] = trades_display['出场日期'].dt.strftime('%Y-%m-%d')
trades_list = trades_display.to_dict('records')
# 分页参数
page_size = 50 # 每页显示50条记录
total_trades = len(trades_list)
total_pages = (total_trades // page_size) + (1 if total_trades % page_size > 0 else 0)
# 读取模板
template_path = os.path.join(os.path.dirname(__file__), 'template.html')
with open(template_path, 'r', encoding='utf-8') as f:
@@ -365,6 +370,9 @@ class ReportGenerator:
start_date=start_date.strftime('%Y-%m-%d') if start_date else trades_filtered['出场日期'].min().strftime('%Y-%m-%d'),
end_date=end_date.strftime('%Y-%m-%d') if end_date else trades_filtered['出场日期'].max().strftime('%Y-%m-%d'),
trades=trades_list,
page_size=page_size,
total_trades=total_trades,
total_pages=total_pages,
**kpis,
**chart_data
)