Source code for openseries.owntypes

"""Declaring types used throughout the project."""

from __future__ import annotations

import datetime as dt
from enum import StrEnum
from pprint import pformat
from typing import (
    TYPE_CHECKING,
    Annotated,
    ClassVar,
    Literal,
    Self,
    TypeAlias,
    TypeVar,
)

from annotated_types import MinLen
from numpy import datetime64
from pandas import Series, Timestamp
from pydantic import BaseModel, Field, StringConstraints

if TYPE_CHECKING:
    from pandas import Series as _Series

    SeriesFloat = _Series[float]
else:
    SeriesFloat = Series

__all__ = ["ValueType"]


SeriesOrFloat_co = TypeVar("SeriesOrFloat_co", float, SeriesFloat, covariant=True)


CountryStringType = Annotated[
    str,
    StringConstraints(
        strip_whitespace=True,
        pattern=r"^[A-Z]{2}$",
        to_upper=True,
        min_length=2,
        max_length=2,
        strict=True,
    ),
]
CountrySetType: TypeAlias = Annotated[set[CountryStringType], MinLen(1)]
CountriesType: TypeAlias = CountrySetType | CountryStringType


[docs] class Countries(BaseModel): """Declare Countries.""" countryinput: CountriesType
CurrencyStringType = Annotated[ str, StringConstraints( pattern=r"^[A-Z]{3}$", to_upper=True, min_length=3, max_length=3, strict=True, strip_whitespace=True, ), ]
[docs] class Currency(BaseModel): """Declare Currency.""" ccy: CurrencyStringType
DateStringType = Annotated[ str, StringConstraints( pattern=r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$", strip_whitespace=True, strict=True, min_length=10, max_length=10, ), ] DateListType: TypeAlias = Annotated[list[DateStringType], MinLen(1)] ValueListType: TypeAlias = Annotated[list[float], MinLen(1)] DaysInYearType = Annotated[int, Field(strict=True, ge=1, le=366)] DateType = str | dt.date | dt.datetime | datetime64 | Timestamp PlotlyConfigType = ( str | int | float | bool | list[str] | dict[str, str | int | float | bool | list[str]] ) PlotlyLayoutType = dict[str, PlotlyConfigType] CaptorLogoType = dict[str, str | float] LiteralJsonOutput = Literal["values", "tsdf"] LiteralTrunc = Literal["before", "after", "both"] LiteralLinePlotMode = ( Literal[ "lines", "markers", "lines+markers", "lines+text", "markers+text", "lines+markers+text", ] | None ) LiteralHowMerge = Literal["outer", "inner"] LiteralQuantileInterp = Literal["linear", "lower", "higher", "midpoint", "nearest"] LiteralBizDayFreq = Literal["B", "BME", "BQE", "BYE"] LiteralPandasReindexMethod = ( Literal["pad", "ffill", "backfill", "bfill", "nearest"] | None ) LiteralNanMethod = Literal["fill", "drop"] LiteralCaptureRatio = Literal["up", "down", "both"] LiteralBarPlotMode = Literal["stack", "group", "overlay", "relative"] LiteralPlotlyOutput = Literal["file", "div"] LiteralPlotlyJSlib = Literal[True, False, "cdn"] LiteralPlotlyHistogramPlotType = Literal["bars", "lines"] LiteralPlotlyHistogramBarMode = Literal["stack", "group", "overlay", "relative"] LiteralPlotlyHistogramCurveType = Literal["normal", "kde"] LiteralPlotlyHistogramHistNorm = Literal[ "percent", "probability", "density", "probability density", ] LiteralPortfolioWeightings = Literal[ "eq_weights", "inv_vol", "max_div", "min_vol_overweight" ] LiteralMinimizeMethods = Literal[ "SLSQP", "Nelder-Mead", "Powell", "CG", "BFGS", "Newton-CG", "L-BFGS-B", "TNC", "COBYLA", "trust-constr", "dogleg", "trust-ncg", "trust-exact", "trust-krylov", ] LiteralSeriesProps = Literal[ "value_ret", "geo_ret", "arithmetic_ret", "vol", "downside_deviation", "ret_vol_ratio", "sortino_ratio", "kappa3_ratio", "z_score", "skew", "kurtosis", "positive_share", "var_down", "cvar_down", "vol_from_var", "worst", "worst_month", "max_drawdown_cal_year", "max_drawdown", "max_drawdown_date", "first_idx", "last_idx", "length", "span_of_days", "yearfrac", "periods_in_a_year", "autocorr", "partial_autocorr", ] LiteralFrameProps = Literal[ "value_ret", "geo_ret", "arithmetic_ret", "autocorr", "vol", "downside_deviation", "ret_vol_ratio", "sortino_ratio", "kappa3_ratio", "z_score", "skew", "kurtosis", "positive_share", "var_down", "cvar_down", "vol_from_var", "worst", "worst_month", "max_drawdown", "max_drawdown_date", "max_drawdown_cal_year", "first_indices", "last_indices", "lengths_of_items", "span_of_days_all", ]
[docs] class PropertiesList(list[str]): """Base class for allowed property arguments definition.""" allowed_strings: ClassVar[set[str]] = { "value_ret", "geo_ret", "arithmetic_ret", "vol", "downside_deviation", "ret_vol_ratio", "sortino_ratio", "kappa3_ratio", "omega_ratio", "z_score", "skew", "kurtosis", "positive_share", "var_down", "cvar_down", "vol_from_var", "worst", "worst_month", "max_drawdown", "max_drawdown_date", "max_drawdown_cal_year", } def _validate(self: Self) -> None: """Validate the string input of the all_properties method.""" seen = set() invalids = set() duplicates = set() msg = "" for item in self: if item not in self.allowed_strings: invalids.add(item) if item in seen: duplicates.add(item) seen.add(item) if len(invalids) != 0: msg += ( f"Invalid string(s): {list(invalids)}.\nAllowed strings are:" f"\n{pformat(self.allowed_strings)}\n" ) if len(duplicates) != 0: msg += f"Duplicate string(s): {list(duplicates)}." if len(msg) != 0: raise PropertiesInputValidationError(msg)
[docs] class OpenTimeSeriesPropertiesList(PropertiesList): """Allowed property arguments for the OpenTimeSeries class.""" allowed_strings: ClassVar[set[str]] = PropertiesList.allowed_strings | { "first_idx", "last_idx", "length", "span_of_days", "yearfrac", "periods_in_a_year", "autocorr", "partial_autocorr", }
[docs] def __init__( self: Self, *args: LiteralSeriesProps, ) -> None: """Property arguments for the OpenTimeSeries class.""" super().__init__(args) self._validate()
[docs] class OpenFramePropertiesList(PropertiesList): """Allowed property arguments for the OpenFrame class.""" allowed_strings: ClassVar[set[str]] = PropertiesList.allowed_strings | { "autocorr", "first_indices", "last_indices", "lengths_of_items", "span_of_days_all", }
[docs] def __init__(self: Self, *args: LiteralFrameProps) -> None: """Property arguments for the OpenFrame class.""" super().__init__(args) self._validate()
[docs] class ValueType(StrEnum): """Enum types of OpenTimeSeries to identify the output.""" EWMA_VOL = "EWMA volatility" EWMA_VAR = "EWMA VaR" PRICE = "Price(Close)" RTRN = "Return(Total)" RELRTRN = "Relative return" ROLLBETA = "Beta" ROLLCORR = "Rolling correlation" ROLLCVAR = "Rolling CVaR" ROLLINFORATIO = "Information Ratio" ROLLRTRN = "Rolling returns" ROLLVAR = "Rolling VaR" ROLLVOL = "Rolling volatility"
[docs] class MixedValuetypesError(Exception): """Raised when provided timeseries valuetypes are not the same."""
[docs] class AtLeastOneFrameError(Exception): """Raised when none of the possible frame inputs is provided."""
[docs] class DateAlignmentError(Exception): """Raised when date input is not aligned with existing range."""
[docs] class NumberOfItemsAndLabelsNotSameError(Exception): """Raised when number of labels is not matching the number of timeseries."""
[docs] class InitialValueZeroError(Exception): """Raised when a calculation cannot be performed due to initial value(s) zero."""
[docs] class CountriesNotStringNorListStrError(Exception): """Raised when countries argument is not provided in correct format."""
[docs] class MarketsNotStringNorListStrError(Exception): """Raised when markets argument is not provided in correct format."""
[docs] class TradingDaysNotAboveZeroError(Exception): """Raised when trading days argument is not above zero."""
[docs] class BothStartAndEndError(Exception): """Raised when both start and end dates are provided."""
[docs] class NoWeightsError(Exception): """Raised when no weights are provided to function where necessary."""
[docs] class LabelsNotUniqueError(Exception): """Raised when provided label names are not unique."""
[docs] class RatioInputError(Exception): """Raised when ratio keyword not provided correctly."""
[docs] class MergingResultedInEmptyError(Exception): """Raised when a merge resulted in an empty DataFrame."""
[docs] class IncorrectArgumentComboError(Exception): """Raised when correct combination of arguments is not provided."""
[docs] class PropertiesInputValidationError(Exception): """Raised when duplicate strings are provided."""
[docs] class ResampleDataLossError(Exception): """Raised when user attempts to run resample_to_business_period_ends on returns."""
class WeightsNotProvidedError(Exception): """Raised when weights are not provided.""" class MultipleCurrenciesError(Exception): """Raised when multiple currencies are provided.""" class PortfolioItemsNotWithinFrameError(Exception): """Raised when portfolio items are not within frame.""" class MaxDiversificationNaNError(Exception): """Raised when max_div weight strategy produces NaN values.""" class MaxDiversificationNegativeWeightsError(Exception): """Raised when max_div weight strategy produces negative weights."""