3、权重边界约束:个股/资产权重上下限、行业权重限制、多空头限制

权重边界约束,说白了就是给投资组合里的每个资产「画个圈」。

你不能让某只股票占比太大,也不能让某个行业过于集中。更不能在明明只能做多的账户里偷偷做空。这些约束,是量化投资中最基础也最容易被忽视的「安全护栏」。

我个人习惯把权重约束分成三类来理解:

  • 个股/资产权重上下限:单只股票最多买多少,最少留多少
  • 行业权重限制:某个行业不能超过总仓位的X%
  • 多空头限制:做多和做空的资金比例怎么分配

咱们一个一个来看。

3.1 个股权重上下限:别把鸡蛋放一个篮子里

这个约束最直观。你想想看,如果某只股票权重到了30%,它一天跌停,整个组合就亏3%。这谁受得了?

所以我们会设置:

  • 上限:比如单只股票不超过总资产的5%或10%
  • 下限:比如单只股票不低于0%(即不允许做空),或者不低于-2%(允许少量做空)

核心公式

对于每只资产 i,其权重 w_i 必须满足:

w_min ≤ w_i ≤ w_max

其中 w_min 和 w_max 是预设的上下限。

我在项目中遇到过这样一个坑:有个策略跑得挺好,年化收益20%+,但回撤特别大。一查原因,发现某只小盘股权重到了15%。那段时间它连续跌停,组合直接崩了。从那以后,我所有策略都强制加上了单只股票5%的上限。

3.2 行业权重限制:别让行业风险绑架你

个股约束只能管住单只股票,但管不住行业集中度。

举个例子:你买了10只银行股,每只只占2%,看起来分散了。但整个银行板块占了20%的仓位。一旦银行出利空,你照样亏得厉害。

行业权重限制就是解决这个问题的:

  • 设定每个行业的最大权重,比如不超过20%
  • 也可以设定最小权重,比如不低于5%(如果你看好某个行业)

我的经验:行业分类最好用申万一级行业,或者你自己定义的行业标签。别用太细的分类,否则约束太多,优化器容易跑不出解。

我曾经做过一个回测,不加行业约束时夏普比率1.5,加了行业约束后降到1.2。但实盘表现反而更好。为什么?因为行业约束避免了「过拟合」——你回测里表现好的行业,未来不一定好。

3.3 多空头限制:做多和做空不是一回事

很多初学者以为「权重可以是负的,那就是做空」。但实际交易中,做多和做空的资金管理完全不同。

多空头限制通常包括:

  • 总多头仓位:所有做多资产权重之和,比如不超过100%
  • 总空头仓位:所有做空资产权重之和(取绝对值),比如不超过50%
  • 净头寸:多头仓位 - 空头仓位,比如要求净头寸为0(市场中性)

注意:做空不是免费的。融券有成本,而且可能被强制平仓。所以空头仓位一定要留足安全边际。

我记得有一次帮朋友调试策略,他设了「净头寸为0」的约束,但没设空头上限。结果优化器给出了「做多200%,做空200%」的解——虽然净头寸是0,但杠杆高得吓人。这种策略一遇到波动就爆仓。

3.4 代码实现:用Python搞定权重约束

下面我用一个简单的例子,展示如何在优化中加入这些约束。

import numpy as np
from scipy.optimize import minimize

# 假设我们有5只股票
n_assets = 5
expected_returns = np.array([0.12, 0.08, 0.15, 0.10, 0.09])
cov_matrix = np.random.randn(n_assets, n_assets)
cov_matrix = cov_matrix @ cov_matrix.T  # 确保正定

# 定义目标函数:最大化夏普比率
def neg_sharpe(weights):
    port_return = np.dot(weights, expected_returns)
    port_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
    return -port_return / port_vol  # 负号是因为我们要最小化

# 约束条件
constraints = [
    # 1. 个股权重上下限:每只股票在0%到10%之间
    {'type': 'ineq', 'fun': lambda w: w - 0.00},   # w >= 0%
    {'type': 'ineq', 'fun': lambda w: 0.10 - w},   # w <= 10%
    
    # 2. 行业权重限制:假设前两只股票属于"金融"行业
    # 金融行业总权重不超过20%
    {'type': 'ineq', 'fun': lambda w: 0.20 - (w[0] + w[1])},
    
    # 3. 多空头限制:净头寸为0(市场中性)
    {'type': 'eq', 'fun': lambda w: np.sum(w)},  # 权重之和为0
    
    # 4. 总多头仓位不超过100%,总空头仓位不超过50%
    {'type': 'ineq', 'fun': lambda w: 1.0 - np.sum(w[w > 0])},   # 多头 <= 100%
    {'type': 'ineq', 'fun': lambda w: 0.5 - np.sum(np.abs(w[w < 0]))},  # 空头 <= 50%
]

# 初始权重(随机)
init_weights = np.random.randn(n_assets)
init_weights = init_weights / np.sum(np.abs(init_weights))  # 归一化

# 优化求解
result = minimize(neg_sharpe, init_weights, constraints=constraints, 
                  method='SLSQP', options={'maxiter': 1000})

print("最优权重:", result.x)
print("负夏普比率:", result.fun)

代码说明

  • 我用的是SLSQP算法,它支持等式和不等式约束
  • 个股上下限用了两个不等式约束:w >= 0% 和 w <= 10%
  • 行业约束通过组合多个资产的权重来实现
  • 多空头约束用等式约束保证净头寸为0

3.5 避坑指南:我踩过的几个坑

做权重约束优化,有几个地方特别容易出问题:

  1. 约束太紧导致无解:比如你既要求每只股票不超过5%,又要求行业集中度不低于30%,还要求净头寸为0。这种组合很可能无解。我的建议是:先放松约束,看看最优解在哪里,再逐步收紧。
  2. 忽略交易成本:权重约束会让调仓更频繁。每次调仓都有手续费和冲击成本。我一般会在优化目标里加上一个「换手率惩罚项」。
  3. 行业分类过时:有些股票业务变了,但行业标签没更新。比如某公司原来是房地产,现在转型做新能源。如果你还用旧标签,行业约束就形同虚设。

一个小技巧:在实盘前,先用历史数据做一次「约束压力测试」。看看在极端行情下(比如2020年3月),你的约束条件是否还能被满足。如果不行,说明约束太理想化了。

3.6 知识体系总览

下面这张图,把权重边界约束的核心逻辑串起来了:

权重边界约束知识体系 个股权重上下限 行业权重限制 多空头限制 关键参数 • 单只股票上限:5%~10% • 单只股票下限:0%(不做空) • 或 -2%(允许少量做空) • 防止个股黑天鹅 关键参数 • 行业最大权重:20%~30% • 行业最小权重:5%(可选) • 行业分类:申万一级 • 防止行业集中风险 关键参数 • 总多头仓位 ≤ 100% • 总空头仓位 ≤ 50% • 净头寸 = 0(市场中性) • 控制杠杆和方向 核心目标 控制风险集中度 · 避免过拟合 · 提升实盘稳健性 三者缺一不可,共同构成投资组合的「安全护栏」

这张图把三类约束并列展示,它们之间是互补关系。个股约束管微观,行业约束管中观,多空头约束管宏观。三者配合,才能构建一个真正稳健的组合。

最后提醒一句:约束条件不是越多越好。每加一个约束,优化器的可行域就缩小一圈。有时候,少即是多。


公众号:蓝海资料掘金营,微信deep3321