3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化交易中,月度调仓与资产再平衡是极其常见的策略。在聚宽(JoinQuant)平台上,我们不需要自己去写复杂的交易日判断逻辑,平台提供了强大的定时运行 API run_monthly,可以非常精准、高效地在每月第一个交易日的指定时间执行调仓任务。
本文将为您详细解析如何实现这一功能,并提供一个完整的 ETF 轮动与再平衡策略模板。
run_monthly要在每月第一个交易日执行策略,我们需要在 initialize 初始化函数中注册 run_monthly 函数:
run_monthly(func, monthday, time='9:30', reference_security='000300.XSHG')
func: 您自己定义的调仓执行函数(例如 market_open)。该函数必须且只能接受一个 context 参数。monthday: 设为 1 表示每月的第一个交易日。如果是 -1 则表示每月倒数第一个交易日。time: 具体执行时间,例如 '09:30' 或 '10:00'。reference_security: 时间参照标的,默认是 '000001.XSHG'(上证指数)。对于股票和 ETF 策略,保持默认或使用 '000300.XSHG' 即可。以下是一个实用的 ETF 轮动与等权重再平衡策略。策略在每月第一个交易日 09:30 运行,计算候选 ETF 的过去 20 日收益率,选择表现最好的 2 只 ETF 进行等权重配置,并卖出不在持仓列表中的 ETF。
# 导入聚宽函数库
import pandas as pd
from jqdata import *
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设定交易税费:买入万三,卖出万三加千一印花税
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 定义候选 ETF 资金池
g.etf_pool = [
'510300.XSHG', # 沪深300 ETF
'510500.XSHG', # 中证500 ETF
'159915.XSHE', # 创业板 ETF
'518880.XSHG' # 黄金 ETF
]
# 设定每月持有的最大 ETF 数量
g.max_hold = 2
# 注册定时任务:每月第一个交易日 09:30 执行调仓
run_monthly(handle_etf_rotation, monthday=1, time='09:30', reference_security='000300.XSHG')
def handle_etf_rotation(context):
log.info("----------------- 月度调仓开始 -----------------")
log.info("当前调仓日期: %s" % context.current_dt.date())
# 1. 获取候选 ETF 过去 20 个交易日的收盘价,计算动量(收益率)
momentum_scores = {}
for etf in g.etf_pool:
# 获取历史数据
hist = attribute_history(etf, 20, '1d', ['close'])
if not hist.empty:
# 计算 20 日收益率
ret = (hist['close'][-1] - hist['close'][0]) / hist['close'][0]
momentum_scores[etf] = ret
# 2. 按照收益率从大到小排序,选出前 g.max_hold 个 ETF
sorted_etfs = sorted(momentum_scores.items(), key=lambda x: x[1], reverse=True)
target_list = [etf[0] for etf in sorted_etfs[:g.max_hold]]
log.info("本月目标持仓 ETF: %s" % target_list)
# 3. 卖出不在目标持仓列表中的 ETF
current_positions = list(context.portfolio.positions.keys())
for etf in current_positions:
if etf not in target_list:
order_target(etf, 0)
log.info("卖出不符合轮动条件的 ETF: %s" % etf)
# 4. 计算每个目标 ETF 的等权重价值,并执行再平衡
if len(target_list) > 0:
# 获取当前账户总资产
total_value = context.portfolio.total_value
# 每个 ETF 分配的资金
target_value_per_etf = total_value / len(target_list)
for etf in target_list:
# 调整标的至目标权重价值
order_target_value(etf, target_value_per_etf)
log.info("再平衡买入/调整 ETF: %s 至目标价值: %.2f" % (etf, target_value_per_etf))
log.info("----------------- 月度调仓结束 -----------------")
handle_etf_rotation 中,我们使用 attribute_history 获取历史数据。由于该函数在 09:30 运行,获取的 20 天数据不包含当天未收盘的 Bar,从而有效避免了未来数据。set_option('use_real_price', True)。在进行 ETF 轮动和分红送配时,强烈建议开启此模式,以确保回测和模拟盘的价格与实际市场一致。order_target_value 可以一步到位地将资产调整到目标金额,它会自动计算当前持仓与目标持仓的差额,自动执行买入或卖出,非常适合等权重再平衡策略。