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

如何在 joinquant 中使用 MaxScore 优化器根据自定义打分配置标的权重?

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

问题描述

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

Title: joinquant 投资组合优化:MaxScore 打分最大化模型配置指南

Question: 如何在 joinquant 中使用 MaxScore 优化器根据自定义打分配置标的权重?

解决方案

如何在 JoinQuant 中使用 MaxScore 优化器根据自定义打分配置标的权重

在量化投资中,当投资者通过因子分析或多因子模型对股票池中的标的进行了打分(Score),并希望在满足特定风控约束(如组合年化波动率、行业暴露限制或单标的权重上限)的前提下,实现组合整体打分最大化时,可以使用 JoinQuant 聚宽平台提供的 portfolio_optimizer 优化器中的 MaxScore 目标函数。


1. MaxScore 优化模型简介

MaxScore(scores) 模型的核心逻辑是:在满足所有约束条件(Constraints)和单标的边界(Bounds)的前提下,赋予打分高(预期表现更好)的标的更高的权重

例如:若有 A、B、C 三只股票,打分分别为 3、2、1。如果全仓买入 A 能获得最高总打分,但可能会突破年化波动率不超过 15% 的风控限制。优化器会自动计算并配置一定比例的 B 和 C,使组合在严格满足波动率约束的条件下获得最高打分。


2. API 参数说明与关键组件

要运行 MaxScore 优化器,需要导入 jqlib.optimizer 模块并调用 portfolio_optimizer 函数。

核心参数

  • date: 优化发生的日期(如 context.previous_date)。
  • securities: 参与优化的股票或标的代码列表。
  • target: 设定为 MaxScore(scores),其中 scores 是以标的代码为索引(index)、打分值为数值的 pandas.Series
  • constraints: 限制条件列表,如 AnnualStdConstraint(收益率标准差限制)、WeightConstraint(总权重限制)等。
  • bounds: 边界条件列表,如 Bound(low, high) 限制单只股票的持仓比例。

3. 完整代码示例

以下示例展示了如何在聚宽回测/模拟环境中,根据过去 5 日的平均收盘价变化率作为自定义打分,结合 MaxScore 优化器进行定期调仓:

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

def initialize(context):
    # 设定基准与真实价格模式
    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(rebalance, monthday=1, time='09:30', reference_security='000300.XSHG')

def rebalance(context):
    # 1. 确定股票池(例如:沪深300前20只成分股)
    securities = get_index_stocks('000300.XSHG')[:20]
    
    # 2. 计算自定义打分(示例:获取过去5天收盘价并计算简单打分)
    hist = attribute_history(securities, 5, '1d', fields=['close'])
    scores = (hist['close'].iloc[-1] / hist['close'].iloc[0] - 1.0)
    
    # 确保 scores 为 pandas.Series
    scores = pd.Series(scores, index=securities)
    
    # 3. 设置优化目标与约束条件
    target = MaxScore(scores=scores)
    
    constraints = [
        WeightConstraint(low=0.9, high=1.0),                 # 组合总仓位限制在 90% ~ 100%
        AnnualStdConstraint(limit=0.25, count=250)           # 组合年化波动率控制在 25% 以内
    ]
    
    bounds = [
        Bound(low=0.0, high=0.15)                            # 单只股票权重不超过 15%
    ]
    
    # 4. 执行组合优化
    optimized_weights = portfolio_optimizer(
        date=context.previous_date,
        securities=securities,
        target=target,
        constraints=constraints,
        bounds=bounds,
        default_port_weight_range=[0.0, 1.0],
        ftol=1e-09,
        return_none_if_fail=True
    )
    
    # 5. 根据优化结果调仓
    if optimized_weights is not None:
        print("优化后的持仓权重:")
        print(optimized_weights)
        
        # 卖出不在优化结果中的股票
        for stock in context.portfolio.positions.keys():
            if stock not in optimized_weights or optimized_weights[stock] == 0:
                order_target_value(stock, 0)
                
        # 按优化权重调整目标价值
        total_value = context.portfolio.total_value
        for stock, weight in optimized_weights.items():
            target_value = total_value * weight
            order_target_value(stock, target_value)
    else:
        print("警告:组合优化失败,保持当前仓位不变。")

4. 注意事项与使用技巧

  1. 打分方向约定MaxScore 默认给数值越大的标的分配更高权重。如果你的打分逻辑是“数值越小越好”(如 PE 估值打分),请使用 MinScore 模型或对打分取负数处理。
  2. 处理优化失败:当约束条件过严(如年化标准差限制过低、单股权重上限过低且标的数量不足)时,优化器可能返回 None。建议设置 return_none_if_fail=True 并编写兜底降级逻辑(如等权重配置或维持原仓位)。
  3. 避免未来函数:传入 portfolio_optimizerdate 参数建议设为 context.previous_date,计算打分时使用历史完整交易日的数据。