问题描述
解决方案
这是一个基于宏观经济数据(PMI)进行择时的策略设计。
策略逻辑
- 数据源:使用国家统计局公布的制造业采购经理指数 (PMI)。
- 核心逻辑:PMI 是衡量制造业荣枯的重要指标。
- PMI > 50:代表经济处于扩张期,股市通常表现较好 -> 全仓买入/持有。
- PMI < 50:代表经济处于收缩期,股市风险较大 -> 清仓/空仓。
- 交易标的:沪深300 ETF(510300.XSHG)作为指数基金代表。
- 调仓频率:按月调仓。通常官方 PMI 数据在每月的最后一天或次月1号公布,因此我们在每月的第一个交易日运行策略。
代码实现
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设置交易标的:沪深300ETF
g.security = '510300.XSHG'
# 设置手续费:股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 设置滑点
set_slippage(PriceRelatedSlippage(0.002), type='stock')
# 设定按月运行,在每月第一个交易日的开盘时运行
run_monthly(trade_func, 1, time='open')
def trade_func(context):
"""
月度调仓函数
"""
# 获取当前的日期
current_date = context.current_dt.date()
# 查询宏观数据:制造业PMI (MAC_MANUFACTURING_PMI)
# 按照统计月份降序排列,取最近的一条数据
# 注意:macro.run_query 会自动根据回测时间 context.current_dt 过滤掉未来数据
q = query(
macro.MAC_MANUFACTURING_PMI
).order_by(
macro.MAC_MANUFACTURING_PMI.stat_month.desc()
).limit(1)
df = macro.run_query(q)
# 如果没有获取到数据,则不进行操作
if df.empty:
log.warn("未获取到PMI数据,跳过本月操作")
return
# 获取PMI数值
current_pmi = df['pmi'][0]
stat_month = df['stat_month'][0]
log.info("当前时间: %s, 获取到的最新PMI数据月份: %s, PMI数值: %s" % (current_date, stat_month, current_pmi))
# 获取当前持仓
position = context.portfolio.positions[g.security]
# 策略逻辑:荣枯线 50
if current_pmi > 50:
# 扩张区间,买入
if position.total_amount == 0:
log.info("PMI > 50 (扩张区间),全仓买入")
order_target_value(g.security, context.portfolio.available_cash)
else:
log.info("PMI > 50 (扩张区间),继续持有")
elif current_pmi <= 50:
# 收缩区间,卖出
if position.total_amount > 0:
log.info("PMI <= 50 (收缩区间),清仓卖出")
order_target(g.security, 0)
else:
log.info("PMI <= 50 (收缩区间),继续空仓")
关键点说明
-
宏观数据获取 (
macro.run_query):- 使用了
macro.MAC_MANUFACTURING_PMI表。 - 聚宽的回测引擎会自动处理“未来函数”。当你调用
macro.run_query时,系统只会返回在context.current_dt之前已经公布的数据。 - 官方 PMI 通常在当月最后一天发布,所以每月1号调仓时通常能获取到上个月的数据。
- 使用了
-
交易逻辑:
- 使用了最经典的荣枯线(50)作为阈值。
- 使用了
order_target_value进行全仓买入,使用order_target进行清仓。
-
标的选择:
- 代码中使用了
510300.XSHG(沪深300ETF) 作为交易对象,这比直接交易指数(如 000300.XSHG,不可交易)更符合实盘逻辑。
- 代码中使用了
常见问题 (Q&A)
Q: 为什么回测时有时候会发现 PMI 数据获取不到?
A: 宏观数据的发布有时间滞后。虽然 PMI 发布较快,但如果回测时间设置得过早(例如2005年以前),可能数据库中缺乏当时的数据。代码中加入了 if df.empty 判断来防止程序报错。
Q: 可以使用财新 PMI 吗?
A: 可以。财新 PMI 通常更能反映中小企业的状况。在聚宽数据库中,可以通过查询 macro.MAC_CAIXIN_PMI 表来获取财新 PMI 数据,逻辑与上述代码完全一致,只需更改查询的表名即可。
Q: 如何避免频繁买卖?
A: 单纯以 50 为界限可能会在 50 附近频繁震荡导致磨损。可以优化策略,例如:
- 加入观察期:连续 2 个月 PMI > 50 才买入。
- 加入趋势判断:PMI > 50 且 PMI > 上月 PMI 才买入。
- 设置缓冲区:PMI > 50.5 买入,PMI < 49.5 卖出。