from __future__ import annotations import hashlib import json from datetime import datetime from pathlib import Path import pandas as pd RUN_ID = "RUN-ANA-WUJI-BASELINE-PILOT-20260607-001" CASE_MATTER_ID = "ANA-WUJI-BASELINE-2023-2026" DESIGN_ID = "DESIGN-WUJI-BASELINE-FLOW-20260607" DESIGN_AUDIT_ID = "AUDIT-ANA-WUJI-BASELINE-FLOW-20260607-001" ROOT = Path(__file__).resolve().parents[1] def sha256_file(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def read_json(path: Path) -> dict: return json.loads(path.read_text(encoding="utf-8")) def manifest_files() -> list[dict]: rows: list[dict] = [] for path in sorted(ROOT.rglob("*")): if not path.is_file(): continue rel = path.relative_to(ROOT).as_posix() if rel == "manifest.json": continue rows.append( { "path": rel, "exists": True, "size": path.stat().st_size, "sha256": sha256_file(path), } ) return rows def main() -> None: generated_at = datetime.now().astimezone().isoformat(timespec="seconds") candidate_summary = read_json(ROOT / "candidate_generation_summary.json") candidate_counts = candidate_summary["candidate_counts"] candidate_image_summary = read_json(ROOT / "candidate_image_generation_summary.json") entry_review_summary = read_json(ROOT / "entry_review_generation_summary.json") entry_ai_summary = read_json(ROOT / "entry_ai_review_summary.json") exit_review_summary = read_json(ROOT / "exit_review_generation_summary.json") exit_ai_summary = read_json(ROOT / "exit_ai_review_summary.json") self_check = read_json(ROOT / "self_check.json") source_db = read_json(ROOT / "source_db_direct_check.json") source_boundaries = source_db["execution_boundaries"] daily_range = source_boundaries["daily_price_usable_range"] minute_range = source_boundaries["minute_price_usable_range_for_intraday_replay"] breadth_range = source_boundaries["market_breadth_daily_usable_range"] image_manifest = pd.read_csv(ROOT / "image_manifest.csv", encoding="utf-8-sig") decisions = pd.read_csv(ROOT / "decision_log.csv", encoding="utf-8-sig") orders = pd.read_csv(ROOT / "order_ledger.csv", encoding="utf-8-sig") lots = pd.read_csv(ROOT / "position_lot_ledger.csv", encoding="utf-8-sig") case_summary = pd.read_csv(ROOT / "case_summary.csv", encoding="utf-8-sig") completed_outputs = [ "run_config.md", "run_config.json", "baseline_rule_mapping.csv", "baseline_excluded_rule_table.csv", "code_validation_report.md", "code_validation_checks.json", "baseline_mapping_check.csv", "sample_recalc_check.csv", "chart_smoke_manifest.csv", "source_db_direct_check.json", "candidate_ledger.csv", "candidate_date_summary.csv", "case_index.csv", "selected_candidate_ledger.csv", "image_manifest.csv", "case_image_board.md", "case_story_board.md", "entry_review_generation_summary.json", "entry_ai_review_summary.json", "sell_signal_candidates.csv", "sell_decision_log.csv", "exit_resolution_log.csv", "order_ledger.csv", "position_lot_ledger.csv", "daily_account_ledger.csv", "case_summary.csv", "chart_evidence_audit.csv", "self_check.json", "self_check.md", ] held_items = [ { "item": "unresolved_lots", "count": int(exit_ai_summary["unresolved_lot_count"]), "reason": "Exit review has resolved all previous held lots under existing rules. Remaining non-SELL lots are explicit WINDOW_END_VALUATION_ONLY or EXIT_DATA_GAP_HELD rows, not forced SELL.", }, { "item": "return_statistics", "count": 1, "reason": "Execution audit is pending and strict_baseline_return_ready_flag=false.", }, ] summary = { "run_id": RUN_ID, "case_matter_id": CASE_MATTER_ID, "design_id": DESIGN_ID, "design_audit_id": DESIGN_AUDIT_ID, "created_at": "2026-06-07T23:37:00+08:00", "updated_at": generated_at, "role_instance_id": "case_analysis.analyst", "stage": "STRUCTURE_PILOT_EXIT_REVIEW_RESOLVED_SELF_CHECK_DONE", "status": "exit_review_held_lots_resolved_self_check_pass_return_stat_held_execution_review_submission_ready", "completed_outputs": completed_outputs, "held_items": held_items, "conclusion_boundary": "Structure pilot only. No complete baseline return, success rate, win rate, drawdown, or strategy effectiveness conclusion. Closed-lot contribution is an internal ledger check and must not be quoted as final return.", "source_db_validation": { "status": "PASS_WITH_MINUTE_BOUNDARY", "evidence": "source_db_direct_check.json", "daily_price_range": f"{daily_range['min_date']} to {daily_range['max_date']}", "minute_price_range": f"{minute_range['min_date']} to {minute_range['max_date']}", "market_breadth_range": f"{breadth_range['min_date']} to {breadth_range['max_date']}", "market_breadth_scope": "ALL_A_SHARE/ALL", "market_breadth_run_id": breadth_range["min_run_id"], }, "candidate_pool": { "candidate_rows": candidate_counts["candidate_rows"], "candidate_entry_dates": candidate_counts["candidate_entry_dates"], "selected_pilot_cases": len(candidate_summary["selected_cases"]), "market_gate_open_dates": candidate_counts["market_gate_open_entry_dates"], "market_gate_closed_dates": candidate_counts["market_gate_closed_entry_dates"], }, "image_package": { "image_count": len(image_manifest), "role_counts": image_manifest.chart_role.value_counts().to_dict(), "candidate_daily_images": candidate_image_summary["image_count"], "entry_review_images": entry_review_summary["entry_review_images"], "buy_decision_images": entry_ai_summary["buy_decision_image_count"], "exit_signal_images": exit_review_summary["exit_signal_images"], "sell_decision_images": int((image_manifest.chart_role == "exit_1m_sell_decision_view").sum()), }, "trade_ledger": { "order_counts": orders.action.value_counts().to_dict(), "lot_status_counts": lots.lot_status.value_counts().to_dict(), "case_count": len(case_summary), "closed_lot_account_return_sum_for_recalc_only": exit_ai_summary["closed_lot_account_return_sum"], }, "decision_counts": decisions.groupby(["decision_stage", "action_status"]).size().astype(int).to_dict(), "self_check": { "status": self_check["overall_status"], "check_count": self_check["check_count"], "fail_count": self_check["fail_count"], "evidence": "self_check.json", }, "repair_context": { "previous_execution_audit_id": "AUDIT-ANA-WUJI-BASELINE-PILOT-20260608-EXEC-001", "previous_review_message_id": "msg_20260608012706751_a5dbc58b", "fixed_issue": "ANA-ISSUE-WUJI-ACCOUNT-LEDGER-DIRECTION-20260608-001", "repair_summary": "Fixed daily_account_ledger cash/open-position direction, added account semantic self-checks, refreshed root case_image_board.md and run_config.md status.", }, "exit_review_resolution_context": { "previous_issue": "ANA-ISSUE-WUJI-EXIT-REVIEW-20260608-001", "resolution_summary": "Re-scanned the approved 10-trading-day observation window with 1-minute evidence. Four previously held lots were closed by minute-confirmed SELL points; one lot is WINDOW_END_VALUATION_ONLY; one lot remains EXIT_DATA_GAP_HELD because minute data coverage ends before the signal date.", "evidence": "exit_resolution_log.csv", }, "strict_baseline_return_ready_flag": False, "execution_review_status": "SUBMISSION_READY", } # Convert tuple keys from groupby for JSON stability. summary["decision_counts"] = {f"{k[0]}::{k[1]}": v for k, v in summary["decision_counts"].items()} (ROOT / "summary.json").write_text( json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) summary_md = [ f"# {RUN_ID} summary", "", "当前阶段:`STRUCTURE_PILOT_EXIT_REVIEW_RESOLVED_SELF_CHECK_DONE`", "当前状态:6 笔待审 lot 已按现有规则复核,结构试点自检通过;执行审核待提交;`RETURN_STAT_READY=false`。", "", "## 已完成", "", "1. 完成 run_config、蓝本映射、代码验证、T+1 / 账本烟测、中文 K 线烟测和源库直连校验。", f"2. 生成真实候选池:{candidate_counts['candidate_rows']} 行,覆盖 {candidate_counts['candidate_entry_dates']} 个入场日。", f"3. 选择 {len(candidate_summary['selected_cases'])} 个代表性小样本案例,并生成 100 日选股日 K 图。", f"4. 生成买入分时复核图 {entry_review_summary['entry_review_images']} 张,AI 买入裁决图 {entry_ai_summary['buy_decision_image_count']} 张。", f"5. 生成卖点日 K 信号图 {exit_review_summary['exit_signal_images']} 张,AI SELL 分时裁决图 {summary['image_package']['sell_decision_images']} 张。", f"6. 建立订单、lot、事件账户账本和案例汇总:BUY {summary['trade_ledger']['order_counts'].get('BUY', 0)} 笔,SELL {summary['trade_ledger']['order_counts'].get('SELL', 0)} 笔。", f"7. 完成机器自检:{self_check['check_count']} 项,失败 {self_check['fail_count']} 项,图片 manifest 共 {len(image_manifest)} 张图且 hash 可反查。", "8. 根据执行审核反馈 `AUDIT-ANA-WUJI-BASELINE-PILOT-20260608-EXEC-001` 修复账户流水方向,并补充现金 / 持仓方向自检。", "9. 针对 `ANA-ISSUE-WUJI-EXIT-REVIEW-20260608-001` 重新按 1 分钟 evidence 扫描观察窗口:4 笔原待审 lot 转为分钟线确认 SELL,1 笔为窗口末估值保留,1 笔为分钟数据缺口保留。", "", "## 未完成 / 保留项", "", "1. 本次退出复核执行审核尚未通过,审核通过前不得把本包读成正式结论。", f"2. 未闭合 / 非真实 SELL lot 共 {exit_ai_summary['unresolved_lot_count']} 笔:1 笔窗口末估值保留,1 笔分钟数据缺口保留。", "3. 当前闭合 lot 的账户贡献合计只用于账本复算,不是完整 baseline 收益率、成功率、胜率或回撤。", "", "## 结论边界", "", "本包只证明第一轮小样本结构试点可以串起候选、图片、AI 手工裁决、订单 / lot / 账户账本、自检和人工审核入口。", "它不产生完整无忌 baseline 收益、成功率、胜率、回撤或策略有效性结论。", "", "后续必须提交 `case_analysis.reviewer` 执行审核;若审核要求继续修正口径、图或账本,按审计意见回写后再继续下一批。", "", ] (ROOT / "summary.md").write_text("\n".join(summary_md), encoding="utf-8") files = manifest_files() manifest = { "schema_version": "1.0", "run_id": RUN_ID, "manifest_stage": "STRUCTURE_PILOT_EXIT_REVIEW_RESOLVED_SELF_CHECK_DONE", "generated_at": generated_at, "hash_status": "size_and_sha256_recorded_for_current_artifacts_manifest_self_excluded", "overall_status": self_check["overall_status"], "file_count": len(files), "files": files, "directories": sorted([p.relative_to(ROOT).as_posix() for p in ROOT.rglob("*") if p.is_dir()]), "notes": "Exit-review-resolved structure pilot package manifest. manifest.json itself is excluded from hashing for stability. Execution review submission is pending; return statistics are not ready.", } (ROOT / "manifest.json").write_text( json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) if __name__ == "__main__": main()