【问题标题】:Stuck using the linear optimisation function to optimise portfolio weights坚持使用线性优化函数优化投资组合权重
【发布时间】:2021-02-24 12:48:24
【问题描述】:

上下文

我目前正在寻求构建一个优化函数来构建投资组合权重。它类似于 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

它包含将使用优化函数计算的每个单元格中的公式。

问题

如何执行优化功能以根据电子表格中的“投资组合部分/列”找到最佳值?我该如何改进上面的代码?

例如,在电子表格的第二个选项卡/工作表中,在第一个投资组合名称上,我想通过最大化 Sortino 比率并将其最小化来优化资产的权重。那么使用优化引擎,可以帮助我实现这一目标的资产的最佳权重是多少?我想对投资组合列中列出的其他投资组合做同样的事情。

【问题讨论】:

  • 很遗憾,虽然我看到了您共享的电子表格,但我无法理解您当前的问题和目标。我为此道歉。为了正确理解它们,请问您期望的示例输入和输出?
  • 感谢您的回复。没问题,是我的错。我以为。我已经用示例更新了谷歌表。基本上,我想通过优化最右边的值(E[r],E[v]...)来计算权重。左侧的投资组合名称突出显示将优化哪个变量(例如,每种资产的权重组合将达到表格右侧的最高 Sortino 比率)。插入权重会自动更新其他数字。如果你愿意,我可以打开电子表格让你玩一下,感受一下?
  • 感谢您的回复。我不得不为我糟糕的英语水平道歉。不幸的是,从您的回复和更新的示例电子表格中,我仍然无法理解您的目标。但是,我想试着理解它。当我能正确理解它时,我会想办法。
  • 没问题,谢谢!我已经更新了问题,但随着我也了解更多信息,我会继续更新:)
  • 您正在使用 LP 求解器,但风险通常使用二次项建模。你需要一个 QP 求解器。

标签: javascript optimization google-sheets solver quantitative-finance


【解决方案1】:

the documentation 中所述,要找到您应该运行engine.solve() 的最佳值。这将返回值,因此您需要将它们存储在一个变量中,然后在您想要的任何地方使用它们。

...
var constraint = engine.addConstraints([0.0],[1.0],[weights,sumWeights],[weights]);

// Get the result of the optimization engine
var solution = engine.solve()

另外请注意solve() 的默认截止日期为 30 秒。如果您想修改默认截止时间,只需将您想要的秒数作为参数传递,就像 engine.solve(300) 一样。此外,检查可应用于您的解决方案的these methods,例如,确定它是可行的还是最优的。

【讨论】:

  • 谢谢你,但我想我的代码可能有错误。我刚刚尝试运行它,但engine.addvariable 出现错误。问题是我不太清楚如何组织它以获取电子表格的一部分并计算它。无论如何谢谢:)
  • 那么你希望你的变量是什么? addVariable() 的最简单形式将采用名称和上限和下限。你想要多少个变量,它们的名字是什么,它们的上限和下限是什么?谢谢!
  • 不用担心。不确定如何在 Google 表格上执行此操作。找到了一种在 Python 中执行此操作的方法。不过还是谢谢:)
  • 嗨!出于其他用户的文档目的,您能否分享一下您是如何在 Python 中实现这一点的?谢谢!
  • 这是一个相当长的代码,但我会在本周晚些时候这样做,谢谢 :)
【解决方案2】:

一个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)

您可以将此代码的各个部分分成不同的函数并运行它们以测试不同的范围。

【讨论】:

    【解决方案3】:

    更新

    更简单的解决方案:

    # 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 课程

    【讨论】:

      猜你喜欢
      • 2022-01-18
      • 2016-08-18
      • 1970-01-01
      • 2018-03-06
      • 2020-10-15
      • 2016-07-07
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      相关资源
      最近更新 更多