坚持使用线性优化函数来优化投资组合权重
Stuck using the linear optimisation function to optimise portfolio weights
上下文
我目前正在寻求建立一个优化函数来构建投资组合权重。它类似于 excel 求解器或 google 工作表求解器函数(尽管有缺陷)。尽管它的工作方式与 excel VBA 不同。这是我第一次玩它。以下是脚本:
function PortfolioOptimisation() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
var assets = ['AssetOne','AssetTwo','AssetThree','AssetFour','AssetFive',
'AssetSix','AssetSeven','AssetEight']; //What I using to optimise variables
var weights = ss.getRangeByName(assets);
// The variables to optimise
var factors = ['OptimisationExpectedReturn','OptimisationExpectedVol','OptimisationNegativeReturn',
'OptimisationPositiveReturns','OptimisationPositiveRisk','OptimisationNegativeRisk',
'OptimisationSortinoRatio','OptimisationSharpeRatio']; //Store it in a variable as I do not want to keep typing up the named ranges.
var sumWeights = ss.getRangeByName('OptimisationWeightsSum')
var optimalPortfolios = ss.getRangeByName(factors);
// Call the optimiser engine
var engine = LinearOptimizationService.createEngine();
engine.addVariable(optimalPortfolios[0]);// Add first variable,
// Add constraints: weights =1, Sum of weights =1, weights = greater than 0, less than or equal to 1.
var constraint = engine.addConstraints([0.0],[1.0],[weights,sumWeights],[weights]);
这就是我试图将其应用于:
Spreadsheet
它包含将使用优化函数计算的每个单元格中的公式。
问题
如何执行优化功能以根据电子表格中的 'portfolio section/column' 找到最佳值?我怎样才能改进上面的代码?
例如,在电子表格的第二个 tab/sheet 中,在第一个投资组合名称上,我想通过最大化和最小化 Sortino 比率来优化资产的权重。那么使用优化引擎,可以帮助我实现这一目标的最佳资产权重是多少?我想对投资组合列中列出的其他投资组合做同样的事情。
如 the documentation 中所述,要找到最佳值,您应该 运行 engine.solve()
。这将 return 值,因此您需要将它们存储在一个变量中,然后在任何您想要的地方使用它们。
...
var constraint = engine.addConstraints([0.0],[1.0],[weights,sumWeights],[weights]);
// Get the result of the optimization engine
var solution = engine.solve()
另外请记住,solve() has a default deadline of 30 seconds. If you want to modify the default deadline time simply pass the amount of seconds you want as a paramater like this engine.solve(300)
. Also, check these methods 可以应用于您的解决方案,例如,确定它是否可行或最佳。
一个python解决方案
def ticker_list():
tckr_list = ['AVV.L', 'SCT.L', 'ROR.L', 'OCDO.L', 'CCC.L', '3IN.L', 'AVST.L', 'ASC.L', 'SPX.L','ECM.L', 'TRN.L', 'PLTR']
return tckr_list
def Optimize_MaxR_Vc():
# after getting a list of your asset returns...
# Number of assets in the portfolio
tckr_list = ticker_list() # this should be for the number of assets you have. if saved as a
Assets = tckr_list
num_assets = len(Assets)
# Lists of variables for Portfolio creation
Portfolio_returns = []
Portfolio_Volatilities = []
Portfolio_GrossR = []
Aveva_Returns_weight = []
Softcat_Returns_weight = []
Rotork_Returns_weight = []
Ocado_Returns_weight = []
Computacenter_Returns_weight = []
TInfrastructure_Returns_weight = []
Avast_Returns_weight = []
ASOS_Returns_weight = []
Spirax_Returns_weight = []
Electrocomponents_Returns_weight = []
Trainline_Returns_weight = []
Palantir_Returns_weight = []
#Optimising for expected returns and standard deviation
Gross_rtn = Gross_return()
for x in range (100000):
weights = np.random.random(num_assets)
weights /= np.sum(weights)
Portfolio_returns.append(np.sum(weights * Portfolio_rtns.mean() * 250)) # expected returns
Portfolio_Volatilities.append(np.sqrt(np.dot(weights.T,np.dot(Portfolio_rtns.cov() * 250, weights)))) # standard deviation
Portfolio_GrossR.append(np.sum(weights * Gross_rtn.mean() * 250)) # Gross returns
Aveva_Returns_weight.append(weights[0])
Softcat_Returns_weight.append(weights[1])
Rotork_Returns_weight.append(weights[2])
Ocado_Returns_weight .append(weights[3])
Computacenter_Returns_weight.append(weights[4])
TInfrastructure_Returns_weight.append(weights[5])
Avast_Returns_weight.append(weights[6])
ASOS_Returns_weight.append(weights[7])
Spirax_Returns_weight.append(weights[8])
Electrocomponents_Returns_weight.append(weights[9])
Trainline_Returns_weight.append(weights[10])
Palantir_Returns_weight.append(weights[11])
# Create an array of data for portfolio
Portfolio_returns = np.array(Portfolio_returns)
Portfolio_Volatilities = np.array(Portfolio_Volatilities)
Portfolio_GrossR = np.array(Portfolio_GrossR)
Aveva_Returns_Weight = np.array(Aveva_Returns_weight)
Softcat_Returns_Weight = np.array(Softcat_Returns_weight)
Rotork_Returns_Weight = np.array(Rotork_Returns_weight)
Ocado_Returns_Weight = np.array(Ocado_Returns_weight)
Computacenter_Returns_Weight = np.array(Computacenter_Returns_weight)
TInfrastructure_Returns_Weight = np.array(TInfrastructure_Returns_weight)
Avast_Returns_Weight = np.array(Avast_Returns_weight)
ASOS_Returns_Weight = np.array(ASOS_Returns_weight)
Spirax_Returns_Weight = np.array(Spirax_Returns_weight)
Electrocomponents_Returns_Weight = np.array(Electrocomponents_Returns_weight)
Trainline_Returns_Weight = np.array(Trainline_Returns_weight)
Palantir_Returns_Weight = np.array(Palantir_Returns_weight)
#Creating a table
Portfolios = pd.DataFrame({'Return': Portfolio_returns,
'Volatility': Portfolio_Volatilities,
'Gross Return': Portfolio_GrossR,
'Aveva Weight': Aveva_Returns_weight,
'Softcat Weight': Softcat_Returns_weight,
'Rotork Weight': Rotork_Returns_weight,
'Ocado Weight': Ocado_Returns_weight,
'Computacenter Weight': Computacenter_Returns_weight,
'3Infrastructure Weight': TInfrastructure_Returns_weight,
'Avast Weight': Avast_Returns_weight,
'ASOS Weight': ASOS_Returns_weight,
'Spirax Weight': Spirax_Returns_weight,
'Electrocomponents': Electrocomponents_Returns_weight,
'Trainline': Trainline_Returns_weight,
'Palantir': Palantir_Returns_weight})
# Custom Portfolios
# With this range, what different types of portfolios can we build?
# if volatitlity is within this range, where is volatility when you search for max return?
Min_return = Portfolios[(Portfolios['Volatility']>=.135) & (Portfolios['Volatility']<=14.358)].min()['Return']
Return = Portfolios.iloc[np.where(Portfolios['Return']==Min_return)]
Min_return_1 = Portfolios[(Portfolios['Volatility']>=.200) & (Portfolios['Volatility']<=9.00)].min()['Return']
Return_2 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_1)]
Min_return_2 = Portfolios[(Portfolios['Volatility']>=.300) & (Portfolios['Volatility']<=8.00)].min()['Return']
Return_3 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_2)]
Min_return_3 = Portfolios[(Portfolios['Volatility']>=.400) & (Portfolios['Volatility']<=7.00)].min()['Return']
Return_4 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_3)]
Min_return_4 = Portfolios[(Portfolios['Volatility']>=.500) & (Portfolios['Volatility']<=6.00)].min()['Return']
Return_5 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_4)]
Min_return_5 = Portfolios[(Portfolios['Volatility']>=.600) & (Portfolios['Volatility']<=5.00)].min()['Return']
Return_6 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_5)]
Min_return_6 = Portfolios[(Portfolios['Volatility']>=.700) & (Portfolios['Volatility']<=4.00)].min()['Return']
Return_7 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_6)]
Min_return_7 = Portfolios[(Portfolios['Volatility']>=.800) & (Portfolios['Volatility']<=3.00)].min()['Return']
Return_8= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_7)]
Min_return_8 = Portfolios[(Portfolios['Volatility']>=.900) & (Portfolios['Volatility']<=2.00)].min()['Return']
Return_8= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_8)]
Min_return_9 = Portfolios[(Portfolios['Volatility']>=.100) & (Portfolios['Volatility']<=1.00)].min()['Return']
Return_9= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_9)]
Final_MaxOp = pd.concat([Return,Return_2, Return_3, Return_4, Return_5, Return_6,
Return_7, Return_8, Return_9])
return Final_MaxOp
我将它保存为 python 实验室中的一个模块,因此要 运行 它,我需要做的就是:
Portfolio = P.Optimize_MaxR_Vc() # load the results
Portfolio # show the results
P 是我保存它的模块,所以我将它导入为
from Portfolio import P
在提出范围之前,运行:
# What is the max returns?
max(Portfolio_returns)
#What is the min volatility?
min(Portfolio_Volatilities)
您可以将这段代码的各个部分分成不同的函数,运行它们可以测试不同的范围。
更新
更简单的解决方案:
# Portfolio returns calculated
def portfolio_returns(weights, returns):
"""weights -> returns"""
# take the weights, transpose it and take the matrix multiplication
return weights.T @ returns
# Volatility
def portfolio_volatility(weights, covmat):
"""Weights -> Covariance"""
# Weights transposes, matrix multiply with covmatrix and matrix multiply this with weights and square root the answer
return (weights.T @ covmat @ weights)**0.5
# minimum vol for a certain return
from scipy.optimize import minimize
import numpy as np
def minimize_vol (target_return, er, Cov):
# number of assets
n = er.shape[0]
# guess weights to achieve goal
initial_guess = np.repeat(1/n, n)
# make copies of this boundary for every asset
boundary = ((0.0, 1.0),)*n
# Return should be whatever the target is
return_is_target = {
'type': 'eq',
'args': (er,),
'fun': lambda weights, er: target_return - portfolio_returns(weights, er)
}
# weights should equal one
weights_sum_1 = {
'type':'eq',
'fun': lambda weights: np.sum(weights) - 1
}
# Optimiser
results = minimize(portfolio_volatility, initial_guess,
args=(cov,), method='SLSQP',
options={'disp': False},
constraints=(return_is_target, weights_sum_1),
bounds=boundary)
return results.x
# Target weights
def optimal_weights(n_points, er, cov):
""" Get a list of weights for min and max returns"""
# generate the target return give the min and max returns
target_rtns = np.linspace(er.min(), er.max(), n_points)
# for target rtns, loop through the function for what this would be and give me a set of weights
weights = [minimize_vol(target_return, er, cov) for target_return in target_rtns]
return weights
# multi asset portfolio for mimimum volatility portfolio
def plot_Portfolio(n_points, er, cov):
"""
plot Efficient portfolio for n assets
"""
weights = optimal_weights(n_points, er, cov)
Returns = [portfolio_returns(w,er) for w in weights]
Covariance = [portfolio_volatility(w,cov) for w in weights]
Portfolio_final = pd.DataFrame({"Returns":Returns, "Volatility": Covariance})
return Portfolio_final.plot.line(x="Volatility", y="Returns");
--> 源自 Edhec 课程
上下文
我目前正在寻求建立一个优化函数来构建投资组合权重。它类似于 excel 求解器或 google 工作表求解器函数(尽管有缺陷)。尽管它的工作方式与 excel VBA 不同。这是我第一次玩它。以下是脚本:
function PortfolioOptimisation() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
var assets = ['AssetOne','AssetTwo','AssetThree','AssetFour','AssetFive',
'AssetSix','AssetSeven','AssetEight']; //What I using to optimise variables
var weights = ss.getRangeByName(assets);
// The variables to optimise
var factors = ['OptimisationExpectedReturn','OptimisationExpectedVol','OptimisationNegativeReturn',
'OptimisationPositiveReturns','OptimisationPositiveRisk','OptimisationNegativeRisk',
'OptimisationSortinoRatio','OptimisationSharpeRatio']; //Store it in a variable as I do not want to keep typing up the named ranges.
var sumWeights = ss.getRangeByName('OptimisationWeightsSum')
var optimalPortfolios = ss.getRangeByName(factors);
// Call the optimiser engine
var engine = LinearOptimizationService.createEngine();
engine.addVariable(optimalPortfolios[0]);// Add first variable,
// Add constraints: weights =1, Sum of weights =1, weights = greater than 0, less than or equal to 1.
var constraint = engine.addConstraints([0.0],[1.0],[weights,sumWeights],[weights]);
这就是我试图将其应用于: Spreadsheet
它包含将使用优化函数计算的每个单元格中的公式。
问题
如何执行优化功能以根据电子表格中的 'portfolio section/column' 找到最佳值?我怎样才能改进上面的代码?
例如,在电子表格的第二个 tab/sheet 中,在第一个投资组合名称上,我想通过最大化和最小化 Sortino 比率来优化资产的权重。那么使用优化引擎,可以帮助我实现这一目标的最佳资产权重是多少?我想对投资组合列中列出的其他投资组合做同样的事情。
如 the documentation 中所述,要找到最佳值,您应该 运行 engine.solve()
。这将 return 值,因此您需要将它们存储在一个变量中,然后在任何您想要的地方使用它们。
...
var constraint = engine.addConstraints([0.0],[1.0],[weights,sumWeights],[weights]);
// Get the result of the optimization engine
var solution = engine.solve()
另外请记住,solve() has a default deadline of 30 seconds. If you want to modify the default deadline time simply pass the amount of seconds you want as a paramater like this engine.solve(300)
. Also, check these methods 可以应用于您的解决方案,例如,确定它是否可行或最佳。
一个python解决方案
def ticker_list():
tckr_list = ['AVV.L', 'SCT.L', 'ROR.L', 'OCDO.L', 'CCC.L', '3IN.L', 'AVST.L', 'ASC.L', 'SPX.L','ECM.L', 'TRN.L', 'PLTR']
return tckr_list
def Optimize_MaxR_Vc():
# after getting a list of your asset returns...
# Number of assets in the portfolio
tckr_list = ticker_list() # this should be for the number of assets you have. if saved as a
Assets = tckr_list
num_assets = len(Assets)
# Lists of variables for Portfolio creation
Portfolio_returns = []
Portfolio_Volatilities = []
Portfolio_GrossR = []
Aveva_Returns_weight = []
Softcat_Returns_weight = []
Rotork_Returns_weight = []
Ocado_Returns_weight = []
Computacenter_Returns_weight = []
TInfrastructure_Returns_weight = []
Avast_Returns_weight = []
ASOS_Returns_weight = []
Spirax_Returns_weight = []
Electrocomponents_Returns_weight = []
Trainline_Returns_weight = []
Palantir_Returns_weight = []
#Optimising for expected returns and standard deviation
Gross_rtn = Gross_return()
for x in range (100000):
weights = np.random.random(num_assets)
weights /= np.sum(weights)
Portfolio_returns.append(np.sum(weights * Portfolio_rtns.mean() * 250)) # expected returns
Portfolio_Volatilities.append(np.sqrt(np.dot(weights.T,np.dot(Portfolio_rtns.cov() * 250, weights)))) # standard deviation
Portfolio_GrossR.append(np.sum(weights * Gross_rtn.mean() * 250)) # Gross returns
Aveva_Returns_weight.append(weights[0])
Softcat_Returns_weight.append(weights[1])
Rotork_Returns_weight.append(weights[2])
Ocado_Returns_weight .append(weights[3])
Computacenter_Returns_weight.append(weights[4])
TInfrastructure_Returns_weight.append(weights[5])
Avast_Returns_weight.append(weights[6])
ASOS_Returns_weight.append(weights[7])
Spirax_Returns_weight.append(weights[8])
Electrocomponents_Returns_weight.append(weights[9])
Trainline_Returns_weight.append(weights[10])
Palantir_Returns_weight.append(weights[11])
# Create an array of data for portfolio
Portfolio_returns = np.array(Portfolio_returns)
Portfolio_Volatilities = np.array(Portfolio_Volatilities)
Portfolio_GrossR = np.array(Portfolio_GrossR)
Aveva_Returns_Weight = np.array(Aveva_Returns_weight)
Softcat_Returns_Weight = np.array(Softcat_Returns_weight)
Rotork_Returns_Weight = np.array(Rotork_Returns_weight)
Ocado_Returns_Weight = np.array(Ocado_Returns_weight)
Computacenter_Returns_Weight = np.array(Computacenter_Returns_weight)
TInfrastructure_Returns_Weight = np.array(TInfrastructure_Returns_weight)
Avast_Returns_Weight = np.array(Avast_Returns_weight)
ASOS_Returns_Weight = np.array(ASOS_Returns_weight)
Spirax_Returns_Weight = np.array(Spirax_Returns_weight)
Electrocomponents_Returns_Weight = np.array(Electrocomponents_Returns_weight)
Trainline_Returns_Weight = np.array(Trainline_Returns_weight)
Palantir_Returns_Weight = np.array(Palantir_Returns_weight)
#Creating a table
Portfolios = pd.DataFrame({'Return': Portfolio_returns,
'Volatility': Portfolio_Volatilities,
'Gross Return': Portfolio_GrossR,
'Aveva Weight': Aveva_Returns_weight,
'Softcat Weight': Softcat_Returns_weight,
'Rotork Weight': Rotork_Returns_weight,
'Ocado Weight': Ocado_Returns_weight,
'Computacenter Weight': Computacenter_Returns_weight,
'3Infrastructure Weight': TInfrastructure_Returns_weight,
'Avast Weight': Avast_Returns_weight,
'ASOS Weight': ASOS_Returns_weight,
'Spirax Weight': Spirax_Returns_weight,
'Electrocomponents': Electrocomponents_Returns_weight,
'Trainline': Trainline_Returns_weight,
'Palantir': Palantir_Returns_weight})
# Custom Portfolios
# With this range, what different types of portfolios can we build?
# if volatitlity is within this range, where is volatility when you search for max return?
Min_return = Portfolios[(Portfolios['Volatility']>=.135) & (Portfolios['Volatility']<=14.358)].min()['Return']
Return = Portfolios.iloc[np.where(Portfolios['Return']==Min_return)]
Min_return_1 = Portfolios[(Portfolios['Volatility']>=.200) & (Portfolios['Volatility']<=9.00)].min()['Return']
Return_2 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_1)]
Min_return_2 = Portfolios[(Portfolios['Volatility']>=.300) & (Portfolios['Volatility']<=8.00)].min()['Return']
Return_3 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_2)]
Min_return_3 = Portfolios[(Portfolios['Volatility']>=.400) & (Portfolios['Volatility']<=7.00)].min()['Return']
Return_4 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_3)]
Min_return_4 = Portfolios[(Portfolios['Volatility']>=.500) & (Portfolios['Volatility']<=6.00)].min()['Return']
Return_5 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_4)]
Min_return_5 = Portfolios[(Portfolios['Volatility']>=.600) & (Portfolios['Volatility']<=5.00)].min()['Return']
Return_6 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_5)]
Min_return_6 = Portfolios[(Portfolios['Volatility']>=.700) & (Portfolios['Volatility']<=4.00)].min()['Return']
Return_7 = Portfolios.iloc[np.where(Portfolios['Return']==Min_return_6)]
Min_return_7 = Portfolios[(Portfolios['Volatility']>=.800) & (Portfolios['Volatility']<=3.00)].min()['Return']
Return_8= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_7)]
Min_return_8 = Portfolios[(Portfolios['Volatility']>=.900) & (Portfolios['Volatility']<=2.00)].min()['Return']
Return_8= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_8)]
Min_return_9 = Portfolios[(Portfolios['Volatility']>=.100) & (Portfolios['Volatility']<=1.00)].min()['Return']
Return_9= Portfolios.iloc[np.where(Portfolios['Return']==Min_return_9)]
Final_MaxOp = pd.concat([Return,Return_2, Return_3, Return_4, Return_5, Return_6,
Return_7, Return_8, Return_9])
return Final_MaxOp
我将它保存为 python 实验室中的一个模块,因此要 运行 它,我需要做的就是:
Portfolio = P.Optimize_MaxR_Vc() # load the results
Portfolio # show the results
P 是我保存它的模块,所以我将它导入为
from Portfolio import P
在提出范围之前,运行:
# What is the max returns?
max(Portfolio_returns)
#What is the min volatility?
min(Portfolio_Volatilities)
您可以将这段代码的各个部分分成不同的函数,运行它们可以测试不同的范围。
更新
更简单的解决方案:
# Portfolio returns calculated
def portfolio_returns(weights, returns):
"""weights -> returns"""
# take the weights, transpose it and take the matrix multiplication
return weights.T @ returns
# Volatility
def portfolio_volatility(weights, covmat):
"""Weights -> Covariance"""
# Weights transposes, matrix multiply with covmatrix and matrix multiply this with weights and square root the answer
return (weights.T @ covmat @ weights)**0.5
# minimum vol for a certain return
from scipy.optimize import minimize
import numpy as np
def minimize_vol (target_return, er, Cov):
# number of assets
n = er.shape[0]
# guess weights to achieve goal
initial_guess = np.repeat(1/n, n)
# make copies of this boundary for every asset
boundary = ((0.0, 1.0),)*n
# Return should be whatever the target is
return_is_target = {
'type': 'eq',
'args': (er,),
'fun': lambda weights, er: target_return - portfolio_returns(weights, er)
}
# weights should equal one
weights_sum_1 = {
'type':'eq',
'fun': lambda weights: np.sum(weights) - 1
}
# Optimiser
results = minimize(portfolio_volatility, initial_guess,
args=(cov,), method='SLSQP',
options={'disp': False},
constraints=(return_is_target, weights_sum_1),
bounds=boundary)
return results.x
# Target weights
def optimal_weights(n_points, er, cov):
""" Get a list of weights for min and max returns"""
# generate the target return give the min and max returns
target_rtns = np.linspace(er.min(), er.max(), n_points)
# for target rtns, loop through the function for what this would be and give me a set of weights
weights = [minimize_vol(target_return, er, cov) for target_return in target_rtns]
return weights
# multi asset portfolio for mimimum volatility portfolio
def plot_Portfolio(n_points, er, cov):
"""
plot Efficient portfolio for n assets
"""
weights = optimal_weights(n_points, er, cov)
Returns = [portfolio_returns(w,er) for w in weights]
Covariance = [portfolio_volatility(w,cov) for w in weights]
Portfolio_final = pd.DataFrame({"Returns":Returns, "Volatility": Covariance})
return Portfolio_final.plot.line(x="Volatility", y="Returns");
--> 源自 Edhec 课程