🚀 AI 一键生成 joinquant 策略代码
立即体验

joinquant 平台如何利用 portfolio_optimizer 实现最小化组合方差(MinVariance)?(附 Python 源码)

joinquant | 发布时间: 2026/7/22
以下内容由 EasyQuant 生成。

问题描述

请根据以下 SEO 主题生成完整页面内容:

Title: joinquant 组合优化:如何构建最小方差(MinVariance)投资模型?

Question: 请教在聚宽平台如何利用 portfolio_optimizer 实现最小化组合方差的权重优化?

解决方案

什么是最小方差(MinVariance)模型?

最小方差模型(Minimum Variance Portfolio)是现代资产配置理论(MPT)中的经典模型。其核心思想是在不考虑预期收益率的情况下,通过计算资产之间的协方差矩阵,寻找一组投资权重,使得整个投资组合的总体方差(即风险)达到最小。这对于追求稳健收益、规避剧烈波动的量化交易者非常适用。

在聚宽(JoinQuant)平台上,我们可以直接调用内置的 portfolio_optimizer 函数,配合 MinVariance 目标函数,轻松实现这一优化过程。


聚宽 portfolio_optimizer 核心 API 介绍

要构建最小方差模型,我们需要使用 jqlib.optimizer 模块中的以下组件:

  1. 优化器入口
    portfolio_optimizer(date, securities, target, constraints, bounds=[Bound(0.0, 1.0)], ...)
    
  2. 目标函数
    • MinVariance(count=250):组合风险最小化(最小化组合方差)。count 参数表示向前取历史收益率的天数,默认 250 天。
  3. 约束条件(可选)
    • WeightConstraint(low=0.0, high=1.0):限制组合总权重范围。
    • AnnualProfitConstraint(limit=0.1, count=250):限制组合年化收益率预期下限。
    • Bound(low, high):限制单只标的的权重上下限。

最小方差优化策略完整源码

以下是一个在聚宽平台运行的完整月频调仓策略。该策略选取上证50部分成分股与ETF构建股票池,并在每月初利用 portfolio_optimizer 计算最小方差权重,同时设定了总权重在 90%~100% 之间,且单只标的权重不超过 10% 的约束。

# 导入聚宽函数库与优化器库
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(before_market_open, monthday=1, time='09:00', reference_security='000300.XSHG')
    run_monthly(market_open, monthday=1, time='09:30', reference_security='000300.XSHG')

def before_market_open(context):
    log.info('今日调仓日期:%s' % context.current_dt.date())
    # 选取上证50成分股的后15只,加上部分ETF构建候选池
    etf = [
        '159902.XSHE',
        '159903.XSHE',
        '510050.XSHG'
    ]
    g.buy_list = list(get_index_stocks('000016.XSHG')[-15:]) + etf

def market_open(context):
    # 1. 卖出不在当前候选池中的持仓标的
    sell_list = set(context.portfolio.positions.keys()) - set(g.buy_list)
    for stock in sell_list:
        order_target_value(stock, 0)

    # 2. 调用投资组合优化器计算最小方差权重
    # 约束条件:组合总权重在 90% 到 100% 之间;单只标的权重限制在 0% 到 10% 之间
    optimized_weight = portfolio_optimizer(
        date=context.previous_date,
        securities=g.buy_list,
        target=MinVariance(count=250), # 最小化组合方差
        constraints=[WeightConstraint(low=0.9, high=1.0)], # 总权重约束
        bounds=[Bound(0.0, 0.1)], # 单只标的权重上限 10%
        default_port_weight_range=[0.0, 1.0],
        ftol=1e-09,
        return_none_if_fail=True
    )

    # 3. 执行调仓操作
    if optimized_weight is None:
        log.warn('警告:组合优化失败,保持原仓位或执行默认等权重')
    else:
        log.info("优化后的权重结果:")
        log.info(optimized_weight)
        
        total_value = context.portfolio.total_value # 获取当前总资产
        for stock in optimized_weight.keys():
            target_value = total_value * optimized_weight[stock] # 计算目标金额
            order_target_value(stock, target_value) # 调整至目标权重

编写与避坑指南

  1. 避免未来函数:在调用 portfolio_optimizer 时,date 参数必须传入 context.previous_date(前一交易日),切勿传入 context.current_dt,否则会引入未来数据导致回测失真。
  2. 优化失败处理:当约束条件过于苛刻(例如标的历史数据不足、或设置了冲突的收益率下限)时,优化器可能返回 None。代码中必须对 optimized_weight is None 进行判断,防止策略抛出异常中断。
  3. 开启真实价格模式:强烈建议在 initialize 中调用 set_option('use_real_price', True)。在真实价格模式下,分红送股会自动调整持仓,使回测结果更贴近实盘。