예제 #1
0
 def gmv_annualized(self) -> Tuple[float, float]:
     """
     Returns the annualized risk and return of the Global Minimum Volatility portfolio
     """
     return (
         Float.annualize_risk(self.gmv_monthly[0], self.gmv_monthly[1]),
         Float.annualize_return(self.gmv_monthly[1]),
     )
예제 #2
0
 def objective_function(w):
     # annual risk
     ts = Rebalance.rebalanced_portfolio_return_ts(w, self.ror, period=self.reb_period)
     risk_monthly = ts.std()
     mean_return = ts.mean()
     result = - Float.annualize_risk(risk_monthly, mean_return)
     return result
예제 #3
0
 def gmv_annual_values(self) -> Tuple[float, float]:
     """
     Returns the annual risk (std) and CAGR of the Global Minimum Volatility portfolio.
     """
     returns = Rebalance.rebalanced_portfolio_return_ts(self.gmv_annual_weights, self.ror, period=self.reb_period)
     return (
         Float.annualize_risk(returns.std(), returns.mean()),
         (returns + 1.0).prod() ** (_MONTHS_PER_YEAR / returns.shape[0]) - 1.0,
     )
예제 #4
0
 def target_risk_range(self) -> np.ndarray:
     """
     Range of annual risk values (from min risk to max risk).
     """
     min_std = self.gmv_annual_values[0]
     ticker_with_largest_risk = self.ror.std().nlargest(1, keep='first').index.values[0]
     max_std_monthly = self.ror.std().max()
     mean_return = self.ror.loc[:, ticker_with_largest_risk].mean()
     max_std = Float.annualize_risk(max_std_monthly, mean_return)
     return np.linspace(min_std, max_std, self.n_points)
예제 #5
0
    def get_monte_carlo(self, n: int = 100) -> pd.DataFrame:
        """
        Generate N random risk / cagr point for rebalanced portfolios.
        Risk and cagr are calculated for a set of random weights.
        """
        weights_df = Float.get_random_weights(n, self.ror.shape[1])

        # Portfolio risk and cagr for each set of weights
        portfolios_ror = weights_df.aggregate(Rebalance.rebalanced_portfolio_return_ts, ror=self.ror, period=self.reb_period)
        random_portfolios = pd.DataFrame()
        for _, data in portfolios_ror.iterrows():
            risk_monthly = data.std()
            mean_return = data.mean()
            risk = Float.annualize_risk(risk_monthly, mean_return)
            cagr = Frame.get_cagr(data)
            row = {
                'Risk': risk,
                'CAGR': cagr
            }
            random_portfolios = random_portfolios.append(row, ignore_index=True)
        return random_portfolios
예제 #6
0
    def get_monte_carlo(self, n: int = 100, kind: str = "mean") -> pd.DataFrame:
        """
        Generate N random risk / cagr point for portfolios.
        Risk and cagr are calculated for a set of random weights.
        """
        weights_series = Float.get_random_weights(n, self.ror.shape[1])

        # Portfolio risk and return for each set of weights
        random_portfolios = pd.DataFrame(dtype=float)
        for weights in weights_series:
            risk_monthly = Frame.get_portfolio_risk(weights, self.ror)
            mean_return_monthly = Frame.get_portfolio_mean_return(weights, self.ror)
            risk = Float.annualize_risk(risk_monthly, mean_return_monthly)
            mean_return = Float.annualize_return(mean_return_monthly)
            if kind.lower() == "cagr":
                cagr = Float.approx_return_risk_adjusted(mean_return, risk)
                row = dict(Risk=risk, CAGR=cagr)
            elif kind.lower() == "mean":
                row = dict(Risk=risk, Return=mean_return)
            else:
                raise ValueError('kind should be "mean" or "cagr"')
            random_portfolios = random_portfolios.append(row, ignore_index=True)
        return random_portfolios
예제 #7
0
    def global_max_return_portfolio(self) -> dict:
        """
        Returns the weights and risk / CAGR of the maximum return portfolio point.
        """
        ror = self.ror
        period = self.reb_period
        n = self.ror.shape[1]  # Number of assets
        init_guess = np.repeat(1 / n, n)
        bounds = ((0.0, 1.0),) * n

        # Set the objective function
        def objective_function(w):
            # Accumulated return for rebalanced portfolio time series
            objective_function.returns = Rebalance.rebalanced_portfolio_return_ts(w, ror, period=period)
            accumulated_return = (objective_function.returns + 1.).prod() - 1.
            return - accumulated_return

        # construct the constraints
        weights_sum_to_1 = {'type': 'eq',
                            'fun': lambda weights: np.sum(weights) - 1
                            }
        weights = minimize(objective_function,
                           init_guess,
                           method='SLSQP',
                           options={'disp': False},
                           constraints=(weights_sum_to_1,),
                           bounds=bounds)
        portfolio_ts = objective_function.returns
        mean_return = portfolio_ts.mean()
        portfolio_risk = portfolio_ts.std()
        point = {
            'Weights': weights.x,
            'CAGR': (1 - weights.fun) ** (_MONTHS_PER_YEAR / self.ror.shape[0]) - 1,
            'Risk': Float.annualize_risk(portfolio_risk, mean_return),
            'Risk_monthly': portfolio_risk
        }
        return point
예제 #8
0
 def objective_function(w):
     ts = Rebalance.rebalanced_portfolio_return_ts(w, ror, period=period)
     mean_return = ts.mean()
     risk = ts.std()
     return Float.annualize_risk(risk=risk, mean_return=mean_return)
예제 #9
0
    def minimize_risk(
        self,
        target_return: float,
        monthly_return: bool = False,
        tolerance: float = 1e-08,
    ) -> Dict[str, float]:
        """
        Finds minimal risk given the target return.
        Returns a "point" with monthly values:
        - weights
        - mean return
        - CAGR
        - risk (std)
        Target return is a monthly or annual value:
        monthly_return = False / True
        tolerance - sets the accuracy for the solver
        """
        if not monthly_return:
            target_return = Float.get_monthly_return_from_annual(target_return)
        ror = self.ror
        n = ror.shape[1]  # number of assets
        init_guess = np.repeat(1 / n, n)  # initial weights

        def objective_function(w):
            return Frame.get_portfolio_risk(w, ror)

        # construct the constraints
        weights_sum_to_1 = {"type": "eq", "fun": lambda weights: np.sum(weights) - 1}
        return_is_target = {
            "type": "eq",
            "fun": lambda weights: target_return
            - Frame.get_portfolio_mean_return(weights, ror),
        }
        weights = minimize(
            objective_function,
            init_guess,
            method="SLSQP",
            constraints=(weights_sum_to_1, return_is_target),
            bounds=self.bounds,
            options={"disp": False, "ftol": tolerance},
        )
        if weights.success:
            # Calculate point of EF given optimal weights
            risk = weights.fun
            # Annualize risk and return
            a_r = Float.annualize_return(target_return)
            a_risk = Float.annualize_risk(risk=risk, mean_return=target_return)
            # # Risk adjusted return approximation
            # r_gmean = Float.approx_return_risk_adjusted(mean_return=a_r, std=a_risk)
            # CAGR calculation
            portfolio_return_ts = Frame.get_portfolio_return_ts(weights.x, ror)
            cagr = Frame.get_cagr(portfolio_return_ts)
            if not self.labels_are_tickers:
                asset_labels = list(self.names.values())
            else:
                asset_labels = self.symbols
            point = {x: y for x, y in zip(asset_labels, weights.x)}
            point["Mean return"] = a_r
            point["CAGR"] = cagr
            # point['CAGR (approx)'] = r_gmean
            point["Risk"] = a_risk
        else:
            raise Exception("No solutions were found")
        return point