--- name: dividend-stability-score description: "5 维综合分红稳定性评分 0-100 (派息年数/CAGR/波动/最近/连续) + 5 星等级. 用于 A 股 — 港美股可扩展 (占位)" version: 1.0.0 type: reference --- # 分红稳定性评分 (Stability Score) **用户原话** (2026-07-22): 推送要加**分红稳定性**,综合评分。 ## 设计 (5 维, 100 分总分) | 维度 | 分值 | 说明 | |------|------|------| | **派息年数** | 30 | 派过 15 年满分 | | **派息 CAGR** | 20 | 最新/最早比, 复合年增长 ≥10% 满分 | | **波动率**(CV) | 25 | 派息标准差/均值, ≤0.2 满分 | | **最近 ≥ 上次** | 15 | `latest_div >= prev_div` | | **连续性** | 10 | 最近 3 年都派 | ## 等级 (0-100 → 1-5 星) | 分数 | 等级 | 标签 | |------|------|------| | 80-100 | 5★ | 长期稳定 | | 60-79 | 4★ | 基本稳定 | | 40-59 | 3★ | 不稳定 | | 20-39 | 2★ | 风险大 | | 0-19 | 1★ | 不推荐 | ## 实现 (`dividend_alert.py` 已部署) ```python def score_dividend_stability(symbol: str, market: str) -> dict: """返回: {"score": 75, "level": 4, "label": "基本稳定", "years": 10, "cagr": 5.2, "cv": 0.3, "latest_div": 3.5}""" try: if market != "CN": return None # 暂时只 A 股 (akshare) import akshare as ak code = symbol.replace(".SH", "").replace(".SZ", "").replace(".BJ", "") df = ak.stock_history_dividend_detail(symbol=code, indicator="分红") if df is None or len(df) < 3: return None df = df[df["进度"] == "实施"].copy() df["派息"] = pd.to_numeric(df["派息"], errors="coerce") df = df.dropna(subset=["派息"]) df = df[df["派息"] > 0] if len(df) < 3: return None df["年份"] = pd.to_datetime(df["公告日期"]).dt.year df = df.sort_values("年份", ascending=False).reset_index(drop=True) years_count = df["年份"].nunique() latest_div = df["派息"].iloc[0] oldest_div = df["派息"].iloc[-1] years_score = min(30, years_count * 2) # 15 年满分 if years_count >= 2 and oldest_div > 0: cagr = (latest_div / oldest_div) ** (1 / (years_count - 1)) - 1 if cagr >= 0.10: cagr_score = 20 elif cagr >= 0.05: cagr_score = 15 elif cagr >= 0.02: cagr_score = 10 elif cagr >= 0: cagr_score = 5 else: cagr_score = 0 else: cagr = 0 cagr_score = 0 if len(df) >= 3: mean_div = df["派息"].mean() std_div = df["派息"].std() cv = std_div / mean_div if mean_div > 0 else 1 if cv <= 0.2: vol_score = 25 elif cv <= 0.4: vol_score = 20 elif cv <= 0.6: vol_score = 15 elif cv <= 0.8: vol_score = 10 else: vol_score = 5 else: cv = 1 vol_score = 5 recent_score = 15 if (len(df) >= 2 and df["派息"].iloc[0] >= df["派息"].iloc[1]) else 5 consecutive_score = 10 if (years_count >= 3 and len(df["年份"].head(3).unique()) >= 3) else 0 total = years_score + cagr_score + vol_score + recent_score + consecutive_score if total >= 80: level, label = 5, "长期稳定" elif total >= 60: level, label = 4, "基本稳定" elif total >= 40: level, label = 3, "不稳定" elif total >= 20: level, label = 2, "风险大" else: level, label = 1, "不推荐" return {"score": total, "level": level, "label": label, "years": years_count, "cagr": cagr * 100, "cv": cv, "latest_div": latest_div} except Exception as e: print(f" [WARN] score_dividend_stability {symbol} failed: {e}", file=sys.stderr) return None ``` ## 推送格式 集成到 `dividend_alert.py` fmt(): ``` 600033 福建高速 [3★不稳定 25年CAGR-2%] 💰每10股派0.71元 | 📊3.53 | 股息率 2.01% ``` 格式: `[