原创 | 来自北方狼 · StockStudy园地 | 2026年8月5日 17:41 重庆
前置说明:如需要下载本文代码(共有 360M 左右,基础数据),请大家关注微信公众号:“StockStudy园地” 后台回复:
【核心观点】
前言
投资最核心的难点,是在纷繁复杂的市场中找到稳定赚钱的公司。很多散户追热点、炒题材,最后只拿到纸面浮盈,拿不到真实现金流。先锋集团创始人约翰·博格提出的博格公式,剥离股价短期波动噪音,只抓长期收益两大核心来源,普通人零基础也能学会,既可手动在炒股软件测算,也能用 Python + Tushare 全自动批量选股。
一、通俗故事:看懂博格公式底层逻辑
假设两个合作对象:
理性人都会选择 A,但股市里大量投资者偏爱"讲故事、无分红"的 B 类公司。博格公式就是一套量化筛选标准,专门锁定持续分红、盈利稳定增长的优质企业。
约翰·博格核心结论:持有周期拉长至 10 年以上,市盈率估值涨跌会相互抵消,股票长期年化收益仅由两部分决定:现金分红、企业净利润长期增长。
简化核心公式(个股长期测算版):
预期年化收益率 ≈ TTM 股息率 + 保守修正永续利润增速
判定标准:两者相加 ≥ 10%,纳入长期观察池。
二、三大核心基础概念(大众易懂版)
2.1 TTM 股息率(确定现金收益)
- 个股全年两次分红:10 派 5(每股 0.5 元)、10 派 3(每股 0.3 元),全年合计每股分红 0.8 元,现价 16 元。
- TTM 股息率 = 0.8 ÷ 16 = 5%;
2.2 CAGR 复合年均增长率(剔除短期水分)
CAGR = (期末值 / 期初值)^(1 / 年数) – 1
2.3 保守永续增速(历史增速打折,理性预判未来)
核心逻辑:历史增速越高,长期维持难度越大,必须阶梯打折,同时设置上限 15%(没有企业能永久超 15% 复利增长),亏损企业保底 -5%。
| >25% | 35% | 历史 50% 增速,折算后 17.5% |
| >20% | 45% | 历史 30% 增速,折算后 13.5% |
| >15% | 55% | 历史 20% 增速,折算后 11% |
| >10% | 65% | 历史 15% 增速,折算后 9.75% |
| >5% | 75% | 历史 8% 增速,折算后 6% |
| >0% | 85% | 历史 3% 增速,折算后 2.55% |
| ≤0% | 保底 -5% | 盈利下滑,最低按 -5% 计算 |
三、通达信软件手工完整测算流程(长城证券烽火版通用)
步骤 1:提取 TTM 股息率
步骤 2:提取 7 年净利润同比增速
当年净利润增速 = (当年净利润 – 上年净利润) ÷ |上年净利润| × 100%
步骤 3:分别计算 3 / 5 / 7 年期 CAGR
统一使用复利开方公式,取完整年度净利润增速数据代入。
步骤 4:加权合并多周期增速
权重分配:7 年 CAGR × 0.4 + 5 年 CAGR × 0.35 + 3 年 CAGR × 0.25
示例数据:7 年 CAGR 13.38%、5 年 14.72%、3 年 11.29%
加权原始 CAGR = 13.38 × 0.4 + 14.72 × 0.35 + 11.29 × 0.25 = 13.33%
步骤 5:阶梯衰减打折
13.33% 落在 10%~15% 区间,留存 65%:
衰减后增速 = 13.33% × 0.65 = 8.66%
步骤 6:增速封顶限制
保守永续增速 = min(衰减后数值,15%) = 8.66%
步骤 7:博格公式计算预期年化收益
预期年化 = TTM 股息率 + 保守永续增速
若个股股息率 5%,总收益 = 5% + 8.66% = 13.66% ≥ 10%,达标入选。
四、Python + Tushare 自动化选股完整方案
4.1 数据接口对应说明(Tushare 函数对照表)
| TTM 股息率、股价、市值、PE/PB | daily_basic | dv_ratio | dv_ratio 原生为 TTM 股息率,与手工计算口径一致 |
| 年报净利润同比增速 | fina_indicator | netprofit_yoy | 仅筛选 end_date=1231 的年度数据,剔除季报 |
| 股票名称、行业分类 | stock_basic | ts_code、name、industry | 个股基础信息补充 |
| ROE、净利率 | fina_indicator | roe、netprofit_margin | 辅助基本面校验指标 |
4.2 完整代码分步实现
1)环境初始化与 Token 配置
import tushare as ts
import pandas as pd
import numpy as np
import math
# 替换为个人Tushare Pro Token
ts.set_token('你的Tushare Token')
pro = ts.pro_api()
2)单只股票提取 7 年年报净利润同比增速
def get_yoy_data(ts_code):
"""获取单只股票近7年年报净利润同比增速"""
df = pro.fina_indicator(
ts_code=ts_code,
fields='ts_code,end_date,netprofit_yoy,ann_date'
)
# 仅保留12月31日年报数据
df_annual = df[df['end_date'].str.endswith('1231')]
# 按公告日期去重,保留最新年报
df_annual = df_annual.sort_values('ann_date', ascending=False)
df_annual = df_annual.drop_duplicates(subset=['end_date'])
if df_annual.empty:
return [], 0
latest_year = int(df_annual['end_date'].iloc[0][:4])
yoy_list = []
# 提取近7年数据
for year in range(latest_year – 6, latest_year + 1):
row = df_annual[df_annual['end_date'].str.startswith(str(year))]
if not row.empty and pd.notna(row.iloc[0]['netprofit_yoy']):
yoy_val = float(row.iloc[0]['netprofit_yoy'])
yoy_list.append(yoy_val)
return yoy_list, latest_year
3)CAGR 计算函数(极端增速约束)
def calc_cagr(yoy_list):
"""计算单周期复合年均增速,限制极端波动"""
if not yoy_list or len(yoy_list) < 3:
return None
# 约束增速极值,避免异常值干扰
ratios = []
for y in yoy_list:
y_clamp = max(–95, min(300, y))
ratios.append(1 + y_clamp / 100)
product = 1.0
for r in ratios:
product *= r
years = len(yoy_list)
return product ** (1 / years) – 1
def get_cagrs(yoy_list):
"""分别输出3/5/7年期CAGR"""
cagr_3y = calc_cagr(yoy_list[–3:]) if len(yoy_list) >= 3 else None
cagr_5y = calc_cagr(yoy_list[–5:]) if len(yoy_list) >= 5 else None
cagr_7y = calc_cagr(yoy_list[–7:]) if len(yoy_list) >= 7 else None
return cagr_3y, cagr_5y, cagr_7y
4)历史增速加权 + 阶梯衰减 + 封顶函数
def conservative_growth(cagr_3y, cagr_5y, cagr_7y):
# 加权计算
weight_map = []
if cagr_7y is not None:
weight_map.append((cagr_7y * 100, 0.40))
if cagr_5y is not None:
weight_map.append((cagr_5y * 100, 0.35))
if cagr_3y is not None:
weight_map.append((cagr_3y * 100, 0.25))
if not weight_map:
return 0.0
total_w = sum(w for _, w in weight_map)
weighted_cagr = sum(v * w for v, w in weight_map) / total_w
# 阶梯打折
if weighted_cagr > 25:
decayed = weighted_cagr * 0.35
elif weighted_cagr > 20:
decayed = weighted_cagr * 0.45
elif weighted_cagr > 15:
decayed = weighted_cagr * 0.55
elif weighted_cagr > 10:
decayed = weighted_cagr * 0.65
elif weighted_cagr > 5:
decayed = weighted_cagr * 0.75
elif weighted_cagr > 0:
decayed = weighted_cagr * 0.85
else:
decayed = max(weighted_cagr, –5.0)
# 增速上限15%
capped = min(decayed, 15.0)
return capped
5)主选股函数(批量全市场筛选)
def bogle_screening(target_return=10.0, trade_date="20260804"):
# 获取全市场每日基础指标,过滤股息率≥0.5%
df_daily = pro.daily_basic(
trade_date=trade_date,
fields='ts_code,dv_ratio,close,pe_ttm,pb,total_mv'
)
df_daily = df_daily[df_daily['dv_ratio'] > 0.5]
result_list = []
total_count = len(df_daily)
# 遍历每只股票计算博格预期收益
for idx, row in df_daily.iterrows():
ts_code = row['ts_code']
dv = row['dv_ratio']
yoy_data, _ = get_yoy_data(ts_code)
if len(yoy_data) < 3:
continue
c3, c5, c7 = get_cagrs(yoy_data)
safe_growth = conservative_growth(c3, c5, c7)
exp_return = dv + safe_growth
result_list.append({
"ts_code": ts_code,
"TTM股息率": round(dv, 3),
"保守永续增速%": round(safe_growth, 3),
"博格预期年化%": round(exp_return, 3),
"3年CAGR%": round(c3*100, 2) if c3 else None,
"5年CAGR%": round(c5*100, 2) if c5 else None,
"7年CAGR%": round(c7*100, 2) if c7 else None
})
# 每100只打印进度
if (idx + 1) % 100 == 0:
print(f"已处理 {idx+1}/{total_count}")
# 筛选达标标的并排序
df_res = pd.DataFrame(result_list)
df_pass = df_res[df_res["博格预期年化%"] >= target_return]
df_pass = df_pass.sort_values("博格预期年化%", ascending=False)
return df_pass
# 执行选股,trade_date替换为最新交易日
qualified_df = bogle_screening(target_return=10.0, trade_date="20260804")
print(f"博格公式筛选达标个股总数:{len(qualified_df)}")
print(qualified_df.head(20))
6)补充个股行业、ROE、净利率辅助信息
def enrich_fundamental(df):
enrich_list = []
for _, row in df.iterrows():
code = row["ts_code"]
# 获取股票名称、行业
stock_info = pro.stock_basic(ts_code=code, fields="ts_code,name,industry")
name = stock_info.iloc[0]["name"] if not stock_info.empty else ""
industry = stock_info.iloc[0]["industry"] if not stock_info.empty else ""
# 获取最新ROE、净利率
fin_ind = pro.fina_indicator(ts_code=code, limit=1, fields="roe,netprofit_margin")
roe = round(fin_ind.iloc[0]["roe"], 2) if not fin_ind.empty else None
margin = round(fin_ind.iloc[0]["netprofit_margin"], 2) if not fin_ind.empty else None
line = row.to_dict()
line.update({"股票名称": name, "行业": industry, "最新ROE%": roe, "净利率%": margin})
enrich_list.append(line)
return pd.DataFrame(enrich_list)
# 调用补充信息
full_df = enrich_fundamental(qualified_df)
# 导出Excel本地保存
full_df.to_excel("博格公式选股结果.xlsx", index=False)
五、关键口径统一说明(手工计算与 Tushare 对齐)
六、博格公式选股局限性与风险提示
1)适用范围限制
- 仅适配持续稳定盈利、常年现金分红的消费、公用、高股息蓝筹;
- 强周期行业(有色、煤炭、钢铁)净利润周期性大幅波动,CAGR 失真,筛选结果参考性低;
- 银行、保险等金融行业财务指标特殊,ROE、盈利增速解读需额外专业分析;
- 上市未满 3 年新股、常年亏损无分红企业无法使用该模型。
2)模型本身短板
博格公式只是量化初筛工具,非直接买入决策依据,筛选出标的后必须额外人工复核:
3)投资核心逻辑重申
博格公式放弃预测短期股价涨跌、主力资金、题材热点,只锚定两个长期核心:
长期投资不需要复杂指标堆砌,抓住现金流与成长两大锚点,即可大幅规避题材炒作陷阱。
七、文档总结
数据来源:Tushare Pro 金融数据 API
创作时间:2026-08-05
附:程序运行后,选出的部分股票参考(详见公众号后台回复"博格选股结果"下载)。
本文转载自微信公众号「StockStudy园地」(作者:北方狼),原文发布于 2026年8月5日。仅供学习交流,版权归原作者所有。
网硕互联帮助中心



评论前必须登录!
注册