I am trying to perform a portfolio optimization that returns the weights which maximize my utility function. I can do this portion just fine including the constraint that we
Not totally sure I understand, but I think you can add the following as another constraint:
def w_opt(W):
def filterer(x):
v = x.range.values
tp = v[0]
lower, upper = tp
return lower <= x[column_name].sum() <= upper
return not W.groupby(level=0, axis=0).filter(filterer).empty
c_ = {'type': 'eq', 'fun': w_opt} # add this to your other constraints
where x.range is the interval (tuple) repeated K[i] times where K is the number of times a particular level occurs and i is the ith level. column_name in your case happens to be a date.
This says constrain the weights such that the sum of the weights in the ith group is between the associated tuple interval.
To map each of the level names to an interval do this:
intervals = [(.08,.51), (.05,.21), (.05,.41), (.05,.41), (.2,.66), (0,.16), (0,.76), (0,.11)]
names = ['equity', 'intl_equity', 'bond', 'intl_bond', 'commodity', 'pe', 'hf', 'cash']
mapper = Series(zip(names, intervals))
fully_mapped = mapper[init_weights.get_level_values(0)]
original_dataset['range'] = fully_mapped.values
After much time this seems to be the only solution that fits...
def solve_weights(Rt, b_ = None):
W = np.ones(len(Rt.columns)) / len(Rt.columns)
if b_ is None:
b_ = [(0.01, 1.0) for i in Rt.columns]
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1})
else:
covar = Rt.cov()
c_ = ({'type':'eq', 'fun': lambda W: sum(W) - 1},
{'type':'eq', 'fun': lambda W: sqrt(np.dot(W, np.dot(covar, W)) * 252) - risk_tgt})
optimized = opt.minimize(fitness, W, args = [Rt], method='SLSQP', constraints=c_, bounds=b_)
if not optimized.success:
raise ValueError(optimized.message)
return optimized.x # Return optimized weights
class_cont = Rt.ix[0].copy()
class_cont.ix[:] = np.around(np.hstack(Rt.groupby(axis=1, level=0).apply(solve_weights).values),3)
scalars = class_cont.groupby(level=0).sum()
scalars.ix[:] = np.around(solve_weights((class_cont * port_rets).groupby(level=0, axis=1).sum(), list(model['tactical'].values)),3)
class_cont.groupby(level=0).transform(lambda x: x * scalars[x.name])