scipy最小化并不总是解决功能

时间:2018-04-27 02:15:49

标签: python scipy finance

对不起,这个主题有点融资,但也有一个scipy / python问题。对于我想要做的事情的背景,它与这两篇博客文章完全一样 https://quantdare.com/risk-parity-in-python/
https://thequantmba.wordpress.com/2016/12/14/risk-parityrisk-budgeting-portfolio-in-python/

所以我有一堆股票回报,我想平衡每只股票的风险贡献。要做到这一点,我需要解决权重,这将使每个使用scipy最小化优化器给我一个相同的风险贡献。

所以我会传递我的目标风险贡献,以及我对优化器的初步猜测。例如,6只股票。我最初的猜测仅仅是投资组合中100%总重量的1/6。

initial_weight = [0.16666666666667, 0.16666666666667, 0.16666666666667,
              0.16666666666667, 0.16666666666667, 0.16666666666667]
risk_contrib_target =[0.16666666666667, 0.16666666666667, 0.16666666666667,
              0.16666666666667, 0.16666666666667, 0.16666666666667]

这是从quantmba链接中获取的,因此所有人都归功于那个人。它看起来对我来说。

 # risk budgeting optimization
def calculate_portfolio_var(w,V):
    # function that calculates portfolio risk
    w = np.matrix(w)
    return (w*V*w.T)[0,0]

def calculate_risk_contribution(w,V):
    # function that calculates asset contribution to total risk
    w = np.matrix(w, dtype=object)
    sigma = np.sqrt(calculate_portfolio_var(w,V))
    # Marginal Risk Contribution
    MRC = V*w.T
    # Risk Contribution
    RC = np.multiply(MRC,w.T)/sigma
    RC = RC / sum(RC)
    return RC

def risk_budget_objective(x,pars):
    # calculate portfolio risk
    V = pars[0]# covariance table
    x_t = pars[1] # risk target in percent of portfolio risk
    sig_p =  np.sqrt(calculate_portfolio_var(x,V)) # portfolio sigma
    risk_target = np.asmatrix(x_t, dtype=object)
    asset_RC = calculate_risk_contribution(x,V)
    J = sum(np.square(asset_RC-risk_target.T))[0,0] * 1000 # sum of squared error
    return J

我还有一个日期列表,我在一段时间内多次解决这个问题。

rebalance_dates = my_list_of_dates

我注意到有时候,它没有正确解决这个问题。这很容易检查,因为它的设置方式,该函数应该有一个0解决方案。此外,我可以检查风险贡献,看看他们达到了我的目标。为了解决这个问题,如果没有找到这个解决方案,我会把它踢到盆地跳跃。我认为这是解决局部最小值而不是全局最小值,我读到这是解决该问题的一种方法。

get_returns_matrix函数只是从我的一个文件中获取我想要的数据。这部分并不重要。

returns_matrix = get_returns_matrix(asset_returns, 60, date, components)

这是优化。

for date in rebalance_dates:
    print(date)
    returns_matrix = get_returns_matrix(asset_returns, 60, date, components)
    covariance = np.cov(returns_matrix)
    annual_covar = [map(lambda x:x * 260, group) for group in covariance]
    annual_covar = [list(x) for x in annual_covar]
    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1.0},
               {'type': 'ineq', 'fun': lambda x: x})
    res= minimize(risk_budget_objective, initial_weight, args=[annual_covar, risk_contrib_target], method='SLSQP',constraints=cons, 
              options={'disp': False, 'ftol': .00000000001, 'eps' : .0000000000000005, 'maxiter':1000})
    if res.fun > .00000000001:
        print("Kick to basin hopping")
        minimizer_kwargs = dict(method="SLSQP", constraints=cons, args=[annual_covar, risk_contrib_target], options={'ftol': .000000000000000000001, 'eps' : .0000000000000005, 'maxiter':100})
        res = basinhopping(risk_budget_objective, initial_weight, niter=50, minimizer_kwargs=minimizer_kwargs)

我有两个约束,一个是权重之和需要等于100%,另一个是权重应该是正数。

这大约有75%的时间正确解决,有时它会陷入局部最小值我相信。因此,正确的结果如下:

|--------|-----------|-----------|-----------|-----------|-----------|-----------|
|Category|Stock 1    |Stock 2    |Stock 3    |Stock 4    |Stock 5    |Stock 6    |
|--------|-----------|-----------|-----------|-----------|-----------|-----------|
|Weights |0.121465654|0.17829418 |0.091558469|0.105659033|0.156959021|0.346063642|
|--------|-----------|-----------|-----------|-----------|-----------|-----------|  
|Risk Con|0.166666667|0.166666667|0.166666667|0.166666667|0.166666667|0.166666667|

Function return val 0.0000000000                    

但偶尔(25%的情况下)我会得到一个不能解决问题的结果,如下所示:

|--------|-----------|-----------|-----------|-----------|-----------|-----------|
|Category|Stock 1    |Stock 2    |Stock 3    |Stock 4    |Stock 5    |Stock 6    |
|--------|-----------|-----------|-----------|-----------|-----------|-----------|
|Weights |0.159442825|0.166949713|0.235404372|0.175430619|0.262772472|0.000000000|
|--------|-----------|-----------|-----------|-----------|-----------|-----------|  
|Risk Con|0.199661774|0.199803048|0.200448716|0.199943667|0.200142796|0.000000000|

Function return val 33.33371143             

错误的时候,它似乎完全无视股票6.给予它0权重和0风险贡献。

我在解算器中没有正确使用任何参数吗?对不起,如果没有我正在使用的数据,这可能有点难以解决。但只是想知道我的方法是否有任何明显的错误。

我也碰巧知道scipy没有正确解决的解决方案,因为我可以在带有GRG非线性约束求解器的excel电子表格中正确地做同样的事情。

非常感谢!

1 个答案:

答案 0 :(得分:0)

Basinhopping是一个随机的全局优化器。无法保证它会在指定的迭代次数内找到全局最优值。

从您的描述中可以看出,您有办法检查解决方案是否是全局最优解。在这种情况下,您可以使用callback参数来优化搜索

  

回调:可调用,回调(x,f,接受),可选

     

将为所有找到的最小值调用回调函数。 x和f是试验最小值的坐标和函数值,并且接受是否接受该最小值。例如,这可以用于保存找到的最低N个最小值。此外,回调可用于指定用户定义的停止标准,可选择返回True以停止流水作业例程。

def my_callback(x, f, accept):
    return minimum_is_global_minimum(x)

然后你可以将niter设置为一个大数字,一旦找到最小值,它就会停止。