3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化交易中,ETF 轮动策略因其能有效分散个股风险、捕捉行业板块轮动红利而广受欢迎。然而,传统的等权重或简单动量轮动在面对市场剧烈波动时,往往面临回撤过大和调仓损耗高的问题。本文将介绍如何在聚宽(JoinQuant)平台上,结合止盈止损机制与投资组合优化器(Portfolio Optimizer),构建一个高回撤控制力的动态再平衡 ETF 轮动策略。
标的选择(ETF 资金池):
精选代表性强、流动性好的行业 ETF(如消费、科技、医药、金融等)以及宽基/商品 ETF(如沪深300 ETF、黄金 ETF)构建轮动池。
动态再平衡(Portfolio Optimizer):
利用聚宽内置的 jqlib.optimizer 库,采用**最大夏普比率(MaxSharpeRatio)或最小方差(MinVariance)**模型,在满足权重约束(如单只 ETF 权重不超过 20%)的前提下,每月初动态计算最优配置权重,实现资产的动态再平衡。
多维度止盈止损机制:
get_current_data() 监控盘中价格,若某只持仓 ETF 相比买入均价(position.avg_cost)跌幅超过设定的阈值(如 -8%),则立即触发市价单平仓。以下是完整的策略代码,可直接复制到聚宽回测环境中运行:
# 导入聚宽函数库与优化器
import pandas as pd
import numpy as np
from jqdata import *
from jqlib.optimizer import *
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设定交易税费:买入万三,卖出万三(ETF免印花税)
set_order_cost(OrderCost(close_tax=0.0, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='fund')
# 策略参数设置
g.etf_pool = [
'510300.XSHG', # 沪深300 ETF
'510500.XSHG', # 中证500 ETF
'512880.XSHG', # 证券ETF
'512010.XSHG', # 医药ETF
'512660.XSHG', # 军工ETF
'510810.XSHG', # 上海红利 ETF
'518880.XSHG', # 黄金ETF
]
g.stop_loss_threshold = -0.08 # 单只 ETF 止损阈值:-8%
g.stop_profit_threshold = 0.25 # 单只 ETF 止盈阈值:+25%
# 定时运行:每月第一个交易日开盘进行动态再平衡调仓
run_monthly(rebalance_portfolio, monthday=1, time='09:30', reference_security='000300.XSHG')
# 每日盘中监控止盈止损
run_daily(check_stop_limits, time='every_bar')
def rebalance_portfolio(context):
"""每月初执行投资组合动态再平衡"""
log.info("====== 开始执行月度再平衡调仓 ======")
# 1. 卖出不在当前候选池中的持仓
current_positions = list(context.portfolio.positions.keys())
for stock in current_positions:
if stock not in g.etf_pool:
order_target(stock, 0)
log.info("非池内标的,清仓: %s" % stock)
# 2. 调用聚宽组合优化器:最大化夏普比率
# 约束条件:单只 ETF 权重在 0% 到 25% 之间
try:
optimized_weight = portfolio_optimizer(
date=context.previous_date,
securities=g.etf_pool,
target=MaxSharpeRatio(rf=0.02, count=250), # 假设无风险利率 2%
constraints=[WeightConstraint(low=0.0, high=1.0)],
bounds=[Bound(0.0, 0.25)],
default_port_weight_range=[0.0, 1.0],
ftol=1e-09,
return_none_if_fail=True
)
except Exception as e:
log.error("优化器运行异常: %s" % str(e))
optimized_weight = None
if optimized_weight is None:
log.warn("组合优化失败,采用等权重被动调仓")
# 备用方案:等权重分配
optimized_weight = pd.Series(1.0 / len(g.etf_pool), index=g.etf_pool)
# 3. 执行调仓交易
total_value = context.portfolio.total_value
# 先卖出超配或需要减仓的标的,释放资金
for stock in g.etf_pool:
target_weight = optimized_weight.get(stock, 0.0)
target_value = total_value * target_weight
current_value = context.portfolio.positions[stock].value
if current_value > target_value:
order_target_value(stock, target_value)
# 再买入增配的标的
for stock in g.etf_pool:
target_weight = optimized_weight.get(stock, 0.0)
target_value = total_value * target_weight
current_value = context.portfolio.positions[stock].value
if current_value < target_value:
order_target_value(stock, target_value)
log.info("调整标的 %s 权重至 %.2f%%" % (stock, target_weight * 100))
def check_stop_limits(context):
"""每日盘中实时监控个股止盈止损"""
current_data = get_current_data()
for stock in list(context.portfolio.positions.keys()):
position = context.portfolio.positions[stock]
if position.closeable_amount > 0:
# 计算当前收益率
current_price = current_data[stock].last_price
avg_cost = position.avg_cost
if avg_cost > 0:
return_rate = (current_price - avg_cost) / avg_cost
# 触发止损
if return_rate <= g.stop_loss_threshold:
order_target(stock, 0)
log.warn("【止损触发】标的 %s 跌幅达 %.2f%%,执行清仓!" % (stock, return_rate * 100))
# 触发止盈
elif return_rate >= g.stop_profit_threshold:
order_target(stock, 0)
log.info("【止盈触发】标的 %s 涨幅达 %.2f%%,锁定利润!" % (stock, return_rate * 100))
避免未来函数:
在调用 portfolio_optimizer 时,传入的日期参数为 context.previous_date(前一交易日),确保优化器计算协方差矩阵和历史收益率时仅使用历史数据,完全规避未来函数。
真实价格模式(use_real_price):
策略开启了 set_option('use_real_price', True)。在真实价格模式下,分红送股会自动调整持仓数量,且止盈止损监控中获取的 position.avg_cost 与 get_current_data() 的最新价均对应真实市场价格,保证了回测与模拟盘的一致性。
双重调仓逻辑的协同:
月度再平衡负责从宏观资产配置角度优化夏普比率;每日盘中监控则作为微观风控防线,一旦个股触发止损,立即清仓,不干扰下一次月度再平衡的重新计算。