Returns an efficient portfolio allocation for the given risk index.

def allocate(self):
        """
        Returns an efficient portfolio allocation for the given risk index.
        """
        df = self.manager.get_historic_data()[self.SUPPORTED_COINS]

        #==== Calculate the daily changes ====#
        change_columns = []
        for column in df:
            if column in self.SUPPORTED_COINS:
                change_column = '{}_change'.format(column)
                values = pd.Series(
                    (df[column].shift(-1) - df[column]) /
                    -df[column].shift(-1)
                ).values
                df[change_column] = values
                change_columns.append(change_column)

        # print(df.head())
        # print(df.tail())

        #==== Variances and returns ====#
        columns = change_columns
        # NOTE: `risks` is not used, but may be used in the future
        risks = df[columns].apply(np.nanvar, axis=0)
        # print('\nVariance:\n{}\n'.format(risks))
        returns = df[columns].apply(np.nanmean, axis=0)
        # print('\nExpected returns:\n{}\n'.format(returns))

        #==== Calculate risk and expected return ====#
        cov_matrix = df[columns].cov()
        # NOTE: The diagonal variances weren't calculated correctly, so here is a fix.
        cov_matrix.values[[np.arange(len(self.SUPPORTED_COINS))] * 2] = df[columns].apply(np.nanvar, axis=0)
        weights = np.array([1/len(self.SUPPORTED_COINS)] * len(self.SUPPORTED_COINS)).reshape(len(self.SUPPORTED_COINS), 1)

        #==== Calculate portfolio with the minimum risk ====#
        min_risk = self.get_min_risk(weights, cov_matrix)
        min_return = np.dot(min_risk, returns.values)

        #==== Calculate portfolio with the maximum return ====#
        max_return = self.get_max_return(weights, returns)

        #==== Calculate efficient frontier ====#
        frontier = self.efficient_frontier(
            returns, cov_matrix, min_return, max_return, 6
        )
        return frontier