"""策略→提示词版本分析引擎 核心功能:将策略评估结果按提示词版本聚合,计算每个版本的准确率。 """ import json from pathlib import Path from datetime import datetime from typing import Optional from .tracking import load_associations, get_associations_for_prompt_version from .registry import get_prompt, get_version_history PROJECT_DIR = Path("/home/hmo/projects/MoFin") DECISIONS_PATH = PROJECT_DIR / "data" / "decisions.json" ACCURACY_PATH = PROJECT_DIR / "data" / "accuracy_stats.json" EVAL_PATH = PROJECT_DIR / "data" / "evaluation.json" def _load_json(path, default=None): try: with open(path, encoding="utf-8") as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return {} if default is None else default def analyze_prompt_version_effectiveness() -> dict: """按提示词版本聚合策略评估结果 关联 decisions.json 中的 evaluation 字段和 associations.json 中的版本记录, 计算出每个提示词版本的: - 生成的策略总数 - 达到止盈数(成功) - 跌破止损数(失败) - 待验证数 - 盈亏比平均值 """ # 加载数据 decisions = _load_json(DECISIONS_PATH, {"decisions": []}) associations = load_associations().get("associations", []) # 建立 code → 最新关联的映射 code_to_pv = {} # code -> {prompt_id, version} for a in associations: code = a.get("code") if code and code not in code_to_pv: code_to_pv[code] = { "prompt_id": a.get("prompt_id"), "version": a.get("prompt_version"), } # 按 prompt_id@version 分组统计 version_stats = {} for d in decisions.get("decisions", []): code = d.get("code") if not code: continue pv = code_to_pv.get(code) if not pv: continue key = f"{pv['prompt_id']}@{pv['version']}" if key not in version_stats: version_stats[key] = { "prompt_id": pv["prompt_id"], "version": pv["version"], "total": 0, "take_profit_hit": 0, "stop_loss_hit": 0, "in_entry_zone": 0, "pending": 0, "avg_rr": 0.0, "rr_sum": 0.0, "rr_count": 0, "stocks": [], } vs = version_stats[key] vs["total"] += 1 vs["stocks"].append(code) # 从 evaluation 中读取状态 evals = d.get("evaluation", []) for ev in evals: if isinstance(ev, dict) and ev.get("phase") == 1: theo = ev.get("theoretical", {}) status = theo.get("status", "") if status == "take_profit_hit": vs["take_profit_hit"] += 1 elif status == "stop_loss_hit": vs["stop_loss_hit"] += 1 elif status == "in_entry_zone": vs["in_entry_zone"] += 1 else: vs["pending"] += 1 # 盈亏比 rr = d.get("rr_ratio") if rr is not None and isinstance(rr, (int, float)): vs["rr_sum"] += rr vs["rr_count"] += 1 # 计算平均盈亏比和成功率 for key, vs in version_stats.items(): if vs["rr_count"] > 0: vs["avg_rr"] = round(vs["rr_sum"] / vs["rr_count"], 2) total_outcome = vs["take_profit_hit"] + vs["stop_loss_hit"] if total_outcome > 0: vs["success_rate"] = round(vs["take_profit_hit"] / total_outcome * 100, 1) else: vs["success_rate"] = None del vs["rr_sum"] return version_stats def get_prompt_version_comparison() -> dict: """生成版本对比报告""" version_stats = analyze_prompt_version_effectiveness() # 补充每个版本的标签信息 for key, vs in version_stats.items(): prompt = get_prompt(vs["prompt_id"]) if prompt: for v in prompt.versions: if v.version == vs["version"]: vs["label"] = v.label vs["changelog"] = v.changelog vs["tags"] = v.tags break if "label" not in vs: vs["label"] = vs["version"] return version_stats def generate_report() -> str: """生成版本有效性报告文本""" comparison = get_prompt_version_comparison() lines = [] lines.append("📊 提示词版本有效性分析 | " + datetime.now().strftime("%Y-%m-%d")) lines.append("") if not comparison: lines.append("暂无数据 — 请先运行策略生成和评估后重试") return "\n".join(lines) # 按 prompt_id 分组显示 by_prompt = {} for key, vs in comparison.items(): pid = vs["prompt_id"] by_prompt.setdefault(pid, []).append(vs) for pid, versions in sorted(by_prompt.items()): prompt = get_prompt(pid) lines.append(f"\n## {prompt.name if prompt else pid}") lines.append(f" {prompt.description if prompt else ''}") lines.append("") # 按版本号排序 versions.sort(key=lambda x: x["version"]) header = f" {'版本':<10} {'标签':<20} {'策略数':<8} {'止盈':<8} {'止损':<8} {'成功率':<10} {'平均R/R':<10}" lines.append(header) lines.append(" " + "-" * len(header)) for vs in versions: sr = f"{vs['success_rate']}%" if vs['success_rate'] is not None else "-" label = vs.get("label", "")[:18] lines.append( f" {vs['version']:<10} {label:<20} " f"{vs['total']:<8} {vs['take_profit_hit']:<8} " f"{vs['stop_loss_hit']:<8} {sr:<10} " f"{vs['avg_rr']:<10}" ) lines.append("") lines.append("---") lines.append("注:数据来自 decisions.json evaluation + associations.json") return "\n".join(lines)