def get_fund_predictions(self):
        """
        This method generates 12 month predictions for fund expected returns and covariance.
        :return: (ers, covars)
                 ers is a pandas series of 12 month expected return indexed on fund id
                 covars is a pandas dataframe of 12 month expected covariance indexed on both axis by fund id
        """

        today = self.data_provider.get_current_date()

        # The earliest data we want to build our predictions
        begin_date = today - timedelta(days=365 * MAX_HISTORY)

        # Get the funds
        funds = self.data_provider.get_tickers()

        # Get all the return data relating to the funds
        fund_returns, benchmark_returns = get_return_history(
            funds, begin_date, today)

        # For now we're assigning the benchmark returns for the return history.
        returns = get_benchmark_returns(funds, benchmark_returns)

        # Filter any funds that don't have enough data.
        # Our latest start date is the first day of the last complete investment cycle from the current date.
        latest_start = self.get_last_cycle_start()

        # try to be more tolerant - although not sure what this will do
        returns = filter_returns(returns,
                                 OLDEST_ACCEPTABLE_DATA,
                                 latest_start=latest_start)
        #returns = filter_returns(returns, OLDEST_ACCEPTABLE_DATA) this gives weird symmetric error in portfolio optim

        if returns.empty:
            raise OptimizationException('No returns data available')

        oldest_dt = today - timedelta(days=OLDEST_ACCEPTABLE_DATA)
        cycles = self.get_cycle_obs(begin_date)
        if cycles.index[-1] < oldest_dt:
            raise OptimizationException('Last observed cycle was too long ago')

        probs = np.array(self.get_normalized_probabilities(oldest_dt).tail(1))

        return_cycles = self._merge_cycle_returns(returns, cycles)
        mu = (1 + self._expected_returns_prob_v1(return_cycles,
                                                 probs))**WEEKDAYS_PER_YEAR - 1
        sigma = self._covariance_matrix_prob_v1(return_cycles,
                                                probs) * WEEKDAYS_PER_YEAR
        return mu, sigma
Example #2
0
 def get_investment_cycles(self):
     # Populate the cache as we'll be hitting it a few times. Boolean evaluation causes full cache population
     obs = InvestmentCycleObservation.objects.all().filter(
         as_of__lt=self.get_current_date()).order_by('as_of')
     if not obs:
         raise OptimizationException(
             "There are no historic observations available")
     return obs
Example #3
0
 def _merge_cycle_returns(self, returns, cycles):
     """
     Merge a Daily and a Monthly Dataframe by certain label column
     :param returns: The Dataframe of the daily return observations
     :param cycles: The Dataframe of the monthly investment cycle observations
     :return: Returns the merged dataframe
     """
     returns[CYCLE_LABEL] = cycles.reindex(returns.index, method='pad')
     if len(returns[CYCLE_LABEL].unique()) != 5:
         emsg = "A full investment cycle was not present in the available history ({} - {})"
         logger.error(emsg.format(returns.index[0], returns.index[-1]))
         raise OptimizationException(
             "Not enough data for portfolio optimisation.")
     return returns
    def get_normalized_probabilities(self, begin_date):
        """
        Normalize the probability to sum 1 (This will configure a prob space)
        :param begin_date: The earliest date you want the probabilities from
        :return: Dataframe of normalized probabilities.
                 The fields are in positions corresponding to the class ids on InvestmentCycleObservation.
        """
        probs_df = self.data_provider.get_probs_df(begin_date)

        if probs_df.empty:
            raise OptimizationException(
                "There are no investment clock predictions available")
        sum_row = probs_df.sum(axis=1)
        norm_probs = probs_df.div(sum_row, axis=0)
        return norm_probs
Example #5
0
 def get_investment_cycles(self):
     obs = self.investment_cycles
     if not obs:
         raise OptimizationException("There are no historic observations available")
     return self.investment_cycles