# -*- coding: utf-8 -*-
from enum import Enum as _Enum, unique as _unique
from dataclasses import dataclass
from typing import List
"""
The following Enum sub-classes replace to corresponding former classes one-on-one. The main reason for this replacement
is the more comfortable iterations over the enumeration class members. Moreover, the Enum class provides potentially
useful functionalities like comparisons, pickling, ... Finally, the decorator @unique ensures unique enumeration values.
"""
class _MyEnum(_Enum):
@classmethod
def has_value(cls, value):
return value in cls._value2member_map_
@classmethod
def to_string(cls, value) -> str:
"""Checks if given enum class contains the value and raises exception if not. If value is str
Args:
enum (_type_): _description_
value (_type_): _description_
Returns:
str: _description_
"""
# Accept either the enum's stored value (e.g. 'Act360') or its name/key (e.g. 'ACT360')
# Matching is case-insensitive for robustness.
if isinstance(value, str):
v = value.strip()
# direct match against stored values (exact)
for member in cls:
if v == member.value:
return member.value
# exact name/key match
if v in cls.__members__:
return cls[v].value
# case-insensitive match against names or values
uv = v.upper()
for member in cls:
if uv == member.name.upper() or uv == str(member.value).upper():
return member.value
raise Exception("Unknown " + cls.__name__ + ": " + value)
if isinstance(value, cls):
return value.value
raise Exception("Given value " + str(value) + " does not belong to enum " + cls.__name__)
class _MyIntEnum(_Enum):
@classmethod
def has_value(cls, value):
return value in cls._value2member_map_
@classmethod
def to_string(cls, value) -> str:
"""Checks if given enum class contains the value and raises exception if not. If value is str
Args:
enum (_type_): _description_
value (_type_): _description_
Returns:
str: _description_
"""
# Accept either the enum name (string) or integer value. Return the enum NAME as string.
if isinstance(value, str):
v = value.strip()
# direct name/key match
if v in cls.__members__:
return v
# case-insensitive name match
uv = v.upper()
for member in cls:
if uv == member.name.upper():
return member.name
raise Exception("Unknown " + cls.__name__ + ": " + value)
elif isinstance(value, int):
try:
return cls(value).name
except Exception:
raise Exception("Unknown " + cls.__name__ + ": " + str(value))
if isinstance(value, cls):
return value.name
raise Exception("Given value " + str(value) + " does not belong to enum " + cls.__name__)
[docs]
@_unique
class InterpolationType(_MyEnum):
CONSTANT = "CONSTANT"
LINEAR = "LINEAR"
LINEAR_LOG = "LINEAR_LOG"
CONSTRAINED_SPLINE = "CONSTRAINED_SPLINE"
HAGAN = "HAGAN"
HAGAN_DF = "HAGAN_DF"
[docs]
@_unique
class SecuritizationLevel(_MyEnum):
NONE = "NONE"
COLLATERALIZED = "COLLATERALIZED"
SENIOR_SECURED = "SENIOR_SECURED"
SENIOR_UNSECURED = "SENIOR_UNSECURED"
SUBORDINATED = "SUBORDINATED"
MEZZANINE = "MEZZANINE"
EQUITY = "EQUITY"
PREFERRED_SENIOR = "PREFERRED_SENIOR"
NON_PREFERRED_SENIOR = "NON_PREFERRED_SENIOR"
# class SecuritizationLevel:
# NONE = 'NONE'
# COLLATERALIZED = 'COLLATERALIZED' #,,,'','SUBORDINATED','MEZZANINE','EQUITY']
# SENIOR_SECURED = 'SENIOR_SECURED'
# SENIOR_UNSECURED = 'SENIOR_UNSECURED'
# SUBORDINATED = 'SUBORDINATED'
# MEZZANINE = 'MEZZANINE'
# EQUITY = 'EQUITY'
# @_unique
# class ProductType(_MyEnum):
# BOND = 'BOND'
# CALLABLE_BOND = 'CALLABLE_BOND'
# @_unique
# class PricerType(_MyEnum):
# ANALYTIC = 'ANALYTIC'
# PDE = 'PDE'
# MONTE_CARLO = 'MONTE_CARLO'
# COMBO = 'COMBO'
@_unique
class EnergyTimeGridStructure(_MyEnum):
BASE = "BASE"
PEAK = "PEAK"
OFFPEAK = "OFFPEAK"
@_unique
class Model(_MyEnum):
BLACK76 = "BLACK76"
CIR = "CIR"
HULL_WHITE = "HULL_WHITE"
HESTON = "HESTON"
LV = "LV"
GBM = "GBM"
G2PP = "G2PP"
VASICEK = "VASICEK"
# class Model:
# BLACK76 = 'BLACK76'
# CIR ='CIR'
# HULL_WHITE = 'HULL_WHITE'
# HESTON = 'HESTON'
# LV = 'LV'
# GBM = 'GBM'
# G2PP = 'G2PP'
# VASICEK = 'VASICEK'
@_unique
class Period(_MyEnum):
A = "A"
SA = "SA"
Q = "Q"
M = "M"
D = "D"
# class Period:
# A = 'A'
# SA = 'SA'
# Q = 'Q'
# M = 'M'
# D = 'D'
[docs]
@_unique
class RollConvention(_MyEnum):
FOLLOWING = "Following"
MODIFIED_FOLLOWING = "ModifiedFollowing"
MODIFIED_FOLLOWING_EOM = "ModifiedFollowingEOM"
MODIFIED_FOLLOWING_BIMONTHLY = "ModifiedFollowingBimonthly"
PRECEDING = "Preceding"
MODIFIED_PRECEDING = "ModifiedPreceding"
NEAREST = "Nearest"
UNADJUSTED = "Unadjusted"
@_unique
class RollRule(_MyEnum):
"""Roll Rules are used for calculating daten when building a schedule and, therefore, rolling forward (or backward) dates by periods or frequencies"""
NONE = "NONE" # no roll rule applied, day of a month drifts if adjustments are made acc. to bdc, i.e. the anchor date changes
EOM = "EOM" # rolls from month end to month end, ambiguous days are adjusted to the end of the month, i.e. Mar 30,
DOM = "DOM" # rolls from a specific day of month to the same day of month, ambiguous days are adjusted to the same day of month, i.e. Mar 30,
IMM = "IMM" # rolls to the third Wednesday of the month, i.e. Mar 30, rolls to Mar 20, if Mar 20 is a weekend, it rolls to Mar 22
[docs]
@_unique
class DayCounterType(_MyEnum):
ACT_ACT = "ActAct"
Act365Fixed = "Act365Fixed"
ACT360 = "Act360"
ThirtyU360 = "30U360"
ThirtyE360 = "30E360"
ACT252 = "Act252"
Thirty360ISDA = "30360ISDA"
ActActICMA = "ActActICMA"
[docs]
@_unique
class InflationInterpolation(_MyEnum):
UNDEFINED = "UNDEFINED"
GERMAN = "GERMAN"
JAPAN = "JAPAN"
CONSTANT = "CONSTANT"
@_unique
class Sector(_MyEnum):
UNDEFINED = "UNDEFINED"
# BASIC_MATERIALS = 'BasicMaterials'
CONGLOMERATES = "Conglomerates"
CONSUMER_GOODS = "ConsumerGoods"
# FINANCIAL = 'Financial'
# HEALTHCARE = 'Healthcare'
# INDUSTRIAL_GOODS = 'IndustrialGoods'
SERVICES = "Services"
# TECHNOLOGY = 'Technology'
# UTILITIES = 'Utilities'
COMMUNICATION_SERVICES = "CommunicationServices"
CONSUMER_STAPLES = "ConsumerStaples"
CONSUMER_DISCRETIONARY = "ConsumerDiscretionary"
ENERGY = "Energy"
FINANCIAL = "Financial"
HEALTH_CARE = "HealthCare"
INDUSTRIALS = "Industrials"
INFORMATION_TECHNOLOGY = "InformationTechnology"
MATERIALS = "Materials"
REAL_ESTATE = "RealEstate"
UTILITIES = "Utilities"
@_unique
class ESGRating(_MyEnum): # see MSCI ESG ratings
AAA = "AAA"
AA = "AA"
A = "A"
BBB = "BBB"
BB = "BB"
B = "B"
CCC = "CCC"
@_unique
class Rating(_MyEnum):
# cf. https://www.moneyland.ch/de/vergleich-rating-agenturen
AAA = "AAA"
AA_PLUS = "AA+"
AA = "AA"
AA_MINUS = "AA-"
A_PLUS = "A+"
A = "A"
A_MINUS = "A-"
BBB_PLUS = "BBB+"
BBB = "BBB"
BBB_MINUS = "BBB-"
BB_PLUS = "BB+"
BB = "BB"
BB_MINUS = "BB-"
B_PLUS = "B+"
B = "B"
B_MINUS = "B-"
CCC_PLUS = "CCC+"
CCC = "CCC"
CCC_MINUS = "CCC-"
CC = "CC"
C = "C"
D = "D"
NONE = "NONE" # not rated
# class ProductType:
# BOND = 'BOND'
# CALLABLE_BOND = 'CALLABLE_BOND'
class PricerType:
ANALYTIC = "ANALYTIC"
PDE = "PDE"
MONTE_CARLO = "MONTE_CARLO"
COMBO = "COMBO"
[docs]
@_unique
class VolatilityStickyness(_MyEnum):
NONE = "NONE"
StickyStrike = "StickyStrike"
StickyXStrike = "StickyXStrike"
StickyFwdMoneyness = "StickyFwdMoneyness"
@_unique
class Currency(_MyEnum):
AED = "AED"
AFN = "AFN"
ALL = "ALL"
AMD = "AMD"
ANG = "ANG"
AOA = "AOA"
ARS = "ARS"
AUD = "AUD"
AWG = "AWG"
AZN = "AZN"
BAM = "BAM"
BBD = "BBD"
BDT = "BDT"
BGN = "BGN"
BHD = "BHD"
BIF = "BIF"
BMD = "BMD"
BND = "BND"
BOB = "BOB"
BRL = "BRL"
BSD = "BSD"
BTN = "BTN"
BWP = "BWP"
BYR = "BYR"
BZD = "BZD"
CAD = "CAD"
CDF = "CDF"
CHF = "CHF"
CLP = "CLP"
CNH = "CNH"
CNY = "CNY"
COP = "COP"
CRC = "CRC"
CUC = "CUC"
CUP = "CUP"
CVE = "CVE"
CZK = "CZK"
DJF = "DJF"
DKK = "DKK"
DOP = "DOP"
DZD = "DZD"
EGP = "EGP"
ERN = "ERN"
ETB = "ETB"
EUR = "EUR"
FJD = "FJD"
FKP = "FKP"
GBP = "GBP"
GEL = "GEL"
GGP = "GGP"
GHS = "GHS"
GIP = "GIP"
GMD = "GMD"
GNF = "GNF"
GTQ = "GTQ"
GYD = "GYD"
HKD = "HKD"
HNL = "HNL"
HRK = "HRK"
HTG = "HTG"
HUF = "HUF"
IDR = "IDR"
ILS = "ILS"
IMP = "IMP"
INR = "INR"
IQD = "IQD"
IRR = "IRR"
ISK = "ISK"
JEP = "JEP"
JMD = "JMD"
JOD = "JOD"
JPY = "JPY"
KES = "KES"
KGS = "KGS"
KHR = "KHR"
KMF = "KMF"
KPW = "KPW"
KRW = "KRW"
KWD = "KWD"
KYD = "KYD"
KZT = "KZT"
LAK = "LAK"
LBP = "LBP"
LKR = "LKR"
LRD = "LRD"
LSL = "LSL"
LTL = "LTL"
LVL = "LVL"
LYD = "LYD"
MAD = "MAD"
MDL = "MDL"
MGA = "MGA"
MKD = "MKD"
MMK = "MMK"
MNT = "MNT"
MOP = "MOP"
MRO = "MRO"
MUR = "MUR"
MVR = "MVR"
MWK = "MWK"
MXN = "MXN"
MYR = "MYR"
MZN = "MZN"
NAD = "NAD"
NGN = "NGN"
NIO = "NIO"
NOK = "NOK"
NPR = "NPR"
NZD = "NZD"
OMR = "OMR"
PAB = "PAB"
PEN = "PEN"
PGK = "PGK"
PHP = "PHP"
PKR = "PKR"
PLN = "PLN"
PYG = "PYG"
QAR = "QAR"
RON = "RON"
RSD = "RSD"
RUB = "RUB"
RWF = "RWF"
SAR = "SAR"
SBD = "SBD"
SCR = "SCR"
SDG = "SDG"
SEK = "SEK"
SGD = "SGD"
SHP = "SHP"
SLL = "SLL"
SOS = "SOS"
SPL = "SPL"
SRD = "SRD"
STD = "STD"
SVC = "SVC"
SYP = "SYP"
SZL = "SZL"
THB = "THB"
TJS = "TJS"
TMT = "TMT"
TND = "TND"
TOP = "TOP"
TRY = "TRY"
TTD = "TTD"
TVD = "TVD"
TWD = "TWD"
TZS = "TZS"
UAH = "UAH"
UGX = "UGX"
USD = "USD"
UYU = "UYU"
UZS = "UZS"
VEF = "VEF"
VND = "VND"
VUV = "VUV"
WST = "WST"
XAF = "XAF"
XAG = "XAG"
XAU = "XAU"
XPD = "XPD"
XPT = "XPT"
XCD = "XCD"
XDR = "XDR"
XOF = "XOF"
XPF = "XPF"
YER = "YER"
ZAR = "ZAR"
ZMW = "ZMW"
ZWD = "ZWD"
class Country(_MyEnum):
DE = "DE"
FR = "FR"
CA = "CA"
US = "US"
GB = "GB"
JP = "JP"
CN = "CN"
class IrLegType(_MyEnum):
"""Enums object for the type of interest rate swap legs."""
FIXED = "FIXED"
FLOAT = "FLOAT"
OIS = "OIS"
@staticmethod
def from_string(s: str) -> str:
s = s.upper()
if s in (IrLegType.FIXED, IrLegType.FLOAT, IrLegType.OIS):
return s
raise ValueError(f"Unknown leg type '{s}'")
# @staticmethod
# def to_string(cls, value: str) -> str:
# return value.upper()
class Instrument(_MyEnum):
"""Enums object for the type of instrument."""
IRS = "IRS"
TBS = "TBS"
BS = "BS"
DEPOSIT = "DEPOSIT"
OIS = "OIS"
FRA = "FRA"
FXF = "FXF"
@dataclass(frozen=True)
class IRIndexMetadata:
name: str
currency: str
tenor: str
spot_days: int
business_day_convention: str
day_count_convention: str
roll_convention: str
calendar: str
aliases: List[str]
class InterestRateIndex(_MyEnum):
EUR1M = IRIndexMetadata(
name="EURIBOR 1M",
currency="EUR",
tenor="1M",
spot_days=2,
business_day_convention="ModifiedFollowing",
day_count_convention="ACT360",
roll_convention="EOM",
calendar="TARGET",
aliases=["EUR1M", " EUR 1M", "EURIBOR 1M"],
)
EUR3M = IRIndexMetadata(
name="EURIBOR 3M",
currency="EUR",
tenor="3M",
spot_days=2,
business_day_convention="ModifiedFollowing",
day_count_convention="ACT360",
roll_convention="EOM",
calendar="TARGET",
aliases=["EUR3M", " EUR 3M", "EURIBOR 3M", "EURIBOR_3M"],
)
EUR6M = IRIndexMetadata(
name="EURIBOR 6M",
currency="EUR",
tenor="6M",
spot_days=2,
business_day_convention="ModifiedFollowing",
day_count_convention="ACT360",
roll_convention="EOM",
calendar="TARGET",
aliases=["EUR6M", "EUR 6M", "EURIBOR 6M"],
)
EUR12M = IRIndexMetadata(
name="EURIBOR 12M",
currency="EUR",
tenor="12M",
spot_days=2,
business_day_convention="ModifiedFollowing",
day_count_convention="ACT360",
roll_convention="EOM",
calendar="TARGET",
aliases=["EUR12M", "EUR 12M", "EURIBOR 12M", "EUR1Y", "EUR_1Y", "EURIBOR_1Y"],
)
ESTR = IRIndexMetadata(
name="€STR",
currency="EUR",
tenor="O/N",
spot_days=0,
business_day_convention="Following",
day_count_convention="ACT360",
roll_convention="None",
calendar="TARGET",
aliases=["EURSTR", "EUR STR", "€STR", "EUR1D", "EUR O/N"],
)
def get_index_by_alias(alias: str) -> InterestRateIndex:
alias = alias.strip().upper()
for index in InterestRateIndex:
value = index.value
aliases = [a.upper() for a in value.aliases]
if alias in aliases or alias == index.name.upper():
print("erfolgreich")
str = index.value.name
return index
raise ValueError(f"Unknown index alias: {alias}")