"""策略→提示词版本关联追踪 当 strategy_lifecycle.py 生成策略时,记录当前使用的提示词版本, 将每只股票的策略与生成它的提示词版本关联起来。 """ import json from pathlib import Path from datetime import datetime from typing import Optional from .models import StrategyLink from .registry import get_prompt DATA_DIR = Path("/home/hmo/projects/MoFin/data/prompts") ASSOCIATIONS_PATH = DATA_DIR / "associations.json" def load_associations() -> dict: """加载关联记录""" DATA_DIR.mkdir(parents=True, exist_ok=True) if not ASSOCIATIONS_PATH.exists(): return {"associations": [], "updated_at": datetime.now().isoformat()} try: return json.loads(ASSOCIATIONS_PATH.read_text(encoding="utf-8")) except (json.JSONDecodeError, FileNotFoundError): return {"associations": [], "updated_at": datetime.now().isoformat()} def save_associations(data: dict): DATA_DIR.mkdir(parents=True, exist_ok=True) data["updated_at"] = datetime.now().isoformat() ASSOCIATIONS_PATH.write_text( json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8", ) def get_current_strategy_prompt_version() -> dict: """获取当前活跃的策略生成提示词版本 返回 {prompt_id, version, label} 或 None """ prompt = get_prompt("strategy-generation") if not prompt: return None for v in prompt.versions: if v.version == prompt.current_version: return { "prompt_id": prompt.id, "version": v.version, "label": v.label, } return None def record_strategy_generation(code: str, name: str, strategy_action: str) -> StrategyLink: """记录策略生成事件 由 strategy_lifecycle.py 在生成策略时调用 """ pv = get_current_strategy_prompt_version() if not pv: # 如果没有提示词版本记录,创建一个默认记录 pv = {"prompt_id": "strategy-generation", "version": "unknown", "label": "未知版本"} link = StrategyLink( code=code, name=name, prompt_id=pv["prompt_id"], prompt_version=pv["version"], strategy_action=strategy_action[:500], # 截断保护 generated_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ) data = load_associations() data.setdefault("associations", []).append(link.to_dict()) # 只保留最近的10000条记录 if len(data["associations"]) > 10000: data["associations"] = data["associations"][-10000:] save_associations(data) return link def get_associations_for_stock(code: str, max_results: int = 10) -> list: """获取某只股票的所有策略关联记录""" data = load_associations() records = [a for a in data.get("associations", []) if a["code"] == code] return sorted(records, key=lambda x: x.get("generated_at", ""), reverse=True)[:max_results] def get_associations_for_prompt_version(prompt_id: str, version: str) -> list: """获取某个提示词版本生成的所有策略""" data = load_associations() return [a for a in data.get("associations", []) if a.get("prompt_id") == prompt_id and a.get("prompt_version") == version] def get_strategy_version_stats() -> dict: """统计每个提示词版本生成的策略数量""" data = load_associations() stats = {} for a in data.get("associations", []): key = f"{a.get('prompt_id', '?')}@{a.get('prompt_version', '?')}" if key not in stats: stats[key] = { "prompt_id": a.get("prompt_id"), "version": a.get("prompt_version"), "count": 0, "stocks": set(), "last_generated": "", } stats[key]["count"] += 1 stats[key]["stocks"].add(a.get("code")) if a.get("generated_at", "") > stats[key]["last_generated"]: stats[key]["last_generated"] = a.get("generated_at", "") # 转换 set 为 list 以便 JSON 序列化 for k in stats: stats[k]["stocks"] = sorted(stats[k]["stocks"]) return stats