3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在量化投资中,投资组合的收益往往受大盘市值、贝塔、动量等风格因子的强烈影响。为了避免策略承受过度的因子风险敞口,实现相对于基准指数(如沪深 300)的风险中性,我们可以使用 JoinQuant 提供的投资组合优化器 portfolio_optimizer 配合 BarraConstraint 进行因子风控。
BarraConstraint 是 JoinQuant 组合优化器中的风险暴露限制函数,用于约束组合在 Barra 风格因子上的偏离度。
size: 市值因子beta: 贝塔因子momentum: 动量因子residual_volatility: 残差波动因子no_linear_size: 非线性市值因子book_to_price: 账面市值比因子liquidity: 流动性因子earning_yield: 盈利预期因子growth: 成长因子leverage: 杠杆因子[low, high],例如 [-0.5, 0.5] 表示限制在 -0.5 到 0.5 个标准差之间。默认 None 代表无限制。standardlize: 是否对因子值进行标准化处理,默认为 True。winsorize: 是否对因子值进行中位数去极值处理,默认为 True。在构建组合优化器时,将 BarraConstraint 传入 constraints 列表中即可:
from jqlib.optimizer import portfolio_optimizer, MinVariance, BarraConstraint, Bound
# 设定 Barra 因子风控约束:限制市值因子在 [-0.2, 0.2],Beta 因子在 [0.8, 1.2]
barra_rule = BarraConstraint(
size=[-0.2, 0.2],
beta=[0.8, 1.2],
winsorize=True,
standardlize=True
)
# 调用优化器计算最优权重
optimized_weight = portfolio_optimizer(
date=context.previous_date,
securities=g.buy_list,
target=MinVariance(count=250), # 目标:组合方差最小化
constraints=[barra_rule],
bounds=[Bound(0.0, 0.1)], # 单只股票权重上限 10%
default_port_weight_range=[0.0, 1.0]
)
以下是一个按月调仓的策略,选出上证 50 成分股,并通过 portfolio_optimizer 与 BarraConstraint 控制市值和贝塔风险敞口:
import pandas as pd
from jqdata import *
from jqlib.optimizer 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')
# 每月首个交易日开盘进行调仓
run_monthly(market_open, monthday=1, time='09:30', reference_security='000300.XSHG')
def market_open(context):
# 1. 确定股票池(上证50成分股)
stocks = get_index_stocks('000016.XSHG')
# 2. 设置 Barra 风险约束条件
# 限制市值因子敞口在 [-0.3, 0.3],Beta 敞口在 [0.8, 1.2]
barra_constraint = BarraConstraint(
size=[-0.3, 0.3],
beta=[0.8, 1.2],
winsorize=True,
standardlize=True
)
# 3. 运行组合优化器(最小化方差模型)
optimized_weight = portfolio_optimizer(
date=context.previous_date,
securities=stocks,
target=MinVariance(count=250),
constraints=[barra_constraint, WeightEqualConstraint(limit=1.0)], # 组合总权重限制为1.0
bounds=[Bound(0.0, 0.1)], # 个股上限10%
return_none_if_fail=True
)
if optimized_weight is None:
log.warn("组合优化失败,保持原仓位")
return
# 4. 卖出不在优化结果中的持仓
for stock in list(context.portfolio.positions.keys()):
if stock not in optimized_weight or optimized_weight[stock] == 0:
order_target_value(stock, 0)
# 5. 根据优化权重调整仓位
total_value = context.portfolio.total_value
for stock, weight in optimized_weight.items():
if weight > 0:
order_target_value(stock, total_value * weight)
count=250(覆盖一年交易日)。return_none_if_fail 设置为 True 并加入异常处理逻辑。