导语:投资大师彼得·林奇有过一个著名的论断:任何一家公司股票如果定价合理的话,市盈率就会与收益增长率相等。本篇内容将为大家讲述彼得·林奇推广的PEG选股策略。
彼得·林奇
彼得·林奇(Peter Lynch)生于1944年1月19日, 是一位卓越的股票投资家和证券投资基金经理, 曾被《时代杂志》评为首席基金经理。1977年至1990年, 在彼得·林奇管理麦哲伦基金的13年间, 基金规模大幅扩增, 由2000万美元成长至140亿美元!他对共同基金的贡献,就像是乔丹之于篮球,邓肯之于现代舞蹈。他不是人们日常认识中的那种脑满肥肠的商人,他把整个比赛提升到一个新的境界,他让投资变成了一种艺术。
彼得·林奇一直以他的选股能力而著称,他有一句名言:只要用心对股票做一点点研究,普通投资者也能成为股票投资专家,并且在选股方面的成绩能像华尔街的专家一样出色。
PEG指标
围绕着彼得·林奇的著名论断,我们来谈谈PEG指标。
论断:任何一家公司股票如果定价合理的话,市盈率就会与收益增长率相等。
了解基础指标:每股收益、市盈率和每股收益增长率。
每股收益(EPS):是税后利润与股本总数的比率。传统的每股收益指标计算公式为:每股收益=期末净利润÷期末股份总数。
每股收益增长率(G):论断中的收益增长率指的就是每股收益的增长率,同比增长率={(本期每股收益-上年同期每股收益)/上年同期每股收益的绝对值} x 100%。
市盈率(PE):是当前股价与每股收益的比值。市盈率对个股、类股及大盘都是很重要参考指标。任何股票若市盈率大大超出同类股票或是大盘,都需要有充分的理由支持,而这往往离不开该公司未来盈利率将快速增长这一重点。一家公司享有非常高的市盈率,说明投资人普遍相信该公司未来每股盈余将快速成长,以至数年后市盈率可降至合理水平。一旦盈利增长不如理想,支撑高市盈率的力量无以为继,股价往往会大幅回落。
PEG指标由上述基础指标演变而来,计算公式为:PEG=PE/G,其中PE为市盈率,G为每股收益增长率。PE代表投资者对公司每股收益增长预期,而G代表公司真实的每股收益增长预期。从合理定价的角度看,个股的市盈率与每股收益增长率应该保持相等。如果PEG小于1,股价是偏低的,反之则偏高。
彼得·林奇PEG选股
PEG选股策略:计算全市场股票的PEG指标,按PEG值从小到大排序,选择前10只股票作为持仓
就这么简单吗?不是的,PEG指标选股具有局限性:
1.PEG指标不合适用于周期性行业选股,因此需要剔除周期性行业内的股票。由于周期性行业非常多,本文选取5个非周期性行业进行PEG选股:食品饮料,医药生物,纺织服装,商业贸易,交通运输 。
2.价值型股票与成长性股票不同,价值型股票由于其成长空间有限,导致价值型股票的PEG往往会低于1,以反映低业绩增长的预期。所有直接使用PEG指标会出现一边倒现象。更何况PEG选股策略主要意图在于挑选出优质成长股。对于个股是否为成长型股票的定义方法并不固定,因此本文采用以下简单粗暴定义(ps:同学们掌握后可以在此处优化哦!)。
3.无法衡量公司的收益增率可持续性,考虑到公司可能出现的扭亏为盈、特殊情况导致单季度收益增率高达1000%+现象,我们需要剔除收益增长率超过40%的个股,因其增长不可持续。
成长型股票池:对所有非周期性行业个股,采用净利润复合增长率、销售净利率两个指标,选取当季两个指标都在前50%的股票。
第一步:获取成长型股票池。
第一步:每隔20个交易日,获取股票池内个股的PE和G值,剔除G大于40的个股,然后计算成长型股票池的PEG指标。
第二步:按PEG值从小到大排序,选择前5只股票作为持仓
以下为策略实现的基本信息:
策略实现难度:3
实现过程中所需要用到的API函数,ps:通过SuperMind量化交易平台API文档快速掌握:
需要用到的API函数 | 功能 |
---|---|
get_industry_stocks() | 获取行业指数成分股 |
get_fundamentals() | 获取财务数据 |
彼得·林奇PEG选股核心是获取成长性股票,本篇内容中粗糙定义了成长型股票,建议初学者前往研究环境操作,并能在此基础上加入自己的一些想法。
以下是作者实现过程中,研究环境的代码草稿,分享给同学们。
#=================获取非周期性行业个股
def non_cyclical_industry(date):
#确定申万一级非周期行业
non_cyclical_industry_list =['S36',#申万一级:纺织服装
'S45',#申万一级:商业贸易
'S37',#申万一级:医药生物
'S34',#申万一级:食品饮料
'S42']#申万一级:交通运输
stock_list=[]
#通过行业成分股获取函数,获取到股票池。
for i in non_cyclical_industry_list:
stk=get_industry_stocks(i,date)
for i in stk:
stock_list.append(i)
return stock_list
date='20180125'
stk=non_cyclical_industry(date)
stock_num = int(len(stk)*0.5)
#获取净利润增长率和销售毛利率
q = query(valuation.symbol,growth.net_profit_compound_growth_ratio,profit.net_profit_margin_on_sales).filter(valuation.symbol.in_(stk))
basic = get_fundamentals(q, date = date)
for i in ['growth_net_profit_compound_growth_ratio','profit_net_profit_margin_on_sales']:
basic = pd.DataFrame(basic).sort_values(by =i, ascending=False)
stockmax=list(basic.iloc[:stock_num]['valuation_symbol'])
stk=list(set(stk)&set(stockmax))
q = query(valuation.symbol,valuation.pe_ttm,growth.basic_eps_year_growth_ratio).filter(valuation.symbol.in_(stk))
basic = get_fundamentals(q, date = '20180125')
basic['PEG']=basic['valuation_pe_ttm']/basic['growth_basic_eps_year_growth_ratio']
basic = basic[basic['PEG']>0]
basic = basic[basic['growth_basic_eps_year_growth_ratio']<50]
basic = pd.DataFrame(basic).sort_values(by ='PEG', ascending=True)
basic.index=basic['valuation_symbol']
del basic['valuation_symbol']
basic.head()
策略回测区间:2022.01.01-2022.12.31
回测资金:1000000
回测频率:日级
回测结果:红色曲线为策略收益率曲线,蓝色曲线为对应的基准指数收益率曲线
策略源代码:
import pandas as pd
import numpy as np
# 初始化函数,全局只运行一次
def init(context):
set_benchmark('399333.SZ')#设置中小板为基准
g.day = 0 #记录运行天数
g.tradeday = 20 #调仓频率
g.stock = [] #储存上期的股票池
g.trade = False #是否调仓的开关
g.stocknum = 10 #持仓数量
pass
#每日开盘前9:00被调用一次,用于储存自定义参数、全局变量,执行盘前选股等
def before_trading(context):
#判断是否调仓
if g.day%g.tradeday==0:
g.trade=True
else:
g.trade=False
g.day=g.day+1
## 开盘时运行函数
def handle_bar(context, bar_dict):
if g.trade==True:
#获取选股结果
needstock_list = peg()
#获取上期持仓个股
holdstock_list = list(g.stock)
#确定本期需要卖出的个股
sell_list = list(set(holdstock_list)-set(needstock_list))
#执行卖出操作,运用for循环,逐个操作。
for s in sell_list:
order_target(s,0)
#确定本期需要买入的个股,其余即为继续持仓的个股
buy_list=[]
for i in needstock_list:
if i in holdstock_list:
pass
else:
buy_list.append(i)
#确定可用资金,平分分配至需买入的个股
n=len(buy_list)
cash=context.portfolio.available_cash/n
#执行买入操作
for s in range(0,n,1):
stock=list(buy_list)[s]
order_value(stock,cash)
#操作完毕,将选股结果放到上期股票池储存变量中,以备下次使用。
g.stock = frozenset(needstock_list)
else:
pass
#选股函数
def peg():
date=get_last_datetime().strftime('%Y%m%d')
#获取非周期行业股票。
stock_list1=non_cyclical_industry(date)
#获取成长型股票池
stock_list2=growth_list(stock_list1,date)
#获取PEG最小的10只股票
q = query(valuation.symbol,valuation.pe_ttm,growth.basic_eps_year_growth_ratio).filter(valuation.symbol.in_(stock_list2))
basic = get_fundamentals(q, date = date)
basic['PEG']=basic['valuation_pe_ttm']/basic['growth_basic_eps_year_growth_ratio']
basic = basic[basic['PEG']>0]
basic = basic[basic['growth_basic_eps_year_growth_ratio']<=40]
basic = pd.DataFrame(basic).sort_values(by ='PEG', ascending=True)
basic.index=basic['valuation_symbol']
#获取股票的代码
needstock_list=[]
for s in range(0,g.stocknum,1):
needstock_list.append(list(basic.index)[s])
return needstock_list
#获取成长型股票池
def growth_list(stk,date):
stock_num = int(len(stk)*0.5)
#获取净利润增长率和销售毛利率
q = query(valuation.symbol,
growth.net_profit_growth_ratio,
profit.net_profit_margin_on_sales).filter(valuation.symbol.in_(stk))
basic = get_fundamentals(q, date = date)
for i in ['growth_net_profit_growth_ratio','profit_net_profit_margin_on_sales']:
basic = pd.DataFrame(basic).sort_values(by =i, ascending=False)
stockmax=list(basic.iloc[:stock_num]['valuation_symbol'])
stk=list(set(stk)&set(stockmax))
return stk
#获取非周期性行业个股
def non_cyclical_industry(date):
#确定申万一级非周期行业
non_cyclical_industry_list =['S36',#申万一级:纺织服装
'S45',#申万一级:商业贸易
'S37',#申万一级:医药生物
'S34',#申万一级:食品饮料
'S42']#申万一级:交通运输
stock_list=[]
#通过行业成分股获取函数,获取到股票池。
for i in non_cyclical_industry_list:
stk=get_industry_stocks(i,date)
for i in stk:
stock_list.append(i)
return stock_list