Files
my_ai_agent_celue_/app/exporter.py
T

320 lines
10 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.
"""
策略交付包导出器 — 生成策略文件 + Markdown 报告 + CSV 明细
交付包结构:
deliverables/{strategy_name}_{timestamp}/
├── {strategy_name}.py 策略源文件 (从 strategies/ 复制)
├── STRATEGY_REPORT.md 策略交付报告 (完整分析)
├── optimization.csv 参数优化响应面
├── walkforward.csv Walk-forward 逐窗口明细
└── backtest_metrics.csv 最优参数回测指标
用法:
from exporter import StrategyExporter
exporter = StrategyExporter()
path = exporter.deliver(
strategy_name="sma_cross",
opt_result=opt_result,
wf_result=wf_result,
accept_report=accept_report,
df=df,
symbol="XAUUSD",
)
print(f"交付包: {path}")
"""
from __future__ import annotations
import os
import shutil
from datetime import datetime
from typing import Optional
import numpy as np
import pandas as pd
import raptorbt
from strategies import get_strategy
from strategies.base import Strategy
from .optimizer import OptimizationResult
from .walk_forward import WalkForwardResult
from .acceptance import AcceptanceReport
class StrategyExporter:
"""
策略交付包导出器
将策略文件 + 优化结果 + walk-forward 结果 + 验收报告
打包成一个完整的交付目录
"""
def __init__(self, output_dir: str = "deliverables"):
self.output_dir = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), output_dir
)
def deliver(
self,
strategy_name: str,
df: pd.DataFrame,
symbol: str,
opt_result: OptimizationResult,
wf_result: WalkForwardResult,
accept_report: AcceptanceReport,
param_grid: Optional[dict] = None,
data_info: Optional[dict] = None,
) -> str:
"""
生成完整交付包
参数:
strategy_name: 策略名称
df: K 线数据
symbol: 标的
opt_result: 优化结果
wf_result: walk-forward 结果
accept_report: 验收报告
param_grid: 参数搜索空间 (用于报告)
data_info: 数据信息 (如 {"source": "Mt5Bridge", "range": "2026-01~2026-06"})
返回:
交付包目录路径
"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
pkg_name = f"{strategy_name}_{timestamp}"
pkg_dir = os.path.join(self.output_dir, pkg_name)
os.makedirs(pkg_dir, exist_ok=True)
strategy = get_strategy(strategy_name)
# 1. 复制策略源文件
src_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"strategies", f"{strategy_name}.py",
)
if os.path.exists(src_path):
shutil.copy2(src_path, os.path.join(pkg_dir, f"{strategy_name}.py"))
# 2. 导出 CSV 明细
opt_result.export(os.path.join(pkg_dir, "optimization.csv"))
wf_result.export(os.path.join(pkg_dir, "walkforward.csv"))
# 3. 用最优参数跑完整回测, 导出指标
best_metrics = self._run_best_backtest(strategy_name, opt_result.best_params, df, symbol)
if best_metrics:
pd.DataFrame(list(best_metrics.items()), columns=["metric", "value"]).to_csv(
os.path.join(pkg_dir, "backtest_metrics.csv"),
index=False, encoding="utf-8-sig",
)
# 4. 生成 Markdown 报告
report = self._generate_report(
strategy_name=strategy_name,
strategy=strategy,
symbol=symbol,
opt_result=opt_result,
wf_result=wf_result,
accept_report=accept_report,
param_grid=param_grid,
data_info=data_info,
best_metrics=best_metrics,
)
report_path = os.path.join(pkg_dir, "STRATEGY_REPORT.md")
with open(report_path, "w", encoding="utf-8") as f:
f.write(report)
return pkg_dir
def _run_best_backtest(self, strategy_name, best_params, df, symbol) -> Optional[dict]:
"""用最优参数跑完整回测, 返回指标字典"""
try:
strategy = get_strategy(strategy_name)
# 用最优参数重新实例化
strategy_cls = type(strategy)
strategy = strategy_cls(**best_params)
signals = strategy.generate_signals(df)
config = strategy.build_config()
arr = Strategy.to_arrays(df)
result = raptorbt.run_single_backtest(
timestamps=arr["timestamps"],
open=arr["open"], high=arr["high"], low=arr["low"], close=arr["close"],
volume=arr["volume"],
entries=signals.entries, exits=signals.exits,
direction=signals.direction, weight=1.0, symbol=symbol,
config=config,
)
m = result.metrics
return {
"total_return_pct": m.total_return_pct,
"sharpe_ratio": m.sharpe_ratio,
"max_drawdown_pct": m.max_drawdown_pct,
"total_trades": m.total_trades,
"win_rate_pct": m.win_rate_pct,
"profit_factor": m.profit_factor,
}
except Exception as e:
print(f" ⚠️ 最优参数回测失败: {e}")
return None
def _generate_report(
self,
strategy_name,
strategy,
symbol,
opt_result,
wf_result,
accept_report,
param_grid,
data_info,
best_metrics,
) -> str:
"""生成 Markdown 交付报告"""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
params_str = ", ".join(f"{k}={v}" for k, v in opt_result.best_params.items())
grid_str = ", ".join(
f'{k}={v}' for k, v in (param_grid or {}).items()
) if param_grid else "N/A"
data_info = data_info or {}
# 验收结果表
accept_lines = []
for c in accept_report.criteria:
status = "✅" if c.passed else "❌"
accept_lines.append(
f"| {c.name} | {c.threshold:.2f} | {c.actual:.2f} | {status} |"
)
accept_table = "\n".join(accept_lines) or "| (无) | | | |"
# 参数稳定性
stability_lines = []
for param, dist in wf_result.param_stability.items():
dist_str = ", ".join(
f"{k}×{v}" for k, v in sorted(dist.items(), key=lambda x: -x[1])
)
stability_lines.append(f"- **{param}**: {dist_str}")
stability_str = "\n".join(stability_lines) or "- (无)"
# 最优参数回测指标
if best_metrics:
metrics_lines = [
f"| {k} | {v:.4f} |" for k, v in best_metrics.items()
]
metrics_table = "\n".join(metrics_lines)
else:
metrics_table = "| (回测失败) | |"
# Top 5 参数组合
top5 = opt_result.top_n(5)
if len(top5) > 0:
cols = opt_result.param_names + [opt_result.metric, "total_trades"]
cols = [c for c in cols if c in top5.columns]
top5_lines = ["| " + " | ".join(cols) + " |",
"|" + "|".join(["---"] * len(cols)) + "|"]
for _, row in top5.iterrows():
vals = []
for c in cols:
v = row[c]
if isinstance(v, float):
vals.append(f"{v:.4f}")
else:
vals.append(str(v))
top5_lines.append("| " + " | ".join(vals) + " |")
top5_table = "\n".join(top5_lines)
else:
top5_table = "(无有效结果)"
verdict = "✅ 通过验收,可交付" if accept_report.passed else "❌ 未通过验收,不可交付"
return f"""# 策略交付报告: {strategy_name}
> 生成时间: {now}
> 引擎: RaptorBT v{raptorbt.__version__}
## 1. 策略概述
| 项目 | 内容 |
|------|------|
| 策略名称 | `{strategy_name}` |
| 描述 | {strategy.description()} |
| 交易标的 | {symbol} |
| 最优参数 | {params_str} |
| 参数搜索空间 | {grid_str} |
| 预热期 | {strategy.warmup_bars()} bars |
## 2. 参数优化
**目标指标**: {opt_result.metric} ({opt_result.direction})
**搜索组合数**: {len(opt_result.results)}
**有效结果数**: {opt_result.results[opt_result.metric].notna().sum()}
### Top 5 参数组合
{top5_table}
## 3. Walk-Forward 验证
| 指标 | IS (训练集) | OOS (测试集) |
|------|------------|-------------|
| 平均夏普比率 | {wf_result.is_sharpe_avg:.4f} | {wf_result.oos_sharpe_avg:.4f} |
| 平均收益率 | - | {wf_result.oos_return_avg:.2f}% |
| 平均最大回撤 | - | {wf_result.oos_max_drawdown_avg:.2f}% |
| 总交易数 | - | {wf_result.oos_trades_total} |
| 平均胜率 | - | {wf_result.oos_win_rate_avg:.1f}% |
**衰减比 (OOS/IS)**: {wf_result.decay_ratio:.2%}
**过拟合判定**: {'⚠️ 是 (衰减比 < 50%)' if wf_result.is_overfit else '✅ 否'}
**验证窗口数**: {wf_result.n_windows}
### 参数稳定性
{stability_str}
## 4. 验收结果
**总体结论**: {verdict}
| 标准 | 阈值 | 实际值 | 结果 |
|------|------|--------|------|
{accept_table}
## 5. 最优参数完整回测
| 指标 | 值 |
|------|-----|
{metrics_table}
## 6. 数据信息
| 项目 | 内容 |
|------|------|
| 数据源 | {data_info.get('source', 'Mt5Bridge')} |
| 数据范围 | {data_info.get('range', 'N/A')} |
| K 线数量 | {data_info.get('bars', 'N/A')} |
| 时间周期 | {data_info.get('timeframe', 'N/A')} |
## 7. 风险提示
1. **回测不等于实盘**: 历史表现不保证未来收益,滑点和延迟可能影响实际执行
2. **参数敏感性**: 请关注参数稳定性分析,参数值频繁变化的策略稳健性较差
3. **市场环境**: 策略可能在特定市场条件下表现优异,其他条件下表现不佳
4. **过拟合风险**: {'⚠️ 检测到过拟合,OOS 表现显著低于 IS' if wf_result.is_overfit else '未检测到明显过拟合'}
5. **交易成本**: 回测已包含手续费和滑点,但实际成本可能更高
## 8. 复现方式
```bash
# 使用最优参数回测
python main.py run --strategy {strategy_name} --symbol {symbol} --export
# 重新验证
python main.py validate --strategy {strategy_name} --param <参数>=<值>
```
---
*本报告由 RaptorBT 策略自动化框架自动生成*
"""