def solve(x0, risk_alphas, loadings, srisk, cost_per_trade=DEFAULT_COST, max_risk=0.01): N = len(x0) # don't hold no risk data (likely dead) lim = np.where(srisk.isnull(), 0.0, 1.0) loadings = loadings.fillna(0) srisk = srisk.fillna(0) risk_alphas = risk_alphas.fillna(0) with Model() as m: w = m.variable(N, Domain.inRange(-lim, lim)) longs = m.variable(N, Domain.greaterThan(0)) shorts = m.variable(N, Domain.greaterThan(0)) gross = m.variable(N, Domain.greaterThan(0)) m.constraint( "leverage_consistent", Expr.sub(gross, Expr.add(longs, shorts)), Domain.equalsTo(0), ) m.constraint("net_consistent", Expr.sub(w, Expr.sub(longs, shorts)), Domain.equalsTo(0.0)) m.constraint("leverage_long", Expr.sum(longs), Domain.lessThan(1.0)) m.constraint("leverage_short", Expr.sum(shorts), Domain.lessThan(1.0)) buys = m.variable(N, Domain.greaterThan(0)) sells = m.variable(N, Domain.greaterThan(0)) gross_trade = Expr.add(buys, sells) net_trade = Expr.sub(buys, sells) total_gross_trade = Expr.sum(gross_trade) m.constraint( "net_trade", Expr.sub(w, net_trade), Domain.equalsTo(np.asarray(x0)), # cannot handle series ) # add risk constraint vol = m.variable(1, Domain.lessThan(max_risk)) stacked = Expr.vstack(vol.asExpr(), Expr.mulElm(w, srisk.values)) stacked = Expr.vstack(stacked, Expr.mul(loadings.values.T, w)) m.constraint("vol-cons", stacked, Domain.inQCone()) alphas = risk_alphas.dot(np.vstack([loadings.T, np.diag(srisk)])) gain = Expr.dot(alphas, net_trade) loss = Expr.mul(cost_per_trade, total_gross_trade) m.objective(ObjectiveSense.Maximize, Expr.sub(gain, loss)) m.solve() result = pd.Series(w.level(), srisk.index) return result
def minimum_variance(matrix): # Given the matrix of returns a (each column is a series of returns) this method # computes the weights for a minimum variance portfolio, e.g. # min 2-Norm[a*w]^2 # s.t. # w >= 0 # sum[w] = 1 # This is the left-most point on the efficiency frontier in the classic Markowitz theory # build the model with Model("Minimum Variance") as model: # introduce the weight variable weights = model.variable("weights", matrix.shape[1], Domain.inRange(0.0, 1.0)) # sum of weights has to be 1 model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) # returns r = Expr.mul(Matrix.dense(matrix), weights) # compute l2_norm squared of those returns # minimize this l2_norm model.objective(ObjectiveSense.Minimize, __l2_norm_squared(model, "2-norm^2(r)", expr=r)) # solve the problem model.solve() # return the series of weights return np.array(weights.level())
def lsq_pos_l1_penalty(matrix, rhs, cost_multiplier, weights_0): """ min 2-norm (matrix*w - rhs)** + 1-norm(cost_multiplier*(w-w0)) s.t. e'w = 1 w >= 0 """ # define model model = mModel.build_model('lsqSparse') # introduce n non-negative weight variables weights = mModel.weights(model, "weights", n=matrix.shape[1], lb=0.0) # e'*w = 1 mBound.equal(model, Expr.sum(weights), 1.0) # sum of squared residuals v = mMath.l2_norm_squared(model, "2-norm(res)**", __residual(matrix, rhs, weights)) print matrix.shape[1] print weights_0 print weights # \Gamma*(w - w0), p is an expression p = mMath.mat_vec_prod(cost_multiplier, Expr.sub(weights, weights_0)) t = mMath.l1_norm(model, 'abs(weights)', p) # Minimise v + t mModel.minimise(model, __sum_weighted(1.0, v, 1.0, t)) return np.array(weights.level())
def lsq_pos_l1_penalty(matrix, rhs, cost_multiplier, weights_0): """ min 2-norm (matrix*w - rhs)** + 1-norm(cost_multiplier*(w-w0)) s.t. e'w = 1 w >= 0 """ # define model with Model('lsqSparse') as model: # introduce n non-negative weight variables weights = model.variable("weights", matrix.shape[1], Domain.inRange(0.0, +np.infty)) # e'*w = 1 model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) # sum of squared residuals v = __l2_norm_squared(model, "2-norm(res)**", __residual(matrix, rhs, weights)) # \Gamma*(w - w0), p is an expression p = Expr.mulElm(cost_multiplier, Expr.sub(weights, weights_0)) cost = model.variable("cost", matrix.shape[1], Domain.unbounded()) model.constraint(Expr.sub(cost, p), Domain.equalsTo(0.0)) t = __l1_norm(model, 'abs(weights)', cost) # Minimise v + t model.objective(ObjectiveSense.Minimize, __sum_weighted(1.0, v, 1.0, t)) # solve the problem model.solve() return np.array(weights.level())
def Markowitz(mu, covar, alpha, L=1.0): # defineerime kasutatava mudeli kasutades keskväärtusi model = mosek_model.build_model('meanVariance') # toome sisse n mitte-negatiivset kaalu weights = mosek_model.weights(model, "weights", n=covar.shape[1]) # leiame kaaludele vastavad finantsvõimendused lev = mosek_math.l1_norm(model, "leverage", weights) # nõuame, et oleks täidetud tingimus omega_1 + ... + omega_n = 1 mosek_bound.equal(model, Expr.sum(weights), 1.0) # nõuame, et oleks täidetud tingimus |omega_1| + ... + |omega_n| <= L mosek_bound.upper(model, lev, L) v = Expr.dot(mu.values, weights) # arvutame variatsiooni var = mosek_math.variance(model, "variance", weights, alpha*covar.values) # maksimeerime sihifunktsiooni mosek_model.maximise(model, Expr.sub(v, var)) # arvutame lõpuks kaalud ja tagastame need weights = pd.Series(data=np.array(weights.level()), index=stocks.keys()) return weights
def lsq_pos(matrix, rhs): """ min 2-norm (matrix*w - rhs)^2 s.t. e'w = 1 w >= 0 """ # define model with Model('lsqPos') as model: # introduce n non-negative weight variables weights = model.variable("weights", matrix.shape[1], Domain.inRange(0.0, +np.infty)) # e'*w = 1 model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) v = __l2_norm(model, "2-norm(res)", expr=__residual(matrix, rhs, weights)) # minimization of the residual model.objective(ObjectiveSense.Minimize, v) # solve the problem model.solve() return np.array(weights.level())
def __l1_norm(model, name, expr): """ Given an expression (e.g. a vector) this returns the L1-norm of this vector as an expression. It also introduces n (where n is the size of the expression) auxiliary variables. Mosek requires a name for any variable that is added to a model. The user has to specify this name explicitly. This requirement may disappear in future version of this API. ATTENTION: THIS WORKS ONLY IF expr is a VARIABLE """ return Expr.sum(__absolute(model, name, expr))
def solve_sdp_program(A): assert A.ndim == 2 assert A.shape[0] == A.shape[1] A = A.copy() n = A.shape[0] with Model('theta_1') as M: A = Matrix.dense(A) # variable X = M.variable('X', Domain.inPSDCone(n)) # objective function M.objective(ObjectiveSense.Maximize, Expr.sum(Expr.dot(Matrix.ones(n, n), X))) # constraints M.constraint(f'c1', Expr.sum(Expr.dot(X, A)), Domain.equalsTo(0.)) M.constraint(f'c2', Expr.sum(Expr.dot(X, Matrix.eye(n))), Domain.equalsTo(1.)) # solution M.solve() sol = X.level() return sum(sol)
def optimize_with_mosek(self, predicted, today): """ 使用Mosek来优化构建组合。在测试中Mosek比scipy的单纯形法快约18倍,如果可能请尽量使用Mosek。 但是Mosek是一个商业软件,因此你需要一份授权。如果没有授权的话请使用scipy或optlang。 """ from mosek.fusion import Expr, Model, ObjectiveSense, Domain, SolutionError index_weight = self.index_weights.loc[today].fillna(0) index_weight = index_weight / index_weight.sum() stocks = list(predicted.index) with Model("portfolio") as M: x = M.variable("x", len(stocks), Domain.inRange(0, self.constraint_config['stocks'])) # 权重总和等于一 M.constraint("sum", Expr.sum(x), Domain.equalsTo(1.0)) # 控制风格暴露 for factor_name, limit in self.constraint_config['factors'].items( ): factor_data = self.factor_data[factor_name].loc[today] factor_data = factor_data.fillna(factor_data.mean()) index_exposure = (index_weight * factor_data).sum() stocks_exposure = factor_data.loc[stocks].values M.constraint( factor_name, Expr.dot(stocks_exposure.tolist(), x), Domain.inRange(index_exposure - limit, index_exposure + limit)) # 控制行业暴露 for industry_name, limit in self.constraint_config[ 'industries'].items(): industry_data = self.industry_data[industry_name].loc[ today].fillna(0) index_exposure = (index_weight * industry_data).sum() stocks_exposure = industry_data.loc[stocks].values M.constraint( industry_name, Expr.dot(stocks_exposure.tolist(), x), Domain.inRange(index_exposure - limit, index_exposure + limit)) # 最大化期望收益率 M.objective("MaxRtn", ObjectiveSense.Maximize, Expr.dot(predicted.tolist(), x)) M.solve() weights = pd.Series(list(x.level()), index=stocks) # try: # weights = pd.Series(list(x.level()), index=stocks) # except SolutionError: # raise RuntimeError("Mosek fail to find a feasible solution @ {}".format(str(today))) return weights[weights > 0]
def markowitz(exp_ret, covariance_mat, aversion): # define model model = mModel.build_model("mean_var") # set of n weights (unconstrained) weights = mModel.weights(model, "weights", n=len(exp_ret)) mBound.equal(model, Expr.sum(weights), 1.0) # standard deviation induced by covariance matrix var = mMath.variance(model, "var", weights, covariance_mat) mModel.maximise(model=model, expr=Expr.sub(Expr.dot(exp_ret, weights), Expr.mul(aversion, var))) return np.array(weights.level())
def markowitz(exp_ret, covariance_mat, aversion): # define model with Model("mean var") as model: # set of n weights (unconstrained) weights = model.variable("weights", len(exp_ret), Domain.inRange(-np.infty, +np.infty)) model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) # standard deviation induced by covariance matrix var = __variance(model, "var", weights, covariance_mat) model.objective(ObjectiveSense.Maximize, Expr.sub(Expr.dot(exp_ret, weights), Expr.mul(aversion, var))) model.solve() #mModel.maximise(model=model, expr=Expr.sub(Expr.dot(exp_ret, weights), Expr.mul(aversion, var))) return np.array(weights.level())
def markowitz(exp_ret, covariance_mat, aversion): # define model with Model("mean var") as model: # set of n weights (unconstrained) weights = model.variable("weights", len(exp_ret), Domain.inRange(-np.infty, +np.infty)) model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) # standard deviation induced by covariance matrix var = __variance(model, "var", weights, covariance_mat) model.objective( ObjectiveSense.Maximize, Expr.sub(Expr.dot(exp_ret, weights), Expr.mul(aversion, var))) model.solve() #mModel.maximise(model=model, expr=Expr.sub(Expr.dot(exp_ret, weights), Expr.mul(aversion, var))) return np.array(weights.level())
def lsq_ls(matrix, rhs): """ min 2-norm (matrix*w - rhs)^2 s.t. e'w = 1 """ # define model model = mModel.build_model('lsqPos') # introduce n non-negative weight variables weights = mModel.weights(model, "weights", n=matrix.shape[1]) # e'*w = 1 mBound.equal(model, Expr.sum(weights), 1.0) v = mMath.l2_norm(model, "2-norm(res)", expr=__residual(matrix, rhs, weights)) # minimization of the residual mModel.minimise(model=model, expr=v) return np.array(weights.level())
def lsq_ls(matrix, rhs): """ min 2-norm (matrix*w - rhs)^2 s.t. e'w = 1 """ # define model with Model('lsqPos') as model: weights = model.variable("weights", matrix.shape[1], Domain.inRange(-np.infty, +np.infty)) # e'*w = 1 model.constraint(Expr.sum(weights), Domain.equalsTo(1.0)) v = __l2_norm(model, "2-norm(res)", expr=__residual(matrix, rhs, weights)) # minimization of the residual model.objective(ObjectiveSense.Minimize, v) # solve the problem model.solve() return np.array(weights.level())
def __init__(self,name, foods, nutrients, daily_allowance, nutritive_value): Model.__init__(self,name) finished = False try: self.foods = [ str(f) for f in foods ] self.nutrients = [ str(n) for n in nutrients ] self.dailyAllowance = array.array(daily_allowance, float) self.nutrientValue = DenseMatrix(nutritive_value).transpose() M = len(self.foods) N = len(self.nutrients) if len(self.dailyAllowance) != N: raise ValueError("Length of daily_allowance does not match " "the number of nutrients") if self.nutrientValue.numColumns() != M: raise ValueError("Number of rows in nutrient_value does not " "match the number of foods") if self.nutrientValue.numRows() != N: raise ValueError("Number of columns in nutrient_value does " "not match the number of nutrients") self.__dailyPurchase = self.variable('Daily Purchase', StringSet(self.foods), Domain.greaterThan(0.0)) self.__dailyNutrients = \ self.constraint('Nutrient Balance', StringSet(nutrients), Expr.mul(self.nutrientValue,self.__dailyPurchase), Domain.greaterThan(self.dailyAllowance)) self.objective(ObjectiveSense.Minimize, Expr.sum(self.__dailyPurchase)) finished = True finally: if not finished: self.__del__()
def __init__(self,name, foods, nutrients, daily_allowance, nutritive_value): Model.__init__(self,name) finished = False try: self.foods = [ str(f) for f in foods ] self.nutrients = [ str(n) for n in nutrients ] self.dailyAllowance = numpy.array(daily_allowance, float) self.nutrientValue = Matrix.dense(nutritive_value).transpose() M = len(self.foods) N = len(self.nutrients) if len(self.dailyAllowance) != N: raise ValueError("Length of daily_allowance does not match " "the number of nutrients") if self.nutrientValue.numColumns() != M: raise ValueError("Number of rows in nutrient_value does not " "match the number of foods") if self.nutrientValue.numRows() != N: raise ValueError("Number of columns in nutrient_value does " "not match the number of nutrients") self.__dailyPurchase = self.variable('Daily Purchase', StringSet(self.foods), Domain.greaterThan(0.0)) self.__dailyNutrients = \ self.constraint('Nutrient Balance', StringSet(nutrients), Expr.mul(self.nutrientValue,self.__dailyPurchase), Domain.greaterThan(self.dailyAllowance)) self.objective(ObjectiveSense.Minimize, Expr.sum(self.__dailyPurchase)) finished = True finally: if not finished: self.__del__()