fix(report): 修复持仓收益百分号格式转换
- 使用apply+lambda统一处理百分号格式 - 添加列存在性检查,避免KeyError - 正确计算盈亏次数
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user