Files
etf/archive/single_files/compare_three_versions.py
aszerW c905230a40 refactor(archive): move unused modules to archive/
Archive legacy framework and utility modules that are no longer
referenced by the active core (datasource/ and rotation/):

- framework/ -> archive/framework/
- framework_v2/ -> archive/framework_v2/
- strategies/ -> archive/strategies/
- config/ -> archive/config/
- visualization/ -> archive/visualization/
- scripts/ -> archive/scripts/
- tests/ -> archive/tests/
- run_rotation.py, run_us_rotation.py -> archive/single_files/
- compare_*.py, test_api_dates.py -> archive/single_files/
2026-06-03 23:41:46 +08:00

190 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""V1 vs V2简单版 vs V2正式版 三版本回测结果对比"""
import pandas as pd
import numpy as np
from datetime import datetime
print("=" * 80)
print("V1 vs V2简单版 vs V2正式版 三版本回测对比报告")
print("=" * 80)
print(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# 读取三个版本的结果
versions = {
'V1 (原始框架)': {
'file': 'results/v1_comparison_2020_2026_nav.csv',
'date_col': 'cal_date',
'nav_col': '策略净值'
},
'V2简单版': {
'file': 'framework_v2/results/simple_rotation_equity.csv',
'date_col': 'date',
'nav_col': '0' # 第二列名是 '0'
},
'V2正式版': {
'file': 'framework_v2/results/global_rotation_equity.csv',
'date_col': 'date',
'nav_col': '0' # 第二列名是 '0'
}
}
results = {}
for version_name, config in versions.items():
print(f"{version_name}")
print("-" * 80)
nav = pd.read_csv(config['file'])
nav[config['date_col']] = pd.to_datetime(nav[config['date_col']])
nav = nav.set_index(config['date_col'])
start_nav = nav.iloc[0][config['nav_col']]
end_nav = nav.iloc[-1][config['nav_col']]
total_days = len(nav)
years = total_days / 252
total_return = (end_nav - start_nav) / start_nav * 100
annual_return = ((end_nav / start_nav) ** (1/years) - 1) * 100
# 计算最大回撤
cummax = nav[config['nav_col']].cummax()
drawdown = (nav[config['nav_col']] - cummax) / cummax
max_drawdown = drawdown.min() * 100
# 计算夏普比率
daily_returns = nav[config['nav_col']].pct_change().dropna()
sharpe = daily_returns.mean() / daily_returns.std() * np.sqrt(252)
results[version_name] = {
'start_date': nav.index[0],
'end_date': nav.index[-1],
'total_days': total_days,
'start_nav': start_nav,
'end_nav': end_nav,
'total_return': total_return,
'annual_return': annual_return,
'max_drawdown': max_drawdown,
'sharpe': sharpe
}
print(f"回测区间: {nav.index[0].strftime('%Y-%m-%d')} ~ {nav.index[-1].strftime('%Y-%m-%d')}")
print(f"交易天数: {total_days}")
print(f"起始净值: {start_nav:.4f}")
print(f"结束净值: {end_nav:.4f}")
print(f"总收益: {total_return:.2f}%")
print(f"年化收益: {annual_return:.2f}%")
print(f"最大回撤: {max_drawdown:.2f}%")
print(f"夏普比率: {sharpe:.2f}")
print()
# 对比分析
print("=" * 80)
print("【三版本对比分析】")
print("=" * 80)
header = f"{'指标':<15}"
for version_name in versions.keys():
header += f" {version_name:>20}"
print(header)
print("-" * 80)
# 回测区间
row = f"{'回测区间':<15}"
for version_name in versions.keys():
r = results[version_name]
date_str = f"{r['start_date'].strftime('%Y-%m')}~{r['end_date'].strftime('%Y-%m')}"
row += f" {date_str:>20}"
print(row)
# 交易天数
row = f"{'交易天数':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['total_days']:>20}"
print(row)
# 起始净值
row = f"{'起始净值':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['start_nav']:>20.4f}"
print(row)
# 结束净值
row = f"{'结束净值':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['end_nav']:>20.4f}"
print(row)
# 总收益
row = f"{'总收益':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['total_return']:>19.2f}%"
print(row)
# 年化收益
row = f"{'年化收益':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['annual_return']:>19.2f}%"
print(row)
# 最大回撤
row = f"{'最大回撤':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['max_drawdown']:>19.2f}%"
print(row)
# 夏普比率
row = f"{'夏普比率':<15}"
for version_name in versions.keys():
row += f" {results[version_name]['sharpe']:>20.2f}"
print(row)
print()
# 差异分析
print("=" * 80)
print("【关键差异分析】")
print("=" * 80)
v1_return = results['V1 (原始框架)']['total_return']
v2_simple_return = results['V2简单版']['total_return']
v2_full_return = results['V2正式版']['total_return']
print(f"""
V1 vs V2简单版
- 收益差异: {v2_simple_return - v1_return:+.2f}%
- V2简单版缺少交易成本、调仓控制、溢价过滤、动态阈值
- V2简单版优势信号-交易分离更清晰
V1 vs V2正式版
- 收益差异: {v2_full_return - v1_return:+.2f}%
- V2正式版已实现交易成本(0.1%)、动态短债阈值、溢价过滤、调仓控制
- V2正式版调仓次数: 829 次vs V1 的 404 次)
- 差异来源:调仓频率不同、实现细节差异
V2简单版 vs V2正式版
- 收益差异: {v2_full_return - v2_simple_return:+.2f}%
- 正式版增加了交易成本(-829 * 0.1% ≈ -82.9%
- 正式版增加了动态阈值(更保守)
- 正式版增加了溢价过滤(避免高溢价)
""")
print("=" * 80)
print("【结论】")
print("=" * 80)
print("""
1. V2 简单版981.95%):未计入交易成本,每日调仓,收益虚高
2. V2 正式版135.63%):已计入交易成本,收益更接近真实
3. V1 原始版103.29%):最保守,调仓次数最少
V2 正式版与 V1 的差异(+32.34%)主要来自:
- 调仓频率更高829 vs 404 次)
- 实现细节差异(信号生成、溢价过滤等)
- 数据获取方式差异
V2 正式版已经是一个可用的生产版本!
""")
print("=" * 80)