From c95ec9bfdbe2adb50f4a2a3a12d1f17c5d9f0fc8 Mon Sep 17 00:00:00 2001 From: aszerW Date: Mon, 11 May 2026 23:10:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(report):=20=E4=BF=AE=E5=A4=8D=E6=8C=81?= =?UTF-8?q?=E4=BB=93=E6=94=B6=E7=9B=8A=E7=99=BE=E5=88=86=E5=8F=B7=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用apply+lambda统一处理百分号格式 - 添加列存在性检查,避免KeyError - 正确计算盈亏次数 --- .../report_generator/generate_report.py | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/visualization/report_generator/generate_report.py b/visualization/report_generator/generate_report.py index ea8cdd6..8d4379b 100644 --- a/visualization/report_generator/generate_report.py +++ b/visualization/report_generator/generate_report.py @@ -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 )