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

如何在 joinquant 中使用组合优化器构建基于 MaxFactorValue 的因子最大化策略?

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

问题描述

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

Title: joinquant 组合优化:基于 MaxFactorValue 的因子最大化策略

Question: 在 joinquant 中如何使用组合优化器构建因子值最大化的投资组合?

解决方案

在 joinquant 中使用组合优化器构建因子值最大化策略

在聚宽(joinquant)平台中,通过投资组合优化器模块 jqlib.optimizer,管理者可以在满足多种约束条件(如年化收益率下限、仓位占比限制、单标的权重上限等)的前提下,实现特定目标的最大化。对于因子选股,MaxFactorValue 目标函数可以帮助我们在限定风险和约束的同时,尽可能赋予因子得分较高的标的更高的权重。


1. 核心核心概念与 API 介绍

1.1 组合优化器函数 portfolio_optimizer

portfolio_optimizer 是聚宽提供的核心优化函数,主要参数如下:

  • date: 优化执行的日期(一般传入 context.previous_date 避免未来函数)。
  • securities: 拟进行权重分配的股票/标的列表。
  • target: 优化目标,此处选用 MaxFactorValue(factor, count)
  • constraints: 约束条件列表,例如收益率约束 AnnualProfitConstraint 或市场约束等。
  • bounds: 单标的权重限制,如 Bound(0, 0.2) 表示单只股票权重不超过 20%。

1.2 目标函数 MaxFactorValue

  • factor: 自定义的 Factor 类或聚宽因子库中的因子。
  • count: 过去计算因子的天数窗口(默认为 1)。

2. 策略构建步骤

  1. 初始化环境与基准: 设置基准(如沪深300 000300.XSHG)、开启动态复权 set_option('use_real_price', True) 及交易手续费。
  2. 定义自定义因子: 继承 jqfactor.Factor 类。例如定义 5 日均值人气指标(AR)因子。
  3. 筛选股票池: 定期(如每月初)获取成分股或候选标的池。
  4. 求解最佳权重: 调用 portfolio_optimizer,传入 MaxFactorValue 目标以及所需的约束条件与边界。
  5. 按权重调仓: 根据优化器输出的股票权重 Series,使用 order_target_value 进行批量调整。

3. 完整代码示例

import pandas as pd
from jqdata import *
from jqfactor import Factor
from jqlib.optimizer import *

# 1. 定义因子:人气指标5日均值
class AR_M5(Factor):
    name = 'ar_m5'
    max_window = 5
    dependencies = ['AR']
    def calc(self, data):
        return data['AR'].mean()

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):
    # 选出上证50成分股作为候选股票池
    g.buy_list = get_index_stocks('000016.XSHG')

def market_open(context):
    # 卖出不在候选池中的股票
    sell_list = set(context.portfolio.positions.keys()) - set(g.buy_list)
    for stock in sell_list:
        order_target_value(stock, 0)
        
    # 调用投资组合优化器
    # 目标:'人气指标5日均值'最大化
    # 约束:组合年化收益率目标下限为 10%
    # 边界:单只标的权重介于 0 到 20% 之间
    optimized_weight = portfolio_optimizer(
        date=context.previous_date,
        securities=g.buy_list,
        target=MaxFactorValue(factor=AR_M5, count=1),
        constraints=[AnnualProfitConstraint(limit=0.1, count=250)],
        bounds=[Bound(0.0, 0.2)],
        default_port_weight_range=[0.0, 1.0],
        ftol=1e-09,
        return_none_if_fail=True
    )
    
    # 检查优化结果并调仓
    if optimized_weight is None:
        log.warn('警告:组合优化失败,保持当前持仓')
    else:
        log.info('组合优化成功,目标权重如下:')
        print(optimized_weight)
        
        total_value = context.portfolio.total_value
        for stock, weight in optimized_weight.items():
            target_value = total_value * weight
            order_target_value(stock, target_value)

4. 注意事项与优化建议

  1. 避免未来函数: 优化计算时传入的 date 参数应为 context.previous_date,切勿使用当天的价格或实时财务数据进行组合优化。
  2. 求解失败处理: 当约束条件过于苛刻(例如年化收益率下限设置过高,或单标的权重上限设置过低)时,优化器可能无法收敛并返回 None。设置 return_none_if_fail=True 可以有效防止策略异常中断,并可在代码中做好降级处理(如维持现状或退化为等权重配置)。
  3. 优化精度与速度: 调整 ftol 参数(如 1e-081e-09)可以平衡优化计算的精度和响应执行速度。