| | |
| | | f"- 价格交易日:`{trade_date_text}`", |
| | | f"- 泡沫解释版本:`{trade_date_text}/v{SCRIPT_VERSION}`", |
| | | f"- 记录数:`{len(rows)}`", |
| | | "- 全量覆盖:`677`个已评估主体,其中本表为可形成数值区间判定的`672`只,另`5`只见同目录`latest_gaps.csv`。", |
| | | f"- 数值判定覆盖:本表共`{len(rows)}`只;无法形成当日数值判定的证券单列于同目录`latest_gaps.csv`。", |
| | | f"- 估值泡沫:`{bubble_count}`只;定义为当前价高于正式基准合理区间上沿。", |
| | | "- 原因性质:除价格位置外,泡沫原因均是基于反向估值、基础报告和量价显影的最可能解释,不是已证实的资金流因果。", |
| | | f"- 输入快照 SHA-256:`{source_hash}`;泡沫解释脚本:`enrich_valuation_bubbles.py v{SCRIPT_VERSION}`。", |
| | |
| | | latest_md = resolve_under(project_root, args.latest_md) |
| | | fieldnames, rows = read_csv(latest_csv) |
| | | source_hash = hashlib.sha256(canonical_base_csv(fieldnames, rows)).hexdigest().upper() |
| | | if len(rows) != 672: |
| | | raise RuntimeError(f"Expected 672 current numeric judgements, got {len(rows)}") |
| | | if not rows: |
| | | raise RuntimeError("Current numeric judgement file is empty") |
| | | tickers = [row["ticker"] for row in rows] |
| | | if len(set(tickers)) != len(tickers): |
| | | raise RuntimeError("Duplicate tickers in latest.csv") |
| | | trade_dates = {date.fromisoformat(row["trade_date"]) for row in rows} |
| | | if len(trade_dates) != 1: |
| | | raise RuntimeError(f"Expected one trade date, got {sorted(trade_dates)}") |
| | | metrics = market_metrics(tickers, next(iter(trade_dates)), args.skip_market) |
| | | metrics: dict[str, MarketMetrics] = {} |
| | | for trade_date in sorted(trade_dates): |
| | | date_tickers = [row["ticker"] for row in rows if date.fromisoformat(row["trade_date"]) == trade_date] |
| | | metrics.update(market_metrics(date_tickers, trade_date, args.skip_market)) |
| | | missing_market = sorted(set(tickers) - set(metrics)) if not args.skip_market else [] |
| | | |
| | | enriched: list[dict[str, str]] = [] |