# Source code for muarch.funcs.moments

from typing import Union

import numpy as np
from scipy.stats import kurtosis, skew

from .time_unit import get_integer_time_unit

__all__ = ["get_annualized_kurtosis", "get_annualized_mean", "get_annualized_sd", "get_annualized_skew"]

[docs]def get_annualized_kurtosis(data: np.ndarray, time_unit: Union[int, str] = 'monthly') -> Union[float, np.ndarray]:
"""
Gets the annualized kurtosis for each asset class in the data cube

Parameters
----------
data
Data matrix or tensor. The axis must represent time, trials and assets respectively where the assets axis
is valid only if the data is a tensor.

time_unit: int or str
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, you could put in a string name of the time_unit. Accepted values are weekly,
monthly, quarterly, semi-annually and yearly

Returns
-------
ndarray
The annualized kurtosis for the asset class or an array containing the annualized kurtosis for each
asset class.
"""
return np.mean([kurtosis(i) for i in annualize_data(data, time_unit)], 0)

[docs]def get_annualized_mean(data: np.ndarray, time_unit: Union[int, str] = 'monthly') -> Union[float, np.ndarray]:
"""
Gets the annualized mean for each asset class in the data cube

Parameters
----------
data
Data matrix or tensor. The axis must represent time, trials and assets respectively where the assets axis
is valid only if the data is a tensor.

time_unit: int or str
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, you could put in a string name of the time_unit. Accepted values are weekly,
monthly, quarterly, semi-annually and yearly

Returns
-------
float or ndarray
The annualized mean for the asset class or an array containing the annualized mean for each asset class.
"""
time_unit = get_integer_time_unit(time_unit)
years = len(data) // time_unit

data = (annualize_data(data, time_unit) + 1).prod(0)
return np.mean(np.sign(data) * np.abs(data) ** (1 / years), 0) - 1

[docs]def get_annualized_sd(data: np.ndarray, time_unit: Union[int, str] = 'monthly') -> Union[float, np.ndarray]:
"""
Gets the annualized standard deviation for each asset class in the data cube

Parameters
----------
data
Data matrix or tensor. The axis must represent time, trials and assets respectively where the assets axis
is valid only if the data is a tensor.

time_unit: int or str
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, you could put in a string name of the time_unit. Accepted values are weekly,
monthly, quarterly, semi-annually and yearly

Returns
-------
float ndarray
The annualized standard deviation (volatility) for the asset class or an array containing the annualized
standard deviation for each asset class.
"""
return annualize_data(data, time_unit).std(1).mean(0)

[docs]def get_annualized_skew(data: np.ndarray, time_unit: Union[int, str] = 'monthly') -> Union[float, np.ndarray]:
"""
Gets the annualized skew for each asset class in the data cube

Parameters
----------
data
Data matrix or tensor. The axis must represent time, trials and assets respectively where the assets axis
is valid only if the data is a tensor.

time_unit: int or str
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, you could put in a string name of the time_unit. Accepted values are weekly,
monthly, quarterly, semi-annually and yearly

Returns
-------
float or ndarray
The annualized skew for the asset class or an array containing the annualized skew for each asset class.
"""
return np.mean([skew(i) for i in annualize_data(data, time_unit)], 0)

def annualize_data(data: np.ndarray, time_unit: Union[str, int]) -> np.ndarray:
"""
Annualizes the data. Note that the dimension of the data will change from this transformation

Parameters
----------
data
Data matrix or tensor. The axis must represent time, trials and assets respectively where the assets axis
is valid only if the data is a tensor.

time_unit: int or str
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, you could put in a string name of the time_unit. Accepted values are weekly,
monthly, quarterly, semi-annually and yearly

Returns
-------
ndarray
The annualized data.
"""

data = np.asarray(data)
time_unit = get_integer_time_unit(time_unit)

assert data.ndim in (2, 3), "data must be 2 or 3 dimensional"
if data.ndim == 2:
t, n = data.shape
return (data.reshape((t // time_unit, time_unit, n)) + 1).prod(1) - 1
else:
t, n, a = data.shape
return (data.reshape((t // time_unit, time_unit, n, a)) + 1).prod(1) - 1