对不起,这个主题有点融资,但也有一个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电子表格中正确地做同样的事情。
非常感谢!
答案 0 :(得分:0)
Basinhopping是一个随机的全局优化器。无法保证它会在指定的迭代次数内找到全局最优值。
从您的描述中可以看出,您有办法检查解决方案是否是全局最优解。在这种情况下,您可以使用callback参数来优化搜索
回调:可调用,回调(x,f,接受),可选
将为所有找到的最小值调用回调函数。 x和f是试验最小值的坐标和函数值,并且接受是否接受该最小值。例如,这可以用于保存找到的最低N个最小值。此外,回调可用于指定用户定义的停止标准,可选择返回True以停止流水作业例程。
def my_callback(x, f, accept):
return minimum_is_global_minimum(x)
然后你可以将niter设置为一个大数字,一旦找到最小值,它就会停止。