低 PE 个股批量筛选与"低 PE 陷阱"识别工具(教学级量化价值投资原型)
内容包含免责声明和风险提示,不荐股、不推荐任何投资产品、不引导开户、无任何引流。
一、实际应用场景描述
在智能证券投资课程中,价值投资(Value Investing)与估值陷阱(Value Trap)是核心教学内容。
本程序适用于:
- 高校量化投资、证券分析课程实验
- 投资者教育(Investor Education)
- 基本面量化筛选教学
- 批判性思维训练(打破"低 PE = 便宜"的迷思)
核心目标:
- 批量筛选低 PE(市盈率)个股
- 统计后续一年超额收益(相对大盘)
- 识别低 PE 陷阱(低 PE 但持续跑输)
- 用数据回答:"PE 越低,股票越值得买吗?"
✅ 不做未来预测
✅ 不构成投资建议
✅ 仅作为历史数据统计教学工具
二、痛点引入(真实可感知)
痛点 表现
PE 迷信 "PE 才 5 倍,肯定便宜"
忽略基本面恶化 业绩下滑导致 PE 被动降低
价值陷阱 低 PE 买入后继续下跌
缺乏统计 不知道低 PE 策略长期到底行不行
工具门槛高 专业选股平台复杂,散户望而却步
👉 需要一个轻量、本地、可解释、可复现的低 PE 策略回测工具
三、核心逻辑讲解(工程视角)
1️⃣ 数据模型设计
StockPE
├── symbol 股票代码
├── name 股票名称
├── pe 市盈率(TTM 或静态)
├── year_return 买入后一年涨幅(%)
└── index_return 同期大盘涨幅(%)
2️⃣ 核心筛选逻辑(教学用)
步骤 操作
① 设定 PE 阈值(如 PE < 15)
② 批量筛选符合条件的个股
③ 统计买入后一年涨跌幅
④ 对比同期大盘涨跌幅
⑤ 计算超额收益
3️⃣ 关键公式
超额收益:
超额收益 = 个股一年涨幅 − 大盘同期涨幅
低 PE 陷阱判定(教学用):
if 超额收益 < −10%:
标记为"疑似低 PE 陷阱"
⚠️ 教学中强调:这只是统计信号,不是"绝对真理"。
4️⃣ 统计维度
指标 含义
低 PE 样本数 符合筛选条件的个股数量
平均超额收益 低 PE 组合 vs 大盘
胜率 跑赢大盘的比例
陷阱比例 超额收益 < −10% 的比例
最佳 / 最差个例 极端案例展示
5️⃣ 设计原则
- 不美化低 PE 策略
- 不妖魔化高 PE
- 让数据自己说话
四、Python 模块化代码(可直接运行)
📁 项目结构
low_pe_trap_detector/
│
├── main.py
├── models.py
├── screener.py
├── analyzer.py
├── reporter.py
├── storage.py
├── README.md
└── DISCLAIMER.md
✅ models.py(数据建模)
"""
models.py
低 PE 个股数据模型
"""
class StockPE:
"""个股 PE 与后续表现"""
def __init__(self, symbol, name, pe, year_return, index_return):
"""
symbol: 股票代码
name: 股票名称
pe: 市盈率
year_return: 买入后一年涨幅(%)
index_return: 同期大盘涨幅(%)
"""
self.symbol = symbol
self.name = name
self.pe = pe
self.year_return = year_return
self.index_return = index_return
def excess_return(self):
"""超额收益 = 个股 − 大盘"""
return self.year_return - self.index_return
✅ screener.py(PE 筛选器)
"""
screener.py
低 PE 个股批量筛选
"""
def screen_low_pe(stocks, pe_threshold=15):
"""
筛选 PE 低于阈值的个股
"""
return [s for s in stocks if s.pe < pe_threshold and s.pe > 0]
✅ analyzer.py(核心分析引擎)
"""
analyzer.py
低 PE 策略效果与陷阱识别
"""
import numpy as np
def analyze(stocks, pe_threshold=15):
"""对低 PE 样本进行统计分析"""
screened = [s for s in stocks if s.pe < pe_threshold and s.pe > 0]
total = len(screened)
if total == 0:
return None
# 超额收益
excess_returns = [s.excess_return() for s in screened]
# 胜率
win_count = sum(1 for er in excess_returns if er > 0)
win_rate = win_count / total * 100
# 低 PE 陷阱(超额收益 < −10%)
trap_count = sum(1 for er in excess_returns if er < -10)
trap_rate = trap_count / total * 100
# 跑输大盘比例
lose_count = sum(1 for er in excess_returns if er < 0)
lose_rate = lose_count / total * 100
return {
"pe_threshold": pe_threshold,
"total_screened": total,
"avg_excess_return": round(np.mean(excess_returns), 2),
"median_excess_return": round(np.median(excess_returns), 2),
"win_rate": round(win_rate, 2),
"lose_rate": round(lose_rate, 2),
"trap_rate": round(trap_rate, 2),
"max_excess": round(max(excess_returns), 2),
"min_excess": round(min(excess_returns), 2),
"std_excess": round(np.std(excess_returns), 2)
}
✅ reporter.py(分析报告输出)
"""
reporter.py
低 PE 策略分析报告 + 陷阱识别
"""
def report(stocks, stats, pe_threshold):
print("\n" + "=" * 65)
print("【低 PE 策略效果与陷阱识别报告】")
print("=" * 65)
# 逐条明细
print(f"\n📋 低 PE 个股明细(PE < {pe_threshold}):")
print("-" * 65)
for s in stocks:
if s.pe >= pe_threshold or s.pe <= 0:
continue
er = round(s.excess_return(), 2)
status = "✅ 跑赢" if er > 0 else "❌ 跑输"
trap = " ⚠️ 陷阱" if er < -10 else ""
print(f" {s.symbol} {s.name}")
print(f" PE:{s.pe} | 个股涨幅:{s.year_return}%")
print(f" 大盘涨幅:{s.index_return}% | 超额:{er}% {status}{trap}")
if stats is None:
print(f"\n⚠️ PE < {pe_threshold} 的个股数量为 0,无法统计")
return
# 汇总
print(f"\n📊 统计汇总(PE < {stats['pe_threshold']}):")
print("-" * 65)
print(f" 筛选样本数:{stats['total_screened']} 只")
print(f" 平均超额收益:{stats['avg_excess_return']}%")
print(f" 中位超额收益:{stats['median_excess_return']}%")
print(f" 胜率(跑赢大盘):{stats['win_rate']}%")
print(f" 跑输大盘比例:{stats['lose_rate']}%")
print(f" 低 PE 陷阱比例(超额 < −10%):{stats['trap_rate']}%")
print(f" 最佳个例:+{stats['max_excess']}%")
print(f" 最差个例:{stats['min_excess']}%")
print(f" 超额收益标准差:{stats['std_excess']}%")
print("\n" + "=" * 65)
# 教学结论
print(f"\n💡 教学启示:")
print("-" * 65)
if stats['win_rate'] < 50:
print(f" ⚠️ 胜率仅 {stats['win_rate']}%,低 PE 策略并非稳赚")
elif stats['win_rate'] < 60:
print(f" ⚠️ 胜率 {stats['win_rate']}% 仅略高于随机")
if stats['trap_rate'] > 20:
print(f" ⚠️ {stats['trap_rate']}% 的低 PE 个股超额收益 < −10%")
print(f" → 存在明显的"价值陷阱"风险")
if stats['avg_excess_return'] < 0:
print(f" ⚠️ 平均超额收益为负({stats['avg_excess_return']}%)")
print(f" → 低 PE 组合整体跑输大盘")
if stats['std_excess'] > 20:
print(f" ⚠️ 超额收益离散度大(σ = {stats['std_excess']}%)")
print(f" → 个股分化严重,需精选而非"一篮子"买入")
print(f"\n 核心结论:")
print(f" PE 只是估值的一个维度,低 PE ≠ 便宜 ≠ 值得买。")
print(f" 需结合净利润质量、行业前景、现金流等综合判断。")
print("=" * 65)
✅ storage.py(本地存储)
"""
storage.py
JSON 本地存储
"""
import json
FILE_PATH = "low_pe_analysis.json"
def save_result(data):
with open(FILE_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
✅ main.py(交互入口)
"""
main.py
低 PE 个股筛选与陷阱识别工具
"""
from models import StockPE
from screener import screen_low_pe
from analyzer import analyze
from reporter import report
from storage import save_result
def main():
print("=== 低 PE 策略效果与陷阱识别工具(教学版)===")
print("量化「市盈率越低股票越值得买」是否成立\n")
stocks = []
while True:
symbol = input("股票代码(空结束):")
if not symbol:
break
name = input("股票名称:")
pe = float(input("市盈率(PE):"))
year_ret = float(input("买入后一年涨幅(%):"))
index_ret = float(input("同期大盘涨幅(%):"))
stocks.append(StockPE(symbol, name, pe, year_ret, index_ret))
if not stocks:
print("⚠️ 未录入数据,退出")
return
pe_threshold = float(input("PE 筛选阈值(默认 15):") or "15")
# 筛选
screened = screen_low_pe(stocks, pe_threshold)
# 分析
stats = analyze(stocks, pe_threshold)
# 输出报告
report(screened, stats, pe_threshold)
# 保存结果
result_data = {
"pe_threshold": pe_threshold,
"total_stocks": len(stocks),
"screened_count": len(screened),
"stats": stats,
"details": [
{
"symbol": s.symbol,
"name": s.name,
"pe": s.pe,
"year_return": s.year_return,
"index_return": s.index_return,
"excess_return": round(s.excess_return(), 2)
}
for s in screened
]
}
save_result(result_data)
print("\n✅ 分析结果已保存")
if __name__ == "__main__":
main()
五、README 与使用说明
# 低 PE 策略效果与陷阱识别工具(教学版)
## 项目说明
批量筛选低 PE 个股,统计后续一年超额收益,识别低 PE 价值陷阱。
## 使用方式
```bash
pip install numpy
python main.py
```
## 输入示例
```
股票代码:600519
股票名称:贵州茅台
市盈率(PE):35
买入后一年涨幅(%):18.5
同期大盘涨幅(%):8.2
```
## 核心指标说明
| 指标 | 含义 |
|---|---|
| 超额收益 | 个股涨幅 − 大盘涨幅 |
| 胜率 | 超额收益 > 0 的比例 |
| 低 PE 陷阱 | 超额收益 < −10% 的个股 |
| 中位超额收益 | 剔除极端值后的典型表现 |
## 适用范围
- 量化投资课程
- 价值投资策略教学
- 基本面量化筛选演示
## 注意事项
- 仅基于历史数据
- 不构成任何投资建议
- 使用前请阅读 DISCLAIMER.md
六、DISCLAIMER.md(免责声明与风险提示)
# 免责声明与风险提示
## 免责声明
本程序仅供**教学与科研用途**,用于演示低 PE 策略的统计分析方法。
作者不提供任何证券交易建议,不推荐任何股票,不承诺任何收益。
## 风险提示
1. 历史统计不代表未来表现,"胜率 55%"≠"下次一定对"
2. PE 计算方式多样(静态 / TTM / 动态),结论可能不同
3. 低 PE 可能反映基本面恶化而非"被低估"
4. 行业差异巨大(银行 PE 天然偏低,科技股偏高)
5. 退市股未被计入,可能存在幸存者偏差
6. 一年持有期仅为教学假设,不代表最佳持有周期
使用本工具产生的任何后果,作者概不负责。
七、核心知识点卡片(教学向)
分类 内容
Python 类、列表推导、条件过滤、NumPy 统计
量化金融 PE 估值、超额收益、胜率统计
价值投资 估值陷阱(Value Trap)识别
批判性思维 打破"低 PE = 便宜"的迷思
数据分析 均值 vs 中位数、标准差、分布特征
工程思想 模块化、筛选器与分析师解耦
可扩展性 可接入财务数据 API、支持多因子筛选
八、总结(工程师视角)
这是一个完全中立、去营销化、可教学的原型系统:
✅ 不鼓吹价值投资
✅ 不妖魔化 PE 指标
✅ 不伪装成选股神器
它真正展示的是:
如何用 Python 把"PE 越低越值得买"这个直觉,变成可量化、可检验、可质疑的数据实验
核心教学价值:
传统观念 数据可能揭示的真相
"PE 5 倍,肯定便宜" 可能业绩正在断崖式下滑
"低 PE 就是价值股" 低 PE 组合平均超额收益可能为负
"买低 PE 不会亏" 20%+ 的低 PE 个股超额收益 < −10%
"PE 是万能指标" PE 忽略成长性、现金流、行业差异
低 PE 陷阱的典型成因(教学参考):
成因 机制
业绩恶化 利润下滑 → PE 被动降低 → 看似"便宜"
行业衰退 整个赛道被淘汰(如胶卷、功能机)
财务造假 虚增利润 → 真实 PE 远高于显示值
周期性高点 周期顶部利润最高 → PE 最低 → 买入即套
本文代码仅供学习与技术交流,不构成任何投资建议,股市有风险,入市需谨慎!
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!