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/
This commit is contained in:
158
archive/framework_v2/scripts/convert_to_viewer_csv.py
Normal file
158
archive/framework_v2/scripts/convert_to_viewer_csv.py
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将 V2 回测细节 JSON 转换为 HTML viewer 需要的 CSV 格式。
|
||||
|
||||
用途:
|
||||
- 适配 backtest_viewer.html 的 CSV 格式要求
|
||||
- 生成 _detail.csv、_close.csv、_holdings.csv、_code_info.csv
|
||||
|
||||
用法:
|
||||
python framework_v2/scripts/convert_to_viewer_csv.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
|
||||
def safe_float(val, decimals=4):
|
||||
"""安全转换浮点数"""
|
||||
if val is None:
|
||||
return None
|
||||
try:
|
||||
return round(float(val), decimals)
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def convert_v2_to_viewer_csv(json_path=None):
|
||||
"""将 V2 JSON 转换为 V1 CSV 格式"""
|
||||
|
||||
# 1. 加载 JSON
|
||||
if json_path is None:
|
||||
json_path = project_root / 'framework_v2' / 'results' / 'backtest_detail_v2.json'
|
||||
|
||||
print(f"加载 V2 JSON: {json_path}")
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
meta = data['meta']
|
||||
days = data['days']
|
||||
|
||||
print(f" 天数: {len(days)}")
|
||||
print(f" 标的: {len(meta['codes'])}")
|
||||
|
||||
# 2. 生成 _detail.csv
|
||||
print("\n生成 _detail.csv...")
|
||||
detail_rows = []
|
||||
|
||||
for day in days:
|
||||
row = {
|
||||
'日期': day['date'],
|
||||
'净值': day['nav'],
|
||||
'日收益': day['daily_return'],
|
||||
'持仓标的': ','.join(day['holdings']) if day['holdings'] else ''
|
||||
}
|
||||
|
||||
# 添加每个标的的动量
|
||||
for code, asset in day['assets'].items():
|
||||
row[f'动量_{code}'] = asset.get('momentum')
|
||||
|
||||
detail_rows.append(row)
|
||||
|
||||
detail_df = pd.DataFrame(detail_rows)
|
||||
detail_path = json_path.parent / 'backtest_detail_v2_detail.csv'
|
||||
detail_df.to_csv(detail_path, index=False, encoding='utf-8-sig')
|
||||
print(f" ✅ {detail_path.name} ({len(detail_df)} 行)")
|
||||
|
||||
# 3. 生成 _close.csv
|
||||
print("\n生成 _close.csv...")
|
||||
close_rows = []
|
||||
|
||||
for day in days:
|
||||
row = {'日期': day['date']}
|
||||
|
||||
for code, asset in day['assets'].items():
|
||||
# ETF 收盘价
|
||||
etf_close = asset.get('etf_close')
|
||||
row[f'收盘价_{code}'] = etf_close
|
||||
|
||||
close_rows.append(row)
|
||||
|
||||
close_df = pd.DataFrame(close_rows)
|
||||
close_path = json_path.parent / 'backtest_detail_v2_close.csv'
|
||||
close_df.to_csv(close_path, index=False, encoding='utf-8-sig')
|
||||
print(f" ✅ {close_path.name} ({len(close_df)} 行)")
|
||||
|
||||
# 4. 生成 _holdings.csv
|
||||
print("\n生成 _holdings.csv...")
|
||||
holdings_rows = []
|
||||
|
||||
for day in days:
|
||||
if not day['holdings']:
|
||||
continue
|
||||
|
||||
for code in day['holdings']:
|
||||
asset = day['assets'].get(code, {})
|
||||
|
||||
code_meta = meta['codes'].get(code, {})
|
||||
|
||||
row = {
|
||||
'日期': day['date'],
|
||||
'标的代码': code,
|
||||
'标的名称': code_meta.get('name', code),
|
||||
'大类': code_meta.get('group', ''),
|
||||
'进场日期': asset.get('entry_date'),
|
||||
'进场价': asset.get('entry_price'),
|
||||
'当前价': asset.get('etf_close'),
|
||||
'持仓天数': asset.get('holding_days', 0),
|
||||
'持仓比例': asset.get('weight', 0),
|
||||
'累计收益': asset.get('cum_return')
|
||||
}
|
||||
|
||||
holdings_rows.append(row)
|
||||
|
||||
holdings_df = pd.DataFrame(holdings_rows)
|
||||
holdings_path = json_path.parent / 'backtest_detail_v2_holdings.csv'
|
||||
holdings_df.to_csv(holdings_path, index=False, encoding='utf-8-sig')
|
||||
print(f" ✅ {holdings_path.name} ({len(holdings_df)} 行)")
|
||||
|
||||
# 5. 生成 _code_info.csv
|
||||
print("\n生成 _code_info.csv...")
|
||||
code_info_rows = []
|
||||
|
||||
for code, meta_info in meta['codes'].items():
|
||||
row = {
|
||||
'代码': code,
|
||||
'名称': meta_info.get('name', code),
|
||||
'大类': meta_info.get('group', '')
|
||||
}
|
||||
code_info_rows.append(row)
|
||||
|
||||
code_info_df = pd.DataFrame(code_info_rows)
|
||||
code_info_path = json_path.parent / 'backtest_detail_v2_code_info.csv'
|
||||
code_info_df.to_csv(code_info_path, index=False, encoding='utf-8-sig')
|
||||
print(f" ✅ {code_info_path.name} ({len(code_info_df)} 行)")
|
||||
|
||||
# 6. 汇总
|
||||
print("\n" + "=" * 80)
|
||||
print("转换完成!")
|
||||
print("=" * 80)
|
||||
print(f"\n输出文件:")
|
||||
print(f" {detail_path.name}")
|
||||
print(f" {close_path.name}")
|
||||
print(f" {holdings_path.name}")
|
||||
print(f" {code_info_path.name}")
|
||||
print(f"\n使用方法:")
|
||||
print(f" 1. 打开 visualization/backtest_viewer.html")
|
||||
print(f" 2. 选择 'V2 GlobalRotationStrategy (最新)'")
|
||||
print(f" 3. 自动加载 CSV 文件")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
convert_v2_to_viewer_csv()
|
||||
Reference in New Issue
Block a user