Cai
2026-08-20 61cf007883ae7d6e8d98f7a0a34f94cabaa79da1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
from __future__ import annotations
 
import hashlib
import importlib.util
import json
import sys
from pathlib import Path
 
 
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "dev" / "ana-dev"))
sys.path.insert(0, str(ROOT / "dev" / "project-dev"))
GENERATOR = ROOT / "ai-valuation-analyst" / "tools" / "generate_image_targets_batch_20260805.py"
SPEC = importlib.util.spec_from_file_location("image_targets_generator_for_repair", GENERATOR)
if SPEC is None or SPEC.loader is None:
    raise RuntimeError(f"cannot load {GENERATOR}")
module = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = module
SPEC.loader.exec_module(module)
 
RESULT_ROOT = ROOT / "ana-data" / "result" / "股票估值"
BATCH_JSON = RESULT_ROOT / "20260805_batch_six_images_valuation" / "batch_results.json"
 
 
def write_json(path: Path, value) -> None:
    path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
 
 
def sha256(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()
 
 
def main() -> None:
    batch = json.loads(BATCH_JSON.read_text(encoding="utf-8"))
    repaired = 0
    for row in batch["rows"]:
        out_dir = RESULT_ROOT / Path(row["formal_path"]).parent
        snapshot_path = next(out_dir.glob("*估值快照_20260804.json"))
        snapshot = json.loads(snapshot_path.read_text(encoding="utf-8"))
        old_cap = snapshot["market"].get("platform_market_cap")
        snapshot["market"]["platform_market_cap"] = None
        snapshot.setdefault("analysis", {})["platform_market_cap_excluded"] = old_cap
        snapshot["analysis"]["platform_market_cap_exclusion_reason"] = (
            "行情接口平台市值是取数时点实时值,与2026-08-04历史收盘价不在同一时点;正式市值只按历史收盘价×同口径股本复算。"
        )
        write_json(snapshot_path, snapshot)
        calc_dir = out_dir / "calculation"
        module.base.run_pipeline(snapshot_path, calc_dir, module.REGISTRY)
        results_path = calc_dir / "valuation_results.json"
        results = json.loads(results_path.read_text(encoding="utf-8"))
        report = (calc_dir / "valuation_report.md").read_text(encoding="utf-8")
        report = report.replace("价格合理性评估(流水线生成底稿)", "价格合理性评估", 1).replace(
            "本文是研究计算底稿,不构成交易指令或收益承诺。",
            "本文为按《股票价格合理性评估操作手册》形成的条件化研究结论,不构成交易指令或收益承诺。",
            1,
        )
        formal_path = RESULT_ROOT / row["formal_path"]
        formal_path.write_text(report, encoding="utf-8")
        row["qa"] = results["qa"]["status"]
        row["qa_errors"] = results["qa"]["error_count"]
        row["qa_warnings"] = results["qa"]["warning_count"]
        row["market_cap"] = float(results["metrics"]["market_cap"])
        evidence_path = out_dir / "source_evidence_manifest.json"
        evidence = json.loads(evidence_path.read_text(encoding="utf-8"))
        note = (
            "行情平台市值与历史收盘价不在同一时点,已从V1平台市值交叉校验输入中剔除;"
            "原始行情哈希和平台值保留,正式市值仅按股价×股本复算。"
        )
        gaps = evidence.setdefault("bounded_gaps", [])
        if note not in gaps:
            gaps.append(note)
        evidence["market_cap_timing_repair"] = {
            "excluded_platform_market_cap": old_cap,
            "formal_recomputed_market_cap": float(results["metrics"]["market_cap"]),
            "snapshot_sha256": sha256(snapshot_path),
            "calculation_sha256": sha256(results_path),
            "formal_report_sha256": sha256(formal_path),
        }
        write_json(evidence_path, evidence)
        repaired += 1
    write_json(BATCH_JSON, batch)
    print(json.dumps({
        "repaired": repaired,
        "qa_errors": sum(row["qa_errors"] for row in batch["rows"]),
        "qa_warnings": sum(row["qa_warnings"] for row in batch["rows"]),
    }, ensure_ascii=False), flush=True)
 
 
if __name__ == "__main__":
    main()