问题描述
解决方案
这是一个基于 QMT (Quant Trader) 平台的价值投资策略示例。
策略逻辑
- 选股范围:沪深300指数成分股(代表大盘蓝筹,流动性好)。
- 选股因子:市盈率(PE)和 市净率(PB)。
- 筛选规则:
- 剔除市盈率为负(亏损)的股票。
- 按照 市盈率 (PE) 从低到高排序。
- 选取排名前 10 的股票。
- 调仓周期:月度调仓(每个月第一个交易日进行检查和调整)。
- 交易方式:等权重买入。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import time
def init(ContextInfo):
"""
初始化函数,策略启动时执行一次
"""
# 1. 设置策略参数
ContextInfo.stock_num = 10 # 持仓股票数量
ContextInfo.index_code = '000300.SH' # 股票池:沪深300
ContextInfo.last_month = -1 # 用于记录上一次调仓的月份
# 2. 设置账号(回测模式下通常默认为 'test',实盘请修改为真实资金账号)
ContextInfo.accid = 'test'
ContextInfo.set_account(ContextInfo.accid)
print("策略初始化完成,开始运行价值投资策略...")
def handlebar(ContextInfo):
"""
行情事件函数,每根K线执行一次
"""
# 获取当前K线的时间
bar_index = ContextInfo.barpos
timetag = ContextInfo.get_bar_timetag(bar_index)
current_date_str = timetag_to_datetime(timetag, '%Y%m%d')
current_month = int(current_date_str[4:6]) # 提取月份
# --- 1. 调仓时间控制 (月度调仓) ---
# 如果当前月份与上一次调仓月份相同,则跳过,不进行操作
if current_month == ContextInfo.last_month:
return
# 更新调仓月份标记
ContextInfo.last_month = current_month
print(f"=== 检测到新月份 {current_date_str},开始执行选股调仓 ===")
# --- 2. 获取股票池 ---
# 获取沪深300成分股
stock_list = ContextInfo.get_sector(ContextInfo.index_code)
if not stock_list:
print("获取成分股失败或为空")
return
# --- 3. 获取因子数据 (PE 和 PB) ---
# 注意:根据QMT文档,因子字段需使用英文名
# Valuation_and_Market_Cap.PE : 市盈率
# Valuation_and_Market_Cap.PB : 市净率
factor_fields = ['Valuation_and_Market_Cap.PE', 'Valuation_and_Market_Cap.PB']
# 获取当天的因子数据
# 返回格式:pandas.DataFrame index=代码 ,columns=字段
factor_data = ContextInfo.get_factor_data(
factor_fields,
stock_list,
current_date_str,
current_date_str
)
if factor_data.empty:
print("未获取到因子数据")
return
# --- 4. 数据清洗与选股逻辑 ---
df = factor_data.copy()
# 4.1 去除空值
df.dropna(inplace=True)
# 4.2 剔除亏损股 (PE <= 0)
df = df[df['Valuation_and_Market_Cap.PE'] > 0]
# 4.3 按照 PE 从低到高排序 (价值投资核心)
df.sort_values(by='Valuation_and_Market_Cap.PE', ascending=True, inplace=True)
# 4.4 选取排名前 N 的股票
target_stocks = df.head(ContextInfo.stock_num).index.tolist()
print(f"本期选中股票 ({len(target_stocks)}只): {target_stocks}")
# --- 5. 执行交易 (等权重) ---
if len(target_stocks) > 0:
target_weight = 1.0 / len(target_stocks) # 单只股票目标仓位权重
# 5.1 卖出不在目标列表中的持仓
# 获取当前持仓
positions = get_trade_detail_data(ContextInfo.accid, 'stock', 'position')
current_holdings = [obj.m_strInstrumentID for obj in positions]
for stock in current_holdings:
if stock not in target_stocks:
# 目标权重设为0,即清仓
order_target_percent(stock, 0, ContextInfo, ContextInfo.accid)
print(f"卖出非目标股: {stock}")
# 5.2 买入/调整目标股票
for stock in target_stocks:
# 目标权重下单,QMT会自动计算需要买卖的数量
order_target_percent(stock, target_weight, ContextInfo, ContextInfo.accid)
print(f"调整 {stock} 仓位至 {target_weight:.2%}")
代码关键点解析
-
# -*- coding: gbk -*-:- QMT 的 Python 编辑器默认编码通常是 GBK,加上这行可以防止中文注释乱码。
-
ContextInfo.get_factor_data:- 这是 QMT 获取基本面数据的核心接口。我们使用了
Valuation_and_Market_Cap.PE(市盈率)作为核心筛选指标。 - 注意:在使用此接口前,请务必在 QMT 客户端菜单中点击 【数据管理】 -> 【补充数据】 -> 【多因子数据】,下载对应时间段的数据,否则回测时会取不到数据。
- 这是 QMT 获取基本面数据的核心接口。我们使用了
-
order_target_percent:- 这是一个非常方便的交易函数。你只需要告诉它你希望某只股票占总资产的百分比(例如 10%),它会自动计算当前持仓与目标持仓的差额,并自动发出买单或卖单。
-
调仓逻辑:
- 代码中使用
current_month与last_month对比。一旦月份发生变化(例如从 1月变到 2月),就会触发一次选股和交易。
- 代码中使用
如何在 QMT 中运行
- 打开 QMT 客户端,进入 【模型研究/策略研究】。
- 新建一个 Python 策略。
- 将上述代码复制粘贴到编辑器中。
- 补充数据:确保你已经下载了“沪深300”成分股的历史行情数据和多因子数据。
- 设置回测参数:
- 周期:选择 日线 (1d)。
- 时间:选择一段历史时间(例如最近2年)。
- 基准:沪深300。
- 点击 【回测】 查看策略表现。
常见问题 Q&A
Q: 为什么回测没有交易记录?
A: 最大的可能是没有下载多因子数据。请去数据管理界面下载。另外,请检查回测时间段内是否包含交易日。
Q: 如何修改为按 PB(市净率)选股?
A: 将代码中排序的那一行:
df.sort_values(by='Valuation_and_Market_Cap.PE', ...)
修改为:
df.sort_values(by='Valuation_and_Market_Cap.PB', ...) 即可。
Q: 实盘可以直接用吗?
A: 实盘需要将 init 函数中的 ContextInfo.accid = 'test' 修改为您真实的资金账号,并将策略运行模式切换为“实盘”或“模拟”。建议先在模拟盘充分测试。