#!/usr/bin/env python3 # -*- coding: utf-8 -*- """stock_name_hygiene.py — 股票名称卫生(每周) 2026-08-22 名称治理:XD/XR/DR 除权息日交易所简称被截断(紫金矿业→XD紫金矿), 各行情源当天均无干净名。本任务每周拉 akshare 官方全量名称,剥 XD 标记后比对, 凡 DB 名是官方名的前缀(截断)即以官方名为准修复,自愈截断/改名/ST 变更。 """ import os, sys, re from datetime import datetime # ── 消息通道统一路由(broadcast/xmpp by delivery) ── try: from messenger import install_stdio_hook as _msh _msh() except Exception: pass DB_PATH = os.environ.get("MOFIN_DB", "/home/hmo/MoFin/data/mofin.db") def strip_marker(n: str) -> str: m = re.match(r"^(XD|XR|DR)(.+)$", n) return m.group(2) if m else n def main(): import sqlite3 import akshare as ak print(f"[stock_name_hygiene] {datetime.now():%Y-%m-%d %H:%M:%S} 开始", flush=True) df = ak.stock_info_a_code_name() name_map = {str(c): strip_marker(str(n)) for c, n in zip(df["code"], df["name"])} print(f" 官方名称源: {len(name_map)} 条", flush=True) conn = sqlite3.connect(DB_PATH, timeout=30) conn.row_factory = sqlite3.Row total = 0 samples = [] for table in ["stocks", "candidates", "holdings"]: try: rows = conn.execute(f"SELECT code, name FROM {table}").fetchall() except Exception: continue fixed = 0 for r in rows: code, cur = str(r["code"]), str(r["name"]) full = name_map.get(code) if not full or full == cur: continue # 只修明确劣化:DB名是官方名前缀(XD截断)、或DB名=代码、或仅ST标记差异 if full.startswith(cur) or cur == code or cur.lstrip("*ST") == full.lstrip("*ST"): conn.execute(f"UPDATE {table} SET name=? WHERE code=? AND name=?", (full, code, cur)) fixed += 1 if len(samples) < 8: samples.append(f"{code} {cur}→{full}") if fixed: print(f" {table}: 修复 {fixed} 行", flush=True) total += fixed conn.commit() conn.close() print(f"[stock_name_hygiene] 完成,共修复 {total} 行" + (f"(样本: {'; '.join(samples)})" if samples else ""), flush=True) if __name__ == "__main__": main()