Scipy.optimize minimize函数行为异常求助排查
minimize in Portfolio Optimization Problem Breakdown
You’ve hit a tricky issue with your portfolio optimization code: Scipy’s optimize.minimize starts off okay but later returns invalid results, and you noticed the weights and returns parameters are getting swapped in the portfolio_stats function. Let’s walk through why this happens and how to fix it.
Root Cause
The problem boils down to a parameter order mismatch in your min_func_sharpe function. Here’s the breakdown:
- Your
portfolio_statsfunction expects parameters in the order:(returns, weights, rf) - But in the original code, you called it as
portfolio_stats(weights, args[0], args[1])— this swapsweightsandreturns(sinceargs[0]is your returns data).
This swap doesn’t crash the code immediately (since both are arrays), but it completely invalidates the portfolio statistics calculations as the optimization iterates, leading to the abnormal results you saw.
The Fix
Correct the parameter order in min_func_sharpe to match what portfolio_stats expects:
def min_func_sharpe(weights,*args): return -portfolio_stats(args[0], weights, args[1])[2]
Now args[0] (your returns data) is passed first, followed by weights, then args[1] (the risk-free rate), aligning with the portfolio_stats signature.
Full Corrected Code
from math import exp import numpy as np import pandas_datareader.data as web import matplotlib.pyplot as plt import scipy.optimize as optimization # 从Yahoo获取数据 def stock_data(stocks,start_date,end_date): start_date= start_date end_date= end_date data=web.DataReader(stocks,data_source='yahoo',start=start_date,end=end_date)['Adj Close'] return data # 计算日对数收益率(区别于算术收益率) def stock_returns(data): daily_returns=(data/data.shift(1)) daily_returns=np.log(daily_returns) return daily_returns # 获取股票统计数据(基于对数收益率的年度近似值) def stock_stats(returns): expected=returns.mean()*252 variance=returns.var()*252 covariance=returns.cov()*252 return [expected,variance,covariance] # 获取投资组合统计数据(基于对数收益率的年度近似值) def portfolio_stats(returns,weights,rf): expected=np.sum(returns.mean()*weights)*252 variance =np.dot(weights.T,np.dot(returns.cov()*252,weights)) sd=np.sqrt(variance) sharpe=(expected-rf)/sd return [expected,sd,sharpe] # 运行投资组合蒙特卡洛模拟 def monte_carlo_porfolios(stocks,returns,simulations,rf): mc_expected=[] mc_sd=[] optimum=[] sharpe=0 for i in range(simulations): weights=np.random.random(len(stocks)) weights /= np.sum(weights) stats =portfolio_stats(returns,weights,rf) expected =stats[0] sd =stats[1] mc_expected.append(expected) mc_sd.append(sd) if (expected-rf)/sd>sharpe: optimum=weights sharpe=(expected-rf)/sd mc_expected=np.array(mc_expected) mc_sd=np.array(mc_sd) return [mc_expected,mc_sd,np.array(optimum)] # 定义待最小化函数(已修正参数顺序) def min_func_sharpe(weights,*args): return -portfolio_stats(args[0], weights, args[1])[2] # 通过优化算法寻找最优组合 def optimize_portfolio(initial,returns,rf): constraints = ({'type':'eq','fun':lambda x:np.sum(x)-1}) bounds = tuple((0,1) for x in range(len(stocks))) optimum = optimization.minimize(fun=min_func_sharpe,x0=initial,args=(returns,rf),method='SLSQP',bounds=bounds,constraints=constraints) return optimum stocks = ['AAPL','WMT','TSLA','GE','AMZN','DB'] start_date ='01/01/2010' end_date ='01/01/2020' rf=0.02 data = stock_data(stocks,start_date,end_date) log_returns = stock_returns(data) monte_carlo = monte_carlo_porfolios(stocks,log_returns,1000,rf) print(optimize_portfolio(monte_carlo[2],log_returns,rf))
Quick Tip
When working with optimization functions (where inputs are passed indirectly via *args), always cross-verify parameter orders between the objective function and the helper functions it calls. Subtle mismatches like this are easy to miss but can completely derail your calculations.
内容的提问来源于stack exchange,提问作者Charmalade

