第4章:传统优化方法:线性规划与二次规划在投资组合中的应用;Python实现(SciPy)
说到投资组合优化,很多人第一反应就是马科维茨的均值-方差模型。没错,这确实是经典中的经典。但你知道吗?在实际工程中,我们往往需要更灵活的数学工具来处理各种约束条件。今天我就带你看看线性规划和二次规划这两个老朋友,在投资组合里能玩出什么花样。
4.1 为什么需要传统优化方法?
强化学习虽然强大,但它不是万能的。我在做量化策略的时候,经常遇到这样的情况:策略跑得好好的,突然某天回撤变大,一查发现是权重分配出了问题。这时候,传统优化方法反而更靠谱。
说白了,传统优化方法有几个不可替代的优势:
- 可解释性强:每一步都有明确的数学推导,出了问题容易排查
- 计算效率高:对于凸优化问题,求解速度非常快
- 约束处理灵活:可以轻松加入各种业务约束,比如不允许做空、行业集中度限制等
我个人习惯把传统优化方法当作强化学习的"安全网"。先用传统方法算出一个基准组合,再用强化学习去微调。这样既保证了稳定性,又保留了学习能力。
4.2 线性规划:最简单的投资组合优化
线性规划(LP)的目标函数和约束条件都是线性的。你想想看,在投资组合里,什么场景适合用线性规划?
举个例子:假设你有100万资金,要在5只股票里分配,要求每只股票不超过40%,总仓位必须满仓。这就是一个典型的线性规划问题。
线性规划的标准形式:
min c^T x
s.t. Ax ≤ b
x ≥ 0
其中x是权重向量,c是目标系数,A和b定义约束条件。
嗯,这里要注意一点:线性规划只能处理线性目标。如果你想最大化收益,那没问题。但如果你想最小化风险(方差),那就不是线性问题了。这时候就需要二次规划出马。
4.3 二次规划:均值-方差模型的数学本质
马科维茨的均值-方差模型,本质上就是一个二次规划问题。目标函数是二次的(方差),约束条件是线性的(权重和等于1,不允许做空等)。
我记得第一次用二次规划做投资组合时,踩过一个坑:协方差矩阵必须是半正定的,否则求解器会报错。后来我养成了一个习惯,每次计算协方差矩阵后,先检查一下特征值,确保没有负数。
避坑指南:我曾经因为数据缺失导致协方差矩阵不正定,结果优化出来的权重全是负数。后来我改用收缩估计(Shrinkage Estimation)来处理协方差矩阵,效果好了很多。
4.4 Python实现:用SciPy搞定优化问题
SciPy的optimize模块提供了线性规划和二次规划的求解器。我个人最常用的是linprog和minimize(配合SLSQP方法)。
先看一个线性规划的例子:
import numpy as np
from scipy.optimize import linprog
# 假设我们有3只股票,预期收益分别为[0.1, 0.12, 0.08]
# 目标:最大化收益(注意linprog默认求最小值,所以取负号)
c = -np.array([0.1, 0.12, 0.08])
# 约束条件:每只股票不超过40%
A_ub = np.array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
b_ub = np.array([0.4, 0.4, 0.4])
# 等式约束:权重和等于1
A_eq = np.array([[1, 1, 1]])
b_eq = np.array([1])
# 变量边界:不允许做空
bounds = [(0, None), (0, None), (0, None)]
# 求解
result = linprog(c, A_ub=A_ub, b_ub=b_ub,
A_eq=A_eq, b_eq=b_eq,
bounds=bounds, method='highs')
print("最优权重:", result.x)
print("最优收益:", -result.fun)
接下来是二次规划,实现均值-方差优化:
import numpy as np
from scipy.optimize import minimize
# 假设我们有4只股票的历史收益率数据
returns = np.random.randn(100, 4) * 0.02 # 模拟数据
cov_matrix = np.cov(returns.T)
expected_returns = np.mean(returns, axis=0)
# 目标函数:最小化方差(风险)
def portfolio_variance(weights):
return weights.T @ cov_matrix @ weights
# 约束条件
constraints = [
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1} # 权重和等于1
]
# 变量边界
bounds = [(0, 1) for _ in range(4)] # 不允许做空
# 初始猜测
x0 = np.array([0.25, 0.25, 0.25, 0.25])
# 求解
result = minimize(portfolio_variance, x0,
method='SLSQP',
bounds=bounds,
constraints=constraints)
print("最小风险组合权重:", result.x)
print("组合方差:", result.fun)
小技巧:在实际项目中,我经常把目标函数改成"最大化夏普比率"。做法很简单:目标函数变成 (weights @ expected_returns) / sqrt(weights.T @ cov_matrix @ weights),然后加一个约束让分母不为零。
4.5 知识体系总览
下面这张图展示了传统优化方法在投资组合中的应用框架。我画这张图的时候,特意把强化学习和传统方法的关系也标出来了,方便你理解它们各自的位置。
4.6 实际项目中的经验总结
做了这么多年量化,我总结了几条关于传统优化方法的经验:
| 场景 | 推荐方法 | 注意事项 |
|---|---|---|
| 只考虑收益最大化 | 线性规划 | 注意约束条件是否合理 |
| 均值-方差优化 | 二次规划 | 协方差矩阵必须正定 |
| 加入交易成本 | 二次规划 | 成本函数要可微 |
| 大规模资产(>1000) | 线性规划 | 二次规划计算量太大 |
最后说一句:传统优化方法不是万能的,但它是每个量化工程师必须掌握的基本功。我见过太多人一上来就搞深度学习,结果连最基本的约束条件都处理不好。先把这些基础打牢,再谈强化学习,你会走得更稳。
公众号:蓝海资料掘金营,微信deep3321