Exemplo n.º 1
0
 def gmv_monthly(self) -> Tuple[float, float]:
     """
     Returns the monthly risk and return of the Global Minimum Volatility portfolio
     """
     return (
         Frame.get_portfolio_risk(self.gmv_weights, self.ror),
         Frame.get_portfolio_mean_return(self.gmv_weights, self.ror),
     )
Exemplo n.º 2
0
 def max_cagr_asset(self) -> dict:
     """
     Find an asset with max CAGR.
     """
     max_asset_cagr = Frame.get_cagr(self.ror).max()
     ticker_with_largest_cagr = Frame.get_cagr(self.ror).nlargest(1, keep='first').index.values[0]
     return {'max_asset_cagr': max_asset_cagr,
             'ticker_with_largest_cagr': ticker_with_largest_cagr,
             'list_position': self.symbols.index(ticker_with_largest_cagr)
             }
Exemplo n.º 3
0
    def get_ef_points(self):
        """
        Get all the points for the Efficient Frontier running optimizer.

        If verbose=True calculates elapsed time for each point and the total elapsed time.
        """
        main_start_time = time.time()
        df = pd.DataFrame()
        # left part of the EF
        for i, target_cagr in enumerate(self.target_cagr_range_left):
            start_time = time.time()
            row = self.minimize_risk(target_cagr)
            df = df.append(row, ignore_index=True)
            end_time = time.time()
            if self.verbose:
                print(f"left EF point #{i + 1}/{self.n_points} is done in {end_time - start_time:.2f} sec.")
        # right part of the EF
        range_right = self.target_cagr_range_right
        if range_right is not None:  # range_right can be a DataFrame. Must put an explicit "is not None"
            n = len(range_right)
            for i, target_cagr in enumerate(range_right):
                start_time = time.time()
                row = self.maximize_risk(target_cagr)
                df = df.append(row, ignore_index=True)
                end_time = time.time()
                if self.verbose:
                    print(f"right EF point #{i + 1}/{n} is done in {end_time - start_time:.2f} sec.")
        df = Frame.change_columns_order(df, ['Risk', 'CAGR'])
        main_end_time = time.time()
        if self.verbose:
            print(f"Total time taken is {(main_end_time - main_start_time) / 60:.2f} min.")
        self._ef_points = df
Exemplo n.º 4
0
 def target_cagr_range_left(self) -> np.ndarray:
     """
     Full range of cagr values (from min to max).
     """
     max_cagr = self.global_max_return_portfolio['CAGR']
     min_cagr = Frame.get_cagr(self.ror).min()
     return np.linspace(min_cagr, max_cagr, self.n_points)
Exemplo n.º 5
0
    def optimize_return(self, option: str = "max") -> dict:
        """
        Finds global max or min for the rate of return.
        Returns monthly values for the risk, mean return and the weights.
        'max' - search for global maximum
        'min' - search for global minimum
        """
        n = self.ror.shape[1]  # Number of assets
        init_guess = np.repeat(1 / n, n)
        # Set the objective function
        if option == "max":

            def objective_function(w, ror):
                month_return_value = Frame.get_portfolio_mean_return(w, ror)
                return -month_return_value

        elif option == "min":

            def objective_function(w, ror):
                month_return_value = Frame.get_portfolio_mean_return(w, ror)
                return month_return_value

        else:
            raise ValueError('option should be "max" or "min"')
        # construct the constraints
        weights_sum_to_1 = {"type": "eq", "fun": lambda weights: np.sum(weights) - 1}
        weights = minimize(
            objective_function,
            init_guess,
            args=(self.ror,),
            method="SLSQP",
            constraints=(weights_sum_to_1,),
            bounds=self.bounds,
            options={
                "disp": False,
                "ftol": 1e-08,
            },  # 1e-06 is not enough to optimize monthly returns
        )
        if weights.success:
            portfolio_risk = Frame.get_portfolio_risk(weights.x, self.ror)
            if option.lower() == "max":
                optimized_return = -weights.fun
            else:
                optimized_return = weights.fun
            point = {
                "Weights": weights.x,
                "Mean_return_monthly": optimized_return,
                "Risk_monthly": portfolio_risk,
            }
            return point
        else:
            raise Exception("No solutions where found")
Exemplo n.º 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
Exemplo n.º 7
0
 def ef_points(self) -> pd.DataFrame:
     """
     DataFrame of weights and risk/return values for the Efficient Frontier.
     The columns of the DataFrame:
     - weights
     - mean return
     - CAGR
     - risk (std)
     All the values are annualized.
     """
     target_rs = self.mean_return_range
     df = pd.DataFrame(dtype="float")
     for x in target_rs:
         row = self.minimize_risk(x, monthly_return=True)
         df = df.append(row, ignore_index=True)
     df = Frame.change_columns_order(df, ["Risk", "Mean return", "CAGR"])
     return df
Exemplo n.º 8
0
 def max_cagr_asset_right_to_max_cagr(self) -> Optional[dict]:
     """
     The asset with max CAGR lieing to the right of the global
     max CAGR point (risk should be more than self.max_return['Risk']).
     Global max return point should not be an asset.
     """
     tolerance = 0.01  # assets CAGR should be less than max CAGR with certain tolerance
     global_max_cagr_is_not_asset = (self.get_cagr() < self.global_max_return_portfolio['CAGR'] * (1 - tolerance)).all()
     if global_max_cagr_is_not_asset:
         condition = self.risk_annual.values > self.global_max_return_portfolio['Risk']
         ror_selected = self.ror.loc[:, condition]
         if not ror_selected.empty:
             cagr_selected = Frame.get_cagr(ror_selected)
             max_asset_cagr = cagr_selected.max()
             ticker_with_largest_cagr = cagr_selected.nlargest(1, keep='first').index.values[0]
             return {'max_asset_cagr': max_asset_cagr,
                     'ticker_with_largest_cagr': ticker_with_largest_cagr,
                     'list_position': self.symbols.index(ticker_with_largest_cagr)
                     }
Exemplo n.º 9
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
Exemplo n.º 10
0
 def objective_function(w):
     return Frame.get_portfolio_risk(w, ror)
Exemplo n.º 11
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
Exemplo n.º 12
0
 def objective_function(w, ror):
     month_return_value = Frame.get_portfolio_mean_return(w, ror)
     return month_return_value