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 )