def test_value_error_for_no_min_volatility_optimal_weights(self):
        # pylint: disable=invalid-name
        """
        Test ValueError when no optimal weights are found for minimum volatility solution.
        """

        with self.assertRaises(ValueError):
            mvo = MeanVarianceOptimisation()
            mvo.allocate(asset_prices=self.data,
                         solution='min_volatility',
                         weight_bounds=(0.9, 1),
                         asset_names=self.data.columns)
    def test_max_sharpe_solution(self):
        """
        Test the calculation of maximum sharpe portfolio weights.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     solution='max_sharpe',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_resampling_asset_prices(self):
        """
        Test resampling of asset prices.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     solution='inverse_variance',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_max_return_min_volatility_solution(self):
        """
        Test the calculation of maximum expected return and minimum volatility portfolio weights.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     risk_aversion=50,
                     solution='max_return_min_volatility',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_valuerror_with_no_asset_names(self):
        """
        Test ValueError when not supplying a list of asset names and no other input
        """

        with self.assertRaises(ValueError):
            mvo = MeanVarianceOptimisation()
            expected_returns = ReturnsEstimators(
            ).calculate_mean_historical_returns(asset_prices=self.data,
                                                resample_by='W')
            covariance = ReturnsEstimators().calculate_returns(
                asset_prices=self.data, resample_by='W').cov()
            mvo.allocate(expected_asset_returns=expected_returns,
                         covariance_matrix=covariance.values)
    def test_max_diversification(self):
        # pylint: disable=invalid-name
        """
        Test the calculation of maximum diversification portfolio weights.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     solution='max_diversification',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_min_volatility_for_target_return(self):
        # pylint: disable=invalid-name
        """
        Test the calculation of minimum volatility-target return portfolio weights.
        """

        mvo = MeanVarianceOptimisation()
        prices = self.data.resample('W').last()
        mvo.allocate(asset_prices=prices,
                     solution='efficient_risk',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_portfolio_metrics(self):
        """
        Test the printing of portfolio metrics to stdout.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data)
        with patch('sys.stdout', new=StringIO()) as fake_out:
            mvo.get_portfolio_metrics()
            output = fake_out.getvalue().strip()
            self.assertTrue(
                'Portfolio Return = 0.017362404155484328' in output)
            self.assertTrue('Portfolio Risk = 9.385801639141577e-06' in output)
            self.assertTrue(
                'Portfolio Sharpe Ratio = -4.125045816381286' in output)
    def test_mvo_with_exponential_returns(self):
        # pylint: disable=invalid-name
        """
        Test the calculation of inverse-variance portfolio weights.
        """

        mvo = MeanVarianceOptimisation(
            calculate_expected_returns='exponential')
        mvo.allocate(asset_prices=self.data,
                     solution='inverse_variance',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_max_return_min_volatility_with_specific_weight_bounds(self):
        # pylint: disable=invalid-name
        """
        Test the calculation of weights when specific bounds are supplied.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     solution='max_return_min_volatility',
                     weight_bounds=['weights[0] <= 0.3'],
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def _calculate_efficient_frontier(mean, covariance, asset_names,
                                      num_portfolios):
        """
        Generate portfolios along the efficient frontier.

        :param mean: (Numpy array) Mean returns.
        :param covariance: (Numpy matrix) Covariance of returns.
        :param asset_names: (Python list) List of asset names in the portfolio.
        :param num_portfolios: (int) Number of portfolios to generate along the efficient frontier.
        :return: (list, list, list) Portfolios on the efficient frontier.
        """

        # Calculate minimum risk portfolio
        mvo = MeanVarianceOptimisation()
        mvo.allocate(expected_asset_returns=mean,
                     covariance_matrix=covariance,
                     asset_names=asset_names,
                     solution='min_volatility')
        min_volatility_weights = mvo.weights.values
        min_volatility_return = mvo.portfolio_return

        # Maximum return
        maximum_return = np.max(mean)

        # Get the target returns along the frontier
        steps = (maximum_return - min_volatility_return) / (num_portfolios - 1)
        target_returns = np.arange(min_volatility_return, maximum_return,
                                   steps)

        # Start calculating the portfolios along the frontier
        portfolio_weights, portfolio_volatility, portfolio_return = min_volatility_weights, mvo.portfolio_risk, mvo.portfolio_return

        bayesian_portfolios = [portfolio_weights]
        bayesian_portfolio_volatilities = [portfolio_volatility]
        bayesian_portfolio_returns = [portfolio_return]
        for target_return in target_returns:
            mvo.allocate(expected_asset_returns=mean,
                         covariance_matrix=covariance,
                         target_return=target_return,
                         asset_names=asset_names,
                         solution='efficient_risk')
            bayesian_portfolios.append(mvo.weights.values)
            bayesian_portfolio_volatilities.append(mvo.portfolio_risk)
            bayesian_portfolio_returns.append(mvo.portfolio_return)

        return bayesian_portfolios, bayesian_portfolio_volatilities, bayesian_portfolio_returns
    def test_no_asset_names_by_passing_cov(self):
        """
        Test MVO when not supplying a list of asset names but passing covariance matrix as input
        """

        mvo = MeanVarianceOptimisation()
        expected_returns = ReturnsEstimators(
        ).calculate_exponential_historical_returns(asset_prices=self.data,
                                                   resample_by='W')
        covariance = ReturnsEstimators().calculate_returns(
            asset_prices=self.data, resample_by='W').cov()
        mvo.allocate(expected_asset_returns=expected_returns,
                     covariance_matrix=covariance)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_mvo_with_input_as_returns_and_covariance(self):
        # pylint: disable=invalid-name
        """
        Test MVO when we pass expected returns and covariance matrix as input.
        """

        mvo = MeanVarianceOptimisation()
        expected_returns = ReturnsEstimators(
        ).calculate_mean_historical_returns(asset_prices=self.data,
                                            resample_by='W')
        covariance = ReturnsEstimators().calculate_returns(
            asset_prices=self.data, resample_by='W').cov()
        mvo.allocate(covariance_matrix=covariance,
                     expected_asset_returns=expected_returns,
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)
示例#14
0
    def _calculate_bayesian_frontier(self, asset_names):
        """
        Generate portfolios along the bayesian efficient frontier.

        :param asset_names: (Numpy array/Python list) List of asset names in the portfolio.
        :return: (Python list, Python list, Python list) Portfolios along the bayesian efficient frontier.
        """

        # Calculate minimum risk portfolio
        mvo = MeanVarianceOptimisation()
        mvo.allocate(expected_asset_returns=self.posterior_mean,
                     covariance_matrix=self.posterior_covariance,
                     asset_names=asset_names,
                     solution='min_volatility')
        min_volatility_weights = mvo.weights.values
        min_volatility_return = mvo.portfolio_return

        # Maximum return
        maximum_return = np.max(self.posterior_mean)

        # Get the target returns along the frontier
        step_size = (maximum_return - min_volatility_return) / (self.discretisations - 1)
        target_returns = np.arange(min_volatility_return, maximum_return + step_size, step_size)

        # Start calculating the portfolios along the frontier
        bayesian_portfolios = [min_volatility_weights]
        bayesian_portfolio_volatilities = [mvo.portfolio_risk]
        bayesian_portfolio_returns = [mvo.portfolio_return]
        for target_return in target_returns:
            mvo.allocate(expected_asset_returns=self.posterior_mean,
                         covariance_matrix=self.posterior_covariance,
                         target_return=target_return,
                         asset_names=asset_names,
                         solution='efficient_risk')
            bayesian_portfolios.append(mvo.weights.values)
            bayesian_portfolio_volatilities.append(mvo.portfolio_risk)
            bayesian_portfolio_returns.append(mvo.portfolio_return)

        return bayesian_portfolios, bayesian_portfolio_volatilities, bayesian_portfolio_returns
    def test_min_volatility_solution(self):
        """
        Test the calculation of minimum volatility portfolio weights.
        """

        mvo = MeanVarianceOptimisation()
        mvo.allocate(asset_prices=self.data,
                     solution='min_volatility',
                     asset_names=self.data.columns)
        weights = mvo.weights.values[0]
        assert (weights >= 0).all()
        assert len(weights) == self.data.shape[1]
        np.testing.assert_almost_equal(np.sum(weights), 1)

        # Check that the volatility is the minimum among all other portfolios
        for solution_string in {
                "inverse_variance", "max_sharpe", "max_return_min_volatility",
                "max_diversification", "max_decorrelation"
        }:
            mvo_ = MeanVarianceOptimisation()
            mvo_.allocate(asset_prices=self.data,
                          solution=solution_string,
                          asset_names=self.data.columns)
            assert mvo.portfolio_risk <= mvo_.portfolio_risk