def test_bayesian_allocation_for_asset_names(self):
        # pylint: disable=invalid-name
        """
        Test bayesian allocation when passing a list of custom asset names
        """

        bayes_allocator = RobustBayesianAllocation(
            discretisations=self.num_of_portfolios)
        bayes_allocator.allocate(sample_mean=self.sample_mean,
                                 sample_covariance=self.sample_covariance,
                                 prior_mean=self.prior_mean,
                                 prior_covariance=self.prior_covariance,
                                 relative_confidence_in_prior_mean=3,
                                 relative_confidence_in_prior_covariance=3,
                                 max_volatility=0.8 * max(self.sample_mean),
                                 asset_names=self.asset_names,
                                 sample_size=self.num_observations)
        weights = bayes_allocator.weights.values[0]
        assert len(weights) == self.num_assets
        assert round(weights[0], 3) == 0.733
        assert round(weights[1], 3) == 0.146
        assert round(weights[2], 3) == 0.094
        assert round(weights[3], 3) == 0.019
        assert round(weights[4], 3) == 0.008
        np.testing.assert_almost_equal(np.sum(weights), 1)
    def test_value_error_for_no_weights_found(self):
        """
        Test ValueError when no robust portfolio is found.
        """

        with self.assertRaises(ValueError):
            bayes_allocator = RobustBayesianAllocation(
                discretisations=self.num_of_portfolios)
            bayes_allocator.allocate(sample_mean=self.sample_mean,
                                     sample_covariance=self.sample_covariance,
                                     prior_mean=self.prior_mean,
                                     prior_covariance=self.prior_covariance,
                                     relative_confidence_in_prior_mean=3,
                                     relative_confidence_in_prior_covariance=3,
                                     max_volatility=1e-5,
                                     sample_size=self.num_observations)
    def test_value_error_for_negative_confidence(self):
        """
        Test ValueError when the investor specifies negative confidence in prior.
        """

        with self.assertRaises(ValueError):
            bayes_allocator = RobustBayesianAllocation(
                discretisations=self.num_of_portfolios)
            bayes_allocator.allocate(sample_mean=self.sample_mean,
                                     sample_covariance=self.sample_covariance,
                                     prior_mean=self.prior_mean,
                                     prior_covariance=self.prior_covariance,
                                     relative_confidence_in_prior_mean=-3,
                                     relative_confidence_in_prior_covariance=3,
                                     max_volatility=0.8 *
                                     max(self.sample_mean),
                                     sample_size=self.num_observations)
    def test_value_error_for_diff_means_lengths(self):
        """
        Test ValueError when the lengths of sample mean and prior mean do not match.
        """

        with self.assertRaises(ValueError):
            bayes_allocator = RobustBayesianAllocation(
                discretisations=self.num_of_portfolios)
            bayes_allocator.allocate(
                sample_mean=self.sample_mean[:2],
                sample_covariance=self.sample_covariance[:2, :2],
                prior_mean=self.prior_mean[:3],
                prior_covariance=self.prior_covariance[:3, :3],
                relative_confidence_in_prior_mean=3,
                relative_confidence_in_prior_covariance=3,
                max_volatility=0.8 * max(self.sample_mean),
                sample_size=self.num_observations)
    def test_value_error_for_single_sample_size(self):
        """
        Test ValueError when the investor specifies only sample size of 1.
        """

        with self.assertRaises(ValueError):
            bayes_allocator = RobustBayesianAllocation(
                discretisations=self.num_of_portfolios)
            bayes_allocator.allocate(
                sample_mean=self.sample_mean,
                sample_covariance=self.sample_covariance,
                prior_mean=self.prior_mean,
                prior_covariance=self.prior_covariance,
                relative_confidence_in_prior_mean=3,
                relative_confidence_in_prior_covariance=3,
                posterior_covariance_estimation_risk_level=1,
                max_volatility=0.8 * max(self.sample_mean),
                sample_size=1)
    def test_value_error_for_negative_max_volatility(self):
        # pylint: disable=invalid-name
        """
        Test ValueError when the investor specifies negative maximum volatility.
        """

        with self.assertRaises(ValueError):
            bayes_allocator = RobustBayesianAllocation(
                discretisations=self.num_of_portfolios)
            bayes_allocator.allocate(
                sample_mean=self.sample_mean,
                sample_covariance=self.sample_covariance,
                prior_mean=self.prior_mean,
                prior_covariance=self.prior_covariance,
                relative_confidence_in_prior_mean=3,
                relative_confidence_in_prior_covariance=3,
                posterior_covariance_estimation_risk_level=1,
                max_volatility=-2,
                sample_size=self.num_observations)
    def test_bayesian_allocation_for_low_confidence(self):
        # pylint: disable=invalid-name, too-many-locals, broad-except
        """
        Test the efficient frontiers generated by RBA when investor has low confidence in prior.
        """

        sample_expected_returns = []
        sample_volatilities = []
        bayesian_expected_returns = []
        bayesian_volatilities = []
        robust_bayesian_expected_returns = []
        robust_bayesian_volatilities = []
        prior_expected_returns = []
        prior_volatilities = []

        for _ in range(self.num_simulations):
            try:
                returns = np.random.multivariate_normal(
                    mean=self.true_mean,
                    cov=self.true_covariance,
                    size=self.num_observations)

                # Sample estimate
                sample_mean = np.mean(returns, axis=0).reshape(
                    (self.num_assets, 1))
                sample_covariance = np.cov(returns, rowvar=False)

                # Bayesian prior
                prior_covariance = np.diag(np.diag(sample_covariance))
                prior_mean = 0.5 * sample_covariance.dot(
                    np.ones((self.num_assets, 1))) / self.num_assets

                # Prior efficient frontier
                _, volatilities, expected_returns = self._calculate_efficient_frontier(
                    prior_mean, prior_covariance, self.asset_names,
                    self.num_of_portfolios)

                for i in range(self.num_of_portfolios):
                    prior_expected_returns.append(expected_returns[i])
                    prior_volatilities.append(volatilities[i])

                # Sample efficient frontier (True market frontier)
                _, volatilities, expected_returns = self._calculate_efficient_frontier(
                    sample_mean, sample_covariance, self.asset_names,
                    self.num_of_portfolios)

                for i in range(self.num_of_portfolios):
                    sample_expected_returns.append(expected_returns[i])
                    sample_volatilities.append(volatilities[i])

                # Do robust bayesian allocation
                bayes_allocator = RobustBayesianAllocation(
                    discretisations=self.num_of_portfolios)
                bayes_allocator.allocate(
                    sample_mean=sample_mean,
                    sample_covariance=sample_covariance,
                    prior_mean=prior_mean,
                    prior_covariance=prior_covariance,
                    relative_confidence_in_prior_mean=1e-4,
                    relative_confidence_in_prior_covariance=1e-4,
                    max_volatility=0.8 * max(sample_mean),
                    sample_size=self.num_observations)

                # Bayesian efficient frontier
                _, volatilities, expected_returns = self._calculate_efficient_frontier(
                    bayes_allocator.posterior_mean,
                    bayes_allocator.posterior_covariance, self.asset_names,
                    self.num_of_portfolios)

                for i in range(self.num_of_portfolios):
                    bayesian_expected_returns.append(expected_returns[i])
                    bayesian_volatilities.append(volatilities[i])

                # Robust bayesian portfolio
                robust_bayesian_expected_returns.append(
                    bayes_allocator.portfolio_return)
                robust_bayesian_volatilities.append(
                    bayes_allocator.portfolio_risk)
            except Exception:
                continue

        assert np.mean(sample_expected_returns) >= np.mean(
            robust_bayesian_expected_returns)
        assert np.mean(robust_bayesian_expected_returns) >= np.mean(
            prior_expected_returns)
        assert np.abs(np.mean(sample_expected_returns) - np.mean(robust_bayesian_expected_returns)) < \
               np.abs(np.mean(robust_bayesian_expected_returns) - np.mean(prior_expected_returns))