示例#1
0
def fit_cor(copula: Copula, data: np.ndarray, typ: str) -> np.ndarray:
    """
    Constructs parameter matrix from matrix of Kendall's Taus or Spearman's Rho

    Parameters
    ----------
    copula: AbstractCopula
        Copula instance

    data: ndarray
        Data to fit copula with
    typ: {'irho', 'itau'}
        The type of rank correlation measure to use. 'itau' uses Kendall's tau while 'irho' uses Spearman's rho

    Returns
    -------
    ndarray
        Parameter matrix is copula is elliptical. Otherwise, a vector
    """

    indices = tri_indices(copula.dim, 1, 'lower')
    if typ == 'itau':
        tau = kendall_tau(data)[indices]
        theta = copula.itau(tau)
    elif typ == 'irho':
        rho = spearman_rho(data)[indices]
        theta = copula.irho(rho)
    else:
        raise ValueError("Correlation Inversion must be either 'itau' or 'irho'")

    if is_elliptical(copula):
        theta = near_psd(create_cov_matrix(theta))[indices]

    return theta
示例#2
0
 def _force_psd(self):
     """
     Forces covariance matrix to be positive semi-definite. This is useful when user is overwriting covariance
     parameters
     """
     cov = near_psd(self.sigma)
     self._rhos = cov[tri_indices(self.dim, 1, 'lower')]
示例#3
0
 def _force_psd(self):
     """
     Forces covariance matrix to be positive semi-definite. This is useful when user is overwriting covariance
     parameters
     """
     cov = near_psd(self.sigma)
     self._rhos = cov[tri_indices(self.dim, 1, 'lower')]
示例#4
0
def fit_cor(copula, data: np.ndarray, typ: str) -> np.ndarray:
    """
    Constructs parameter matrix from matrix of Kendall's Taus or Spearman's Rho

    Parameters
    ----------
    copula: BaseCopula
        Copula instance

    data: ndarray
        Data to fit copula with
    typ: {'irho', 'itau'}
        The type of rank correlation measure to use. 'itau' uses Kendall's tau while 'irho' uses Spearman's rho

    Returns
    -------
    ndarray
        Parameter matrix is copula is elliptical. Otherwise, a vector
    """

    indices = tri_indices(copula.dim, 1, 'lower')
    if typ == 'itau':
        tau = kendall_tau(data)[indices]
        theta = copula.itau(tau)
    elif typ == 'irho':
        rho = spearman_rho(data)[indices]
        theta = copula.irho(rho)
    else:
        raise ValueError(
            "Correlation Inversion must be either 'itau' or 'irho'")

    if is_elliptical(copula):
        theta = near_psd(create_cov_matrix(theta))[indices]

    return theta
示例#5
0
    def __setitem__(self, index: Union[int, Tuple[Union[slice, int],
                                                  Union[slice, int]], slice],
                    value: Union[float, Collection[float], np.ndarray]):
        d = self.dim

        if np.isscalar(value):
            if value < -1 or value > 1:
                raise ValueError("correlation value must be between -1 and 1")
        else:
            value = np.asarray(value)
            if not np.all((value >= -1 - EPS) & (value <= 1 + EPS)):
                raise ValueError("correlation value must be between -1 and 1")

        if isinstance(index, slice):
            value = near_psd(value)
            if value.shape != (d, d):
                raise ValueError(
                    f"The value being set should be a matrix of dimension ({d}, {d})"
                )
            self._rhos = value[tri_indices(d, 1, 'lower')]
            return

        if isinstance(index, int):
            self._rhos[index] = value

        else:
            index = tuple(index)
            if len(index) != 2:
                raise IndexError('index can only be 1 or 2-dimensional')

            x, y = index
            # having 2 slices for indices is equivalent to self[:]
            if isinstance(x, slice) and isinstance(y, slice):
                self[:] = value
                return
            elif isinstance(x, slice) or isinstance(y, slice):
                value = np.repeat(
                    value, d) if np.isscalar(value) else np.asarray(value)
                if len(value) != d:
                    raise ValueError(
                        f"value must be a scalar or be a vector with length {d}"
                    )

                # one of the item is
                for i, v in enumerate(value):
                    idx = (i, y) if isinstance(x, slice) else (x, i)
                    if idx[0] == idx[1]:  # skip diagonals
                        continue

                    idx = _get_rho_index(d, idx)
                    self._rhos[idx] = v
            else:
                # both are integers
                idx = _get_rho_index(d, index)
                self._rhos[idx] = float(value)

        self._force_psd()
示例#6
0
    def covs(self, value: Union[C[float], np.ndarray]):
        value = np.asarray(value)

        shape = self.n_clusters, self.n_dim, self.n_dim
        if value.shape != shape:
            raise GMCParamError(f"covariance array should have shape {shape}")

        self._covs = np.array([near_psd(cov) for cov in value
                               ])  # forces covariance matrix to be psd
示例#7
0
    def __setitem__(self, i, value):
        d = self.dim

        if isinstance(i, slice):
            value = near_psd(value)
            if value.shape != (d, d):
                return IndexError(f"The value being set should be a matrix of dimension ({d}, {d})")
            self._rhos = value[tri_indices(d, 1, 'lower')]
            return

        if isinstance(i, int):
            self._rhos[i] = value

        else:
            i = _get_rho_index(d, i)
            self._rhos[i] = value

        self._force_psd()
示例#8
0
    def __init__(self, data: np.ndarray, time_unit='monthly'):
        """
        Returns an OptData class which is an enhancement of ndarray with helper methods

        Parameters
        ----------
        data: ndarray
            3D tensor where the first axis represents the time period, the second axis represents the trials
            and the third axis represents the assets

        time_unit: {int, 'monthly', 'quarterly', 'semi-annually', 'yearly'}, optional
            Specifies how many units (first axis) is required to represent a year. For example, if each time period
            represents a month, set this to 12. If quarterly, set to 4. Defaults to 12 which means 1 period represents
            a month. Alternatively, specify one of 'monthly', 'quarterly', 'semi-annually' or 'yearly'
        """

        assert data.ndim == 3, "Data must be 3 dimensional with shape like (t, n, a) where `t` represents the time " \
                               "periods, `n` represents the trials and `a` represents the assets"

        periods, trials, n_assets = data.shape
        self.time_unit = translate_frequency(time_unit)

        # empirical covariance taken along the time-asset axis then averaged by trials
        # annualized data
        a = (data + 1).reshape(periods // self.time_unit, self.time_unit,
                               trials, n_assets).prod(1) - 1
        cov_mat = np.mean(
            [np.cov(a[i].T) for i in range(periods // self.time_unit)], 0)
        cov_mat = near_psd(cov_mat)
        assert is_psd(
            cov_mat), "covariance matrix must be positive semi-definite"

        if np.allclose(np.diag(cov_mat),
                       1) and np.alltrue(np.abs(cov_mat) <= 1):
            warnings.warn(
                "The covariance matrix feels like a correlation matrix. Are you sure it's correct?"
            )

        self.n_years = periods / self.time_unit
        self.n_assets = n_assets

        # minor optimization when using rebalanced optimization. This is essentially a cache
        self._unrebalanced_returns_data: Optional[np.ndarray] = None
        self._cov_mat = cov_mat
示例#9
0
    def __setitem__(self, i, value):
        d = self.dim

        if isinstance(i, slice):
            value = near_psd(value)
            if value.shape != (d, d):
                return IndexError(f"The value being set should be a matrix of dimension ({d}, {d})")
            self._rhos = value[tri_indices(d, 1, 'lower')]
            return

        assert -1.0 <= value <= 1.0, "correlation value must be between -1 and 1"
        if isinstance(i, int):
            self._rhos[i] = value

        else:
            i = _get_rho_index(d, i)
            self._rhos[i] = value

        self._force_psd()
示例#10
0
def coalesce_covariance_matrix(
        cov,
        w: Iterable[float],
        indices: Optional[Iterable[int]] = None) -> Union[np.ndarray, float]:
    """
    Aggregates the covariance with the weights given at the indices specified
    The aggregated column will be the first column.
    Parameters
    ----------
    cov: ndarray
        Covariance matrix of the portfolio
    w: ndarray
        The weights to aggregate the columns by. Weights do not have to sum to 1, if it needs to, you should check
        it prior
    indices: iterable int, optional
        The column index of the aggregated data. If not specified, method will aggregate the first 'n' columns
        where 'n' is the length of :code:`w`
    Returns
    -------
    ndarray
        Aggregated covariance matrix
    Examples
    --------
    If we have a (60 x 1000 x 10) data and we want to aggregate the assets the first 3 indexes,
    >>> from allopy.opt_data import coalesce_covariance_matrix
    >>> import numpy as np
    form covariance matrix
    >>> np.random.seed(8888)
    >>> cov = np.random.standard_normal((5, 5))
    >>> cov = cov @ cov.T
    coalesce first and second column where contribution is (30%, 70%) respectively.
    Does not have to sum to 1
    >>> coalesce_covariance_matrix(cov, (0.3, 0.7))
    coalesce fourth and fifth column
    >>> coalesce_covariance_matrix(cov, (0.2, 0.4), (3, 4))
    """
    w = np.asarray(w)
    cov = np.asarray(cov)
    n = len(w)

    assert cov.ndim == 2 and cov.shape[0] == cov.shape[
        1], 'cov must be a square matrix'
    assert n <= len(
        cov), 'adjustment weights cannot be larger than the covariance matrix'

    if indices is None:
        indices = np.arange(n)

    _, a = cov.shape  # get number of assets originally

    # form transform matrix
    T = np.zeros((a - n + 1, a))
    T[0, :n] = w
    T[1:, n:] = np.eye(a - n)

    # re-order covariance matrix
    rev_indices = sorted(
        set(range(a)) -
        set(indices))  # these are the indices that are not aggregated
    indices = [*indices, *rev_indices]
    cov = cov[
        indices][:,
                 indices]  # reorder the covariance matrix, first by rows then by columns

    cov = T @ cov @ T.T
    return float(cov) if cov.size == 1 else near_psd(cov)