问题描述
解决方案
在 QMT 平台中编写 PEG(市盈率相对盈利增长比率)选股策略,核心在于获取 PE(市盈率) 和 G(净利润增长率) 数据。
根据 QMT API 文档,获取这两个指标最直接且高效的方法是使用 多因子数据接口 ContextInfo.get_factor_data。该接口可以直接返回经过清洗和计算的因子数据,避免了手动查询财务报表并计算的繁琐过程。
1. 数据获取方法
你需要使用以下两个核心步骤:
-
确定因子名称:
- PE (市盈率):在“股票因子_估值与市值”库中,字段名为
Valuation_and_Market_Cap.PE(通常指 TTM 或静态 PE,具体取决于数据源定义,QMT 默认 PE 通常较为通用)。 - G (增长率):在“股票因子_成长能力”库中,最常用的是“归属母公司股东的净利润增长率”,字段名为
Growth.NPParentCompanyGrowRate。
- PE (市盈率):在“股票因子_估值与市值”库中,字段名为
-
调用接口:
使用ContextInfo.get_factor_data(fieldList, stockList, startDate, endDate)获取数据。
2. PEG 策略实现代码
下面是一个完整的 PEG 选股策略示例。该策略逻辑如下:
- 选股范围:沪深 300 成分股。
- 计算 PEG:$PEG = \frac{PE}{Growth \times 100}$。
- 过滤条件:
- PE > 0(剔除亏损股)。
- Growth > 0(剔除负增长)。
- 0 < PEG < 1(寻找低估值高增长标的)。
- 交易逻辑:每月初调仓,买入 PEG 最小的前 10 只股票。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
# 设置账号(请替换为您的实际账号)
ContextInfo.accid = '6000000000'
ContextInfo.set_account(ContextInfo.accid)
# 策略参数设置
ContextInfo.target_num = 10 # 持仓最大数量
ContextInfo.trade_period = '1m' # 运行周期,建议日线或月线
# 设置股票池为沪深300
ContextInfo.index_code = '000300.SH'
print("PEG策略初始化完成")
def handlebar(ContextInfo):
# 获取当前K线的时间
index = ContextInfo.barpos
timetag = ContextInfo.get_bar_timetag(index)
current_date = timetag_to_datetime(timetag, '%Y%m%d')
# 简单的月度调仓逻辑:仅在每月第一个交易日运行
# 注意:这里使用简单的逻辑判断,实际回测建议配合定时器或更严格的日期判断
if not ContextInfo.is_new_bar():
return
# 获取沪深300成分股
stock_list = ContextInfo.get_sector(ContextInfo.index_code, timetag)
if not stock_list:
print("未获取到成分股")
return
# ----------------------------------------------------------------
# 核心步骤:获取 PE 和 增长率 数据
# ----------------------------------------------------------------
# 字段定义:
# Valuation_and_Market_Cap.PE : 市盈率
# Growth.NPParentCompanyGrowRate : 归属母公司股东的净利润增长率
fields = ['Valuation_and_Market_Cap.PE', 'Growth.NPParentCompanyGrowRate']
# 获取因子数据 (返回 DataFrame)
# 注意:get_factor_data 获取的是区间数据,这里我们取当日数据
factor_data = ContextInfo.get_factor_data(fields, stock_list, current_date, current_date)
if factor_data.empty:
print(f"{current_date} 无因子数据")
return
# 数据处理:将数据转为 DataFrame 格式方便计算
# get_factor_data 返回格式较为复杂,通常是 dict 或 multi-index DataFrame
# 这里我们假设返回的是以股票代码为 Index,因子名为 Columns 的 DataFrame (当时间为单点时)
# 如果返回的是 Dict (key为股票代码),需要转换
df_factors = pd.DataFrame(index=stock_list)
# 填充数据
for stock in stock_list:
if stock in factor_data:
# 注意:根据API返回结构,这里可能需要适配
# 如果 factor_data 是 dict: {stock: df}
stock_df = factor_data[stock]
if not stock_df.empty:
df_factors.loc[stock, 'PE'] = stock_df['Valuation_and_Market_Cap.PE'].iloc[-1]
df_factors.loc[stock, 'G'] = stock_df['Growth.NPParentCompanyGrowRate'].iloc[-1]
# ----------------------------------------------------------------
# 计算 PEG 指标
# ----------------------------------------------------------------
# 剔除无效数据 (NaN)
df_factors.dropna(inplace=True)
# 过滤逻辑:
# 1. PE 必须为正 (亏损股没有计算 PEG 的意义)
# 2. 增长率 G 必须为正
df_factors = df_factors[(df_factors['PE'] > 0) & (df_factors['G'] > 0)]
# 计算 PEG = PE / (G * 100)
# 注意:QMT中增长率通常是小数(如0.2代表20%),PEG公式中G通常取整数(20),所以要乘以100
df_factors['PEG'] = df_factors['PE'] / (df_factors['G'] * 100)
# 筛选 PEG 在 0 到 1 之间的低估值股票
target_stocks_df = df_factors[(df_factors['PEG'] > 0) & (df_factors['PEG'] < 1)]
# 按 PEG 从小到大排序,取前 N 只
target_stocks_df = target_stocks_df.sort_values(by='PEG', ascending=True)
buy_list = target_stocks_df.head(ContextInfo.target_num).index.tolist()
print(f"日期: {current_date}, 选股数量: {len(buy_list)}")
print(f"选股列表: {buy_list}")
# ----------------------------------------------------------------
# 交易执行
# ----------------------------------------------------------------
# 获取当前持仓
positions = ContextInfo.get_trade_detail_data(ContextInfo.accid, 'stock', 'position')
holding_list = [obj.m_strInstrumentID for obj in positions if obj.m_nVolume > 0]
# 卖出不在买入列表中的股票
for stock in holding_list:
if stock not in buy_list:
order_target_value(stock, 0, ContextInfo, ContextInfo.accid)
print(f"卖出: {stock}")
# 买入选中的股票
if len(buy_list) > 0:
# 简单的等权重分配资金
# 获取账户总资产
account_info = ContextInfo.get_trade_detail_data(ContextInfo.accid, 'stock', 'account')
total_asset = account_info[0].m_dBalance if account_info else 1000000
per_stock_value = total_asset / len(buy_list)
for stock in buy_list:
order_target_value(stock, per_stock_value, ContextInfo, ContextInfo.accid)
print(f"买入: {stock}, 目标金额: {per_stock_value}")
3. 代码关键点解析
-
ContextInfo.get_factor_data的使用:- 这是获取 PEG 所需数据的核心。
Valuation_and_Market_Cap.PE:获取市盈率。Growth.NPParentCompanyGrowRate:获取净利润增长率。- 注意:在使用此接口前,请确保在 QMT 客户端菜单中点击 【数据管理】 -> 【补充数据】 -> 【多因子数据】,下载相应时间段的数据,否则回测或运行时会取不到值。
-
PEG 计算公式:
- 代码中使用了
df_factors['PE'] / (df_factors['G'] * 100)。 - 这是因为 QMT 返回的增长率通常是小数形式(例如 0.5 代表 50%),而经典的 PEG 计算中分母通常直接使用百分比的数值(即 50),因此需要乘以 100。
- 代码中使用了
-
数据清洗:
- PEG 指标对于亏损股(PE < 0)或负增长股(G < 0)没有参考意义,因此代码中通过
df_factors[(df_factors['PE'] > 0) & (df_factors['G'] > 0)]进行了剔除。
- PEG 指标对于亏损股(PE < 0)或负增长股(G < 0)没有参考意义,因此代码中通过
-
交易执行:
- 使用了
order_target_value函数进行调仓,这是一种目标价值下单方式,能够自动计算买卖数量将持仓调整到指定金额,非常适合这种定期轮动策略。
- 使用了
Q&A
Q: 为什么我运行代码时提示获取不到因子数据?
A: QMT 的多因子数据不是默认下载的。你需要手动去客户端的“数据管理”界面,选择“多因子数据”,并勾选你需要的时间段进行下载补充。
Q: 可以使用 TTM(滚动)市盈率吗?
A: 可以。虽然文档中 Valuation_and_Market_Cap.PE 通常即为 TTM 或包含 TTM 逻辑,但如果你想更精确地控制,可以使用 Valuation_and_Market_Cap.PE 配合财务数据自己计算,或者查看因子库中是否有明确标注 PE_TTM 的字段(不同券商版本的 QMT 因子库略有差异,建议在公式编辑器中查看具体的因子列表)。
Q: 如何获取预测的增长率而不是历史增长率?
A: 上述代码使用的是历史财务数据的增长率。如果需要预测数据,可以使用“股票因子_分析师预期”库中的字段,例如 Analyst_Estimation.FEARNG (未来预期盈利增长),但这需要你的 QMT 账号有相应的分析师预期数据权限。