from __future__ import annotations
|
|
import csv
|
import hashlib
|
import json
|
from datetime import datetime
|
from pathlib import Path
|
from zoneinfo import ZoneInfo
|
|
|
RUN_ID = "RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001"
|
TASK_ID = "ANA-WUJI-V1-FINAL-CONCLUSION-20260610"
|
SOURCE_RUN_ID = "RUN-ANA-WUJI-STRICT-SELL-ROLLING-REPAIR-20260609-001"
|
SOURCE_EXEC_AUDIT_ID = "AUDIT-ANA-WUJI-STRICT-SELL-ROLLING-REPAIR-20260610-EXEC-REREVIEW-003"
|
DESIGN_AUDIT_ID = "AUDIT-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-DESIGN-001"
|
EXEC_AUDIT_ID = "AUDIT-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-EXEC-001"
|
EXEC_REVIEW_MESSAGE_ID = "msg_20260610215718079_7732c8c2"
|
EXEC_REVIEW_REQUEST_ID = "msg_20260610215112659_4d34e4c7"
|
EXEC_REVIEW_TIME = "2026-06-10T21:57:18+08:00"
|
|
OUT_DIR = Path(__file__).resolve().parents[1]
|
PROJECT_ROOT = Path(__file__).resolve().parents[4]
|
SOURCE_DIR = PROJECT_ROOT / "ana-data" / "result" / SOURCE_RUN_ID
|
|
|
def now_iso() -> str:
|
return datetime.now(ZoneInfo("Asia/Shanghai")).isoformat(timespec="seconds")
|
|
|
def read_csv(path: Path) -> list[dict[str, str]]:
|
with path.open("r", encoding="utf-8-sig", newline="") as f:
|
return list(csv.DictReader(f))
|
|
|
def write_csv(path: Path, rows: list[dict[str, object]], fieldnames: list[str] | None = None) -> None:
|
if fieldnames is None:
|
fieldnames = list(rows[0].keys()) if rows else []
|
with path.open("w", encoding="utf-8-sig", newline="") as f:
|
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
writer.writeheader()
|
writer.writerows(rows)
|
|
|
def write_json(path: Path, data: object) -> None:
|
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
|
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 file_row(path: Path) -> dict[str, object]:
|
return {
|
"path": path.relative_to(OUT_DIR).as_posix(),
|
"size": path.stat().st_size,
|
"sha256": sha256_file(path),
|
}
|
|
|
def case_line(row: dict[str, str]) -> str:
|
case_id = row["case_id"]
|
ret = row.get("account_return_closed_lots", "")
|
return (
|
f"- `{case_id}`:账户贡献 `{ret}`;"
|
f"[图片板](../{SOURCE_RUN_ID}/cases/{case_id}/case_image_board.md);"
|
f"[故事板](../{SOURCE_RUN_ID}/cases/{case_id}/case_story_board.md)"
|
)
|
|
|
def main() -> None:
|
summary_path = OUT_DIR / "v1_final_conclusion_summary.json"
|
summary = json.loads(summary_path.read_text(encoding="utf-8-sig"))
|
readouts = read_csv(OUT_DIR / "v1_final_readouts.csv")
|
case_rows = read_csv(OUT_DIR / "v1_case_readout_index.csv")
|
boundary_rows = read_csv(OUT_DIR / "v1_boundary_table.csv")
|
|
primary = [r for r in case_rows if r["v1_return_scope"] == "V1_PRIMARY_STRICT_CLOSED_CASE"]
|
positive = [r for r in primary if r["success_flag"] == "1"]
|
non_positive = [r for r in primary if r["success_flag"] != "1"]
|
boundary_cases = [r for r in case_rows if r["v1_return_scope"] != "V1_PRIMARY_STRICT_CLOSED_CASE"]
|
top_positive = sorted(positive, key=lambda r: float(r["account_return_closed_lots"] or 0), reverse=True)[:3]
|
top_negative = sorted(non_positive, key=lambda r: float(r["account_return_closed_lots"] or 0))[:3]
|
boundary_example = boundary_cases[:1]
|
|
readout_map = {r["readout_id"]: r for r in readouts}
|
primary_readout = readout_map["V1_PRIMARY_STRICT_CLOSED_CASE"]
|
coverage_readout = readout_map["V1_ALL_BUY_CASE_COVERAGE"]
|
lot_readout = readout_map["V1_STRICT_CLOSED_LOT_RECALC_ONLY"]
|
rolling_readout = readout_map["V1_ROLLING_LOW_BUY"]
|
|
summary.update(
|
{
|
"execution_review_status": "EXECUTION_REVIEW_PASSED_LAYERED_V1_CITATION_ALLOWED",
|
"execution_review_audit_id": EXEC_AUDIT_ID,
|
"execution_review_message_id": EXEC_REVIEW_MESSAGE_ID,
|
"execution_review_request_message_id": EXEC_REVIEW_REQUEST_ID,
|
"execution_review_passed_at": EXEC_REVIEW_TIME,
|
"citation_allowed": True,
|
"formal_summary_written": True,
|
"formal_summary_path": "ana-doc/wuji/无忌交易系统V1最终案例总结.md",
|
}
|
)
|
write_json(summary_path, summary)
|
|
summary_md = f"""# 无忌 V1 最终结论引用包摘要
|
|
## 当前可引用状态
|
|
当前包基于已通过执行复审的 V1 结果包生成,来源审计 ID 为 `{SOURCE_EXEC_AUDIT_ID}`。本包已通过最终结论引用包执行审核,审计 ID 为 `{EXEC_AUDIT_ID}`。
|
|
允许按 V1 口径、审计 ID 和边界表分层引用;不得写成无边界完整 baseline 结论或策略有效性证明。
|
|
## V1 主口径:V1_PRIMARY_STRICT_CLOSED_CASE
|
|
| 指标 | 读数 |
|
|---|---:|
|
| V1 主口径严格闭合 case | {primary_readout['case_count']} |
|
| V1 主口径正收益 case | {primary_readout['positive_case_count']} |
|
| V1 主口径非正收益 case | {primary_readout['non_positive_case_count']} |
|
| V1 主口径成功率读数 | {primary_readout['success_rate']} |
|
| V1 主口径账户贡献合计读数 | {primary_readout['account_contribution_sum']} |
|
|
## 覆盖口径
|
|
| 指标 | 读数 |
|
|---|---:|
|
| V1 有 BUY case | {coverage_readout['case_count']} |
|
| V1 边界 case | {coverage_readout['boundary_count']} |
|
| 滚动低吸 BUY 订单 | {rolling_readout['lot_count']} |
|
|
## lot 辅助口径
|
|
| 指标 | 读数 |
|
|---|---:|
|
| V1 lot 总数 | {lot_readout['lot_count']} |
|
| V1 边界 lot | {lot_readout['boundary_count']} |
|
|
## 边界
|
|
`v1_boundary_table.csv` 完整保留来源边界表,共 {len(boundary_rows)} 行。必须特别保留 `MARKET_RISK_MINUTE_BREADTH_DATA_GAP_HELD`,它表示本地数据源没有开盘 10 分钟全 A 下跌家数分钟级广度,市场风险卖点不能被无边界化。
|
|
## 禁止外推
|
|
1. 不得把 V1 读数写成旧 V0 包结论。
|
2. 不得写成无边界完整 baseline 成功率、收益率、胜率或回撤。
|
3. 不得作出策略有效性的证明结论。
|
4. 不得省略审计 ID、V1 口径、边界表和市场风险分钟广度数据缺口。
|
"""
|
(OUT_DIR / "v1_final_conclusion_summary.md").write_text(summary_md, encoding="utf-8")
|
|
human_index = f"""# 无忌 V1 最终结论人工审核第一入口
|
|
## 先看这里
|
|
当前 V1 最终结论引用包已通过执行审核:`{EXEC_AUDIT_ID}`。
|
|
可以引用:在 V1 主口径 `V1_PRIMARY_STRICT_CLOSED_CASE` 下,250 个严格闭合 case 中 99 个为正收益,成功率读数为 0.396,账户贡献合计读数为 0.12970642。
|
|
必须同时引用:
|
- 来源 V1 执行复审 ID:`{SOURCE_EXEC_AUDIT_ID}`
|
- 本最终引用包执行审核 ID:`{EXEC_AUDIT_ID}`
|
- `v1_boundary_table.csv`
|
- `MARKET_RISK_MINUTE_BREADTH_DATA_GAP_HELD`
|
|
不能引用:不能把 V1 读数写成旧 V0 包结论,不能写成无边界完整 baseline 结论,不能作出策略有效性的证明结论,不能省略市场风险分钟广度数据缺口。
|
|
## 当前状态
|
|
| 项目 | 内容 |
|
|---|---|
|
| 当前 run | `{RUN_ID}` |
|
| 来源 run | `{SOURCE_RUN_ID}` |
|
| 来源执行复审审计 ID | `{SOURCE_EXEC_AUDIT_ID}` |
|
| 当前设计审核审计 ID | `{DESIGN_AUDIT_ID}` |
|
| 当前执行审核审计 ID | `{EXEC_AUDIT_ID}` |
|
| RETURN_STAT_READY | `false` |
|
| 执行审核状态 | `通过 / 允许按 V1 边界引用` |
|
|
## V1 三层读数
|
|
1. `V1_PRIMARY_STRICT_CLOSED_CASE`:250 个严格闭合 case,正收益 99 个,成功率读数 0.396,账户贡献合计 0.12970642。
|
2. `V1_ALL_BUY_CASE_COVERAGE`:251 个有 BUY case,其中 1 个 case 进入边界;该口径不替代主成功率。
|
3. `V1_STRICT_CLOSED_LOT_RECALC_ONLY`:735 个 lot,其中 734 个闭合、1 个边界;lot 口径只用于复算和问题定位。
|
4. `V1_ROLLING_LOW_BUY`:滚动低吸 BUY 订单 25 笔;只说明 V1 执行数量,不单独构成策略有效性结论。
|
|
## 第一入口链接
|
|
- 来源 V1 图片总入口:[case_image_board.md](../{SOURCE_RUN_ID}/case_image_board.md)
|
- 来源 V1 摘要:[summary.md](../{SOURCE_RUN_ID}/summary.md)
|
- 来源 V1 手工裁决草稿:[manual_decision_external_draft.md](../{SOURCE_RUN_ID}/manual_decision_external_draft.md)
|
- 当前 V1 结论摘要:[v1_final_conclusion_summary.md](v1_final_conclusion_summary.md)
|
- 当前 V1 同事总结:[v1_final_case_summary_for_colleagues.md](v1_final_case_summary_for_colleagues.md)
|
- 当前 V1 读数表:[v1_final_readouts.csv](v1_final_readouts.csv)
|
- 当前 V1 case 索引:[v1_case_readout_index.csv](v1_case_readout_index.csv)
|
- 当前 V1 边界表:[v1_boundary_table.csv](v1_boundary_table.csv)
|
|
## 代表性 case
|
|
主口径正收益较高样例:
|
{chr(10).join(case_line(r) for r in top_positive)}
|
|
主口径负收益较低样例:
|
{chr(10).join(case_line(r) for r in top_negative)}
|
|
边界样例:
|
{chr(10).join(case_line(r) for r in boundary_example)}
|
|
## 阅读提示
|
|
1. 先看本文件的可引用 / 不可引用边界。
|
2. 再看 `v1_final_case_summary_for_colleagues.md`,它是同事阅读版摘要。
|
3. 需要追溯单个案例时,从 `v1_case_readout_index.csv` 找到 `case_id`,再打开对应图片板和故事板。
|
4. 对卖点和滚动低吸的人工裁决来源,优先看 `manual_decision_external_draft.md` 和 `manual_decision_ledger.csv`。
|
"""
|
(OUT_DIR / "v1_final_human_review_index.md").write_text(human_index, encoding="utf-8")
|
|
final_md = f"""# 无忌交易系统 V1 最终案例总结(分层引用版)
|
|
创建人员:case_analysis.analyst / laoan
|
文件职责:给同事阅读的 V1 当前正式总结入口,说明可以引用的读数、必须带上的边界,以及从哪里追溯证据。
|
|
## 一句话结论
|
|
`RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001` 已通过最终结论引用包执行审核,审计 ID 为 `{EXEC_AUDIT_ID}`。当前允许把 V1 读数按分层口径写给同事看,但不能写成无边界完整 baseline 结论,也不能作出策略有效性的证明结论。
|
|
## 可以引用的 V1 读数
|
|
在 V1 主口径 `V1_PRIMARY_STRICT_CLOSED_CASE` 下,250 个严格闭合 case 中 99 个为正收益,成功率读数为 0.396,账户贡献合计读数为 0.12970642。
|
|
覆盖口径 `V1_ALL_BUY_CASE_COVERAGE` 为 251 个有 BUY case,其中 1 个边界 case;覆盖口径只说明样本覆盖,不替代主成功率分母。
|
|
辅助 lot 口径 `V1_STRICT_CLOSED_LOT_RECALC_ONLY` 为 735 个 lot,其中 734 个闭合、1 个边界;lot 口径只用于复算和问题定位,不包装成 case 成功率。
|
|
滚动低吸 BUY 订单为 25 笔;它只说明 V1 执行数量,不单独构成策略有效性结论。
|
|
## 必须同时写明的边界
|
|
1. 来源 V1 执行复审 ID:`{SOURCE_EXEC_AUDIT_ID}`。
|
2. 本最终引用包执行审核 ID:`{EXEC_AUDIT_ID}`。
|
3. 该读数只覆盖 V1 已审核主口径,不等于无边界完整 baseline。
|
4. `v1_boundary_table.csv` 必须保留,其中包括 `MARKET_RISK_MINUTE_BREADTH_DATA_GAP_HELD`。
|
5. 当前仍保留 `RETURN_STAT_READY=false`。
|
|
## 不可以写的内容
|
|
1. 不得把 V1 读数写成旧 V0 包结论。
|
2. 不得写成无边界完整 baseline 成功率、收益率、胜率或回撤。
|
3. 不得省略 1 个边界 case、1 个边界 lot 和市场风险分钟广度数据缺口。
|
4. 不得把滚动低吸 BUY 25 笔单独包装成策略有效性结论。
|
|
## 给同事看的阅读入口
|
|
1. 先看 V1 人工第一入口:`ana-data/result/RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001/v1_final_human_review_index.md`。
|
2. 再看 V1 结果包摘要:`ana-data/result/RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001/v1_final_conclusion_summary.md`。
|
3. 想逐个看案例时,看 `ana-data/result/RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001/v1_case_readout_index.csv`,再进入对应 case 图片板和 story board。
|
4. 想查买卖和滚动低吸裁决来源时,看 `ana-data/result/RUN-ANA-WUJI-STRICT-SELL-ROLLING-REPAIR-20260609-001/manual_decision_external_draft.md` 和 `manual_decision_ledger.csv`。
|
5. 想复核边界时,看 `ana-data/result/RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001/v1_boundary_table.csv`。
|
|
## 审计链
|
|
| 阶段 | 审计 ID |
|
|---|---|
|
| V1 精准卖点与滚动渣男执行复审 | `{SOURCE_EXEC_AUDIT_ID}` |
|
| V1 最终结论引用设计审核 | `{DESIGN_AUDIT_ID}` |
|
| V1 最终结论引用包执行审核 | `{EXEC_AUDIT_ID}` |
|
|
## 与旧 V0 总结的关系
|
|
旧 `ana-doc/wuji/无忌交易系统最终案例总结.md` 保留为 V0 / 代理卖点阶段的历史分层引用记录。当前要给同事看 V1 完整精准卖点与滚动低吸返修后的结果,应优先使用本文件和 `RUN-ANA-WUJI-V1-FINAL-CONCLUSION-20260610-001`。
|
"""
|
(OUT_DIR / "v1_final_case_summary_for_colleagues.md").write_text(final_md, encoding="utf-8")
|
|
final_json = {
|
"schema_version": "1.0",
|
"task_id": TASK_ID,
|
"run_id": RUN_ID,
|
"source_run_id": SOURCE_RUN_ID,
|
"source_execution_rereview_audit_id": SOURCE_EXEC_AUDIT_ID,
|
"design_audit_id": DESIGN_AUDIT_ID,
|
"execution_review_audit_id": EXEC_AUDIT_ID,
|
"execution_review_message_id": EXEC_REVIEW_MESSAGE_ID,
|
"generated_at": now_iso(),
|
"citation_allowed": True,
|
"return_stat_ready": False,
|
"readouts": summary["readouts"],
|
"required_boundary": [
|
"V1 scope",
|
SOURCE_EXEC_AUDIT_ID,
|
EXEC_AUDIT_ID,
|
"v1_boundary_table.csv",
|
"MARKET_RISK_MINUTE_BREADTH_DATA_GAP_HELD",
|
],
|
"public_summary_path": "ana-doc/wuji/无忌交易系统V1最终案例总结.md",
|
}
|
write_json(OUT_DIR / "v1_final_case_summary_for_colleagues.json", final_json)
|
|
readme = f"""# 无忌 V1 最终结论引用包
|
|
本包是 V1 执行复审通过后的引用层整理,不重跑候选池、买卖裁决、人工裁决或账本。
|
|
当前执行审核已通过:`{EXEC_AUDIT_ID}`。
|
|
先看:[v1_final_human_review_index.md](v1_final_human_review_index.md)
|
|
给同事看的总结:[v1_final_case_summary_for_colleagues.md](v1_final_case_summary_for_colleagues.md)
|
|
来源执行复审 ID:`{SOURCE_EXEC_AUDIT_ID}`
|
|
设计审核 ID:`{DESIGN_AUDIT_ID}`
|
|
当前状态:允许按 V1 口径、审计 ID 和边界表分层引用;不得写成无边界完整 baseline 结论或策略有效性证明。
|
"""
|
(OUT_DIR / "README.md").write_text(readme, encoding="utf-8")
|
|
items = read_csv(OUT_DIR / "self_check_items.csv")
|
if not any(r.get("item") == "EXECUTION_REVIEW_AUDIT_RECORDED" for r in items):
|
items.append(
|
{
|
"item": "EXECUTION_REVIEW_AUDIT_RECORDED",
|
"status": "PASS",
|
"detail": f"audit={EXEC_AUDIT_ID}, message={EXEC_REVIEW_MESSAGE_ID}",
|
}
|
)
|
write_csv(OUT_DIR / "self_check_items.csv", items, ["item", "status", "detail"])
|
fail_count = sum(1 for r in items if r["status"] != "PASS")
|
self_check = {
|
"schema_version": "1.0",
|
"run_id": RUN_ID,
|
"generated_at": now_iso(),
|
"status": "PASS_FOR_V1_FINAL_CONCLUSION_EXECUTION_REVIEW_PASSED" if fail_count == 0 else "FAIL",
|
"pass_count": len(items) - fail_count,
|
"fail_count": fail_count,
|
"items_path": "self_check_items.csv",
|
}
|
write_json(OUT_DIR / "self_check.json", self_check)
|
(OUT_DIR / "self_check.md").write_text(
|
f"""# 自检摘要
|
|
- status:`{self_check['status']}`
|
- PASS:{self_check['pass_count']}
|
- FAIL:{self_check['fail_count']}
|
- 检查项:`self_check_items.csv`
|
""",
|
encoding="utf-8",
|
)
|
|
package_files = []
|
for p in OUT_DIR.rglob("*"):
|
if not p.is_file():
|
continue
|
if p.name in {"manifest.csv", "manifest.json"}:
|
continue
|
package_files.append(file_row(p))
|
package_files.sort(key=lambda r: str(r["path"]))
|
write_csv(OUT_DIR / "manifest.csv", package_files, ["path", "size", "sha256"])
|
write_json(
|
OUT_DIR / "manifest.json",
|
{
|
"schema_version": "1.0",
|
"run_id": RUN_ID,
|
"generated_at": now_iso(),
|
"files": package_files,
|
},
|
)
|
|
print(
|
json.dumps(
|
{
|
"run_id": RUN_ID,
|
"status": self_check["status"],
|
"execution_review_audit_id": EXEC_AUDIT_ID,
|
"manifest_files": len(package_files),
|
},
|
ensure_ascii=False,
|
)
|
)
|
|
|
if __name__ == "__main__":
|
main()
|