使用cvxopt根据有效前沿给定收益率Y值求解对应风险标准差X值
实现方案
核心逻辑
原有代码是通过遍历风险厌恶系数mu求解有效前沿的离散点,再通过多项式拟合反推对应收益率的风险,精度受拟合误差、离散点密度影响。我们可以直接调整二次规划的约束条件,单次求解即可得到指定目标收益率下的最小风险:
- 优化目标改为最小化组合方差:
min 0.5 * w^T * cov_matrix * w - 保留原有约束:权重和为1、单资产权重≥0
- 新增约束:组合加权收益率 = 指定目标收益率
实现代码
import numpy as np import cvxopt.opt as opt import cvxopt.solvers as solvers import cvxopt.blas as blas import matplotlib.pyplot as plt # 原有有效前沿计算函数保持不变 def optimal_portfolio(returns_vec, cov_matrix): n = len(returns_vec) N = 1000 mus = [10 ** (5 * t / N - 1.0) for t in range(N)] S = opt.matrix(cov_matrix) pbar = opt.matrix(returns_vec) G = -opt.matrix(np.eye(n)) h = opt.matrix(0.0, (n, 1)) A = opt.matrix(1.0, (1, n)) b = opt.matrix(1.0) portfolios = [solvers.qp(mu * S, -pbar, G, h, A, b)['x'] for mu in mus] returns = [blas.dot(pbar, x) for x in portfolios] risks = [np.sqrt(blas.dot(x, S * x)) for x in portfolios] m1 = np.polyfit(returns, risks, 2) x1 = np.sqrt(m1[2] / m1[0]) wt = solvers.qp(opt.matrix(x1 * S), -pbar, G, h, A, b)['x'] return np.asarray(wt), returns, risks # 新增指定收益率求风险函数 def get_risk_for_target_return(target_return, returns_vec, cov_matrix): n = len(returns_vec) # 校验目标收益率是否在可行区间内 min_possible_return = min(returns_vec) max_possible_return = max(returns_vec) if target_return < min_possible_return or target_return > max_possible_return: raise ValueError(f"目标收益率需在[{min_possible_return:.6f}, {max_possible_return:.6f}]区间内") S = opt.matrix(cov_matrix) # 二次规划目标:min 0.5 * w^T S w,对应P=S, q=0向量 P = S q = opt.matrix(0.0, (n, 1)) # 非负约束:w >=0 → -w <=0 G = -opt.matrix(np.eye(n)) h = opt.matrix(0.0, (n, 1)) # 双等式约束:1. 权重和为1;2. 组合收益率等于目标值 A = opt.matrix(np.vstack([np.ones(n), returns_vec])) b = opt.matrix([1.0, target_return]) # 求解二次规划 sol = solvers.qp(P, q, G, h, A, b) optimal_weights = np.asarray(sol['x']).flatten() # 计算对应风险(标准差) risk = np.sqrt(np.dot(optimal_weights.T, np.dot(cov_matrix, optimal_weights))) return risk, optimal_weights # 测试用例(原有参数) return_vec = [0.055355, 0.010748, 0.041505, 0.074884, 0.039795, 0.065079] cov_matrix =[[ 0.005329, -0.000572, 0.003320, 0.006792, 0.001580, 0.005316], [-0.000572, 0.000625, 0.000606, -0.000266, -0.000107, 0.000531], [0.003320, 0.000606, 0.006610, 0.005421, 0.000990, 0.006852], [0.006792, -0.000266, 0.005421, 0.011385, 0.002617, 0.009786], [0.001580, -0.000107, 0.000990, 0.002617, 0.002226, 0.002360], [0.005316, 0.000531, 0.006852, 0.009786, 0.002360, 0.011215]] # 原有逻辑计算有效前沿 weights, returns, risks = optimal_portfolio(return_vec, cov_matrix) # 测试指定收益率查询 target_return = 0.05 risk, w = get_risk_for_target_return(target_return, return_vec, cov_matrix) print(f"目标收益率{target_return:.4f}对应的最小风险为{risk:.4f}") print(f"对应最优权重为{w.round(4)}") # 可视化验证:把计算的点打到原有有效前沿上 fig = plt.figure() plt.ylabel('Return') plt.xlabel('Risk') plt.plot(risks, returns, 'y-o', label='有效前沿') plt.scatter(risk, target_return, c='red', s=100, label='指定目标收益率点') plt.legend() plt.show()
注意事项
- 求解前会自动校验目标收益率的可行区间,避免传入超出最大/最小单个资产收益率的无效值导致求解失败
- 如果需要允许做空,只需删除非负约束(把G和h参数设置为
None传入solvers.qp即可)
内容的提问来源于stack exchange,提问作者Itachi Kurosaki
相关产品推荐
相关产品推荐

