def get_weights(self) -> QFSeries:
        assets_number = self.cov_matrix.shape[1]
        st_devs = self.std_of_assets.values
        st_devs = st_devs.reshape((1, -1))  # make it a horizontal vector

        P = matrix(self.cov_matrix.values)
        q = matrix(0.0, (assets_number, 1))

        A = matrix(st_devs)
        b = matrix([1.0])

        G_lower_bound, h_lower_bound = each_weight_greater_than_0_constraint(
            assets_number)

        if self.upper_constraint is not None:
            G_upper_bound, h_upper_bound = self._get_upper_bound_constraints(
                P, q, G_lower_bound, h_lower_bound, A, b)
            G, h = merge_constraints(G_lower_bound, h_lower_bound,
                                     G_upper_bound, h_upper_bound)
        else:
            G, h = G_lower_bound, h_lower_bound

        result = qp(P, q, G, h, A, b, options=self.optimizer_options)
        dummy_weights = np.array(result['x']).squeeze()
        scaled_weights = dummy_weights / dummy_weights.sum()
        weights_series = QFSeries(data=scaled_weights,
                                  index=self.cov_matrix.columns.values)

        return weights_series
Beispiel #2
0
    def get_optimal_weights(
            cls,
            P: np.ndarray = None,
            q: np.ndarray = None,
            upper_constraints: Union[Sequence, float] = None) -> np.ndarray:
        """
        Solves the problem defined by matrix h, vector f and constraints.

        Parameters
        ----------
        P
            a square matrix from the quadratic formula
        q
            a vector (can be empty) from the quadratic formula
        upper_constraints
            vector of upper limits of weights (if it's a single value, the constraint will be the same for each weight).
            Example: 0.5 means that max allocation of some asset can be 50%.

        Returns
        -------
        weights
            best weights for the given problem. Sum of all weights is equal 1.
        """
        assets_number = P.shape[0]
        if P is not None:
            P = matrix(P)
        else:
            P = matrix(0.0, (assets_number, assets_number))

        if q is not None:
            q = matrix(q)
        else:
            q = matrix(0.0, (assets_number, 1))

        A, b = constr.sum_weights_equal_1_constraint(assets_number)
        G, h = constr.each_weight_greater_than_0_constraint(assets_number)

        if upper_constraints is not None:
            G_2, h_2 = constr.upper_bound_constraint(assets_number,
                                                     upper_constraints)
            G, h = constr.merge_constraints(G, h, G_2, h_2)

        initial_weights = matrix(1.0 / assets_number, (assets_number, 1))

        # minimize (1/2)x'Px + q'x
        # subject to Gx <= h; Ax = b
        result = solvers.qp(P,
                            q,
                            G,
                            h,
                            A,
                            b,
                            initvals=initial_weights,
                            options=cls.options)
        return np.array(result['x']).squeeze()
    def _get_upper_bound_constraints(self, P, q, G_lower_bound, h_lower_bound,
                                     A, b):
        upper_bound = self.upper_constraint

        assets_number = self.cov_matrix.shape[1]
        if isinstance(upper_bound, float):
            upper_bound = [upper_bound] * assets_number

        h_upper_bound_scaled = None
        G_upper_bound, h_upper_bound = upper_bound_constraint(
            assets_number, upper_bound)

        prev_scaling_factor = 1
        scaling_factor = 30  # experimentally chosen
        loop_iter = 1

        while abs(prev_scaling_factor -
                  scaling_factor) > self.upper_bound_tolerance:
            h_upper_bound_scaled = h_upper_bound * scaling_factor
            G, h = merge_constraints(G_lower_bound, h_lower_bound,
                                     G_upper_bound, h_upper_bound_scaled)

            result = qp(P, q, G, h, A, b, options=self.optimizer_options)
            dummy_weights = np.array(result['x']).squeeze()

            prev_scaling_factor = scaling_factor
            scaling_factor = dummy_weights.sum()

            loop_iter += 1
            if loop_iter >= self.max_iter:
                warnings.warn(
                    "Max. number of iterations achieved during searching for a weights scaling factor."
                )
                break

        return G_upper_bound, h_upper_bound_scaled