Coverage for rivapy / tools / enums.py: 94%
414 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-27 14:36 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-27 14:36 +0000
1# -*- coding: utf-8 -*-
2from enum import Enum as _Enum, unique as _unique
3from dataclasses import dataclass
4from typing import List
7"""
9The following Enum sub-classes replace to corresponding former classes one-on-one. The main reason for this replacement
10is the more comfortable iterations over the enumeration class members. Moreover, the Enum class provides potentially
11useful functionalities like comparisons, pickling, ... Finally, the decorator @unique ensures unique enumeration values.
12"""
15class _MyEnum(_Enum):
16 @classmethod
17 def has_value(cls, value):
18 return value in cls._value2member_map_
20 @classmethod
21 def to_string(cls, value) -> str:
22 """Checks if given enum class contains the value and raises exception if not. If value is str
24 Args:
25 enum (_type_): _description_
26 value (_type_): _description_
28 Returns:
29 str: _description_
30 """
32 # Accept either the enum's stored value (e.g. 'Act360') or its name/key (e.g. 'ACT360')
33 # Matching is case-insensitive for robustness.
34 if isinstance(value, str):
35 v = value.strip()
36 # direct match against stored values (exact)
37 for member in cls:
38 if v == member.value:
39 return member.value
40 # exact name/key match
41 if v in cls.__members__:
42 return cls[v].value
43 # case-insensitive match against names or values
44 uv = v.upper()
45 for member in cls:
46 if uv == member.name.upper() or uv == str(member.value).upper():
47 return member.value
48 raise Exception("Unknown " + cls.__name__ + ": " + value)
49 if isinstance(value, cls):
50 return value.value
51 raise Exception("Given value " + str(value) + " does not belong to enum " + cls.__name__)
54class _MyIntEnum(_Enum):
55 @classmethod
56 def has_value(cls, value):
57 return value in cls._value2member_map_
59 @classmethod
60 def to_string(cls, value) -> str:
61 """Checks if given enum class contains the value and raises exception if not. If value is str
63 Args:
64 enum (_type_): _description_
65 value (_type_): _description_
67 Returns:
68 str: _description_
69 """
71 # Accept either the enum name (string) or integer value. Return the enum NAME as string.
72 if isinstance(value, str):
73 v = value.strip()
74 # direct name/key match
75 if v in cls.__members__:
76 return v
77 # case-insensitive name match
78 uv = v.upper()
79 for member in cls:
80 if uv == member.name.upper():
81 return member.name
82 raise Exception("Unknown " + cls.__name__ + ": " + value)
83 elif isinstance(value, int):
84 try:
85 return cls(value).name
86 except Exception:
87 raise Exception("Unknown " + cls.__name__ + ": " + str(value))
88 if isinstance(value, cls):
89 return value.name
90 raise Exception("Given value " + str(value) + " does not belong to enum " + cls.__name__)
93@_unique
94class InterpolationType(_MyEnum):
95 CONSTANT = "CONSTANT"
96 LINEAR = "LINEAR"
97 LINEAR_LOG = "LINEAR_LOG"
98 CONSTRAINED_SPLINE = "CONSTRAINED_SPLINE"
99 HAGAN = "HAGAN"
100 HAGAN_DF = "HAGAN_DF"
103@_unique
104class ExtrapolationType(_MyEnum):
105 NONE = "NONE"
106 CONSTANT = "CONSTANT"
107 CONSTANT_DF = "CONSTANT_DF"
108 LINEAR = "LINEAR"
109 LINEAR_LOG = "LINEAR_LOG"
112@_unique
113class SecuritizationLevel(_MyEnum):
114 NONE = "NONE"
115 COLLATERALIZED = "COLLATERALIZED"
116 SENIOR_SECURED = "SENIOR_SECURED"
117 SENIOR_UNSECURED = "SENIOR_UNSECURED"
118 SUBORDINATED = "SUBORDINATED"
119 MEZZANINE = "MEZZANINE"
120 EQUITY = "EQUITY"
121 PREFERRED_SENIOR = "PREFERRED_SENIOR"
122 NON_PREFERRED_SENIOR = "NON_PREFERRED_SENIOR"
125# class SecuritizationLevel:
126# NONE = 'NONE'
127# COLLATERALIZED = 'COLLATERALIZED' #,,,'','SUBORDINATED','MEZZANINE','EQUITY']
128# SENIOR_SECURED = 'SENIOR_SECURED'
129# SENIOR_UNSECURED = 'SENIOR_UNSECURED'
130# SUBORDINATED = 'SUBORDINATED'
131# MEZZANINE = 'MEZZANINE'
132# EQUITY = 'EQUITY'
134# @_unique
135# class ProductType(_MyEnum):
136# BOND = 'BOND'
137# CALLABLE_BOND = 'CALLABLE_BOND'
140# @_unique
141# class PricerType(_MyEnum):
142# ANALYTIC = 'ANALYTIC'
143# PDE = 'PDE'
144# MONTE_CARLO = 'MONTE_CARLO'
145# COMBO = 'COMBO'
148@_unique
149class EnergyTimeGridStructure(_MyEnum):
150 BASE = "BASE"
151 PEAK = "PEAK"
152 OFFPEAK = "OFFPEAK"
155@_unique
156class Model(_MyEnum):
157 BLACK76 = "BLACK76"
158 CIR = "CIR"
159 HULL_WHITE = "HULL_WHITE"
160 HESTON = "HESTON"
161 LV = "LV"
162 GBM = "GBM"
163 G2PP = "G2PP"
164 VASICEK = "VASICEK"
167# class Model:
168# BLACK76 = 'BLACK76'
169# CIR ='CIR'
170# HULL_WHITE = 'HULL_WHITE'
171# HESTON = 'HESTON'
172# LV = 'LV'
173# GBM = 'GBM'
174# G2PP = 'G2PP'
175# VASICEK = 'VASICEK'
178@_unique
179class Period(_MyEnum):
180 A = "A"
181 SA = "SA"
182 Q = "Q"
183 M = "M"
184 D = "D"
187# class Period:
188# A = 'A'
189# SA = 'SA'
190# Q = 'Q'
191# M = 'M'
192# D = 'D'
195@_unique
196class RollConvention(_MyEnum):
197 FOLLOWING = "Following"
198 MODIFIED_FOLLOWING = "ModifiedFollowing"
199 MODIFIED_FOLLOWING_EOM = "ModifiedFollowingEOM"
200 MODIFIED_FOLLOWING_BIMONTHLY = "ModifiedFollowingBimonthly"
201 PRECEDING = "Preceding"
202 MODIFIED_PRECEDING = "ModifiedPreceding"
203 NEAREST = "Nearest"
204 UNADJUSTED = "Unadjusted"
207@_unique
208class RollRule(_MyEnum):
209 """Roll Rules are used for calculating daten when building a schedule and, therefore, rolling forward (or backward) dates by periods or frequencies"""
211 NONE = "NONE" # no roll rule applied, day of a month drifts if adjustments are made acc. to bdc, i.e. the anchor date changes
212 EOM = "EOM" # rolls from month end to month end, ambiguous days are adjusted to the end of the month, i.e. Mar 30,
213 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,
214 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
217@_unique
218class DayCounterType(_MyEnum):
219 ACT_ACT = "ActAct"
220 Act365Fixed = "Act365Fixed"
221 ACT360 = "Act360"
222 ThirtyU360 = "30U360"
223 ThirtyE360 = "30E360"
224 ACT252 = "Act252"
225 Thirty360ISDA = "30360ISDA"
226 ActActICMA = "ActActICMA"
229@_unique
230class InflationInterpolation(_MyEnum):
231 UNDEFINED = "UNDEFINED"
232 GERMAN = "GERMAN"
233 JAPAN = "JAPAN"
234 CONSTANT = "CONSTANT"
237@_unique
238class Sector(_MyEnum):
239 UNDEFINED = "UNDEFINED"
240 # BASIC_MATERIALS = 'BasicMaterials'
241 CONGLOMERATES = "Conglomerates"
242 CONSUMER_GOODS = "ConsumerGoods"
243 # FINANCIAL = 'Financial'
244 # HEALTHCARE = 'Healthcare'
245 # INDUSTRIAL_GOODS = 'IndustrialGoods'
246 SERVICES = "Services"
247 # TECHNOLOGY = 'Technology'
248 # UTILITIES = 'Utilities'
250 COMMUNICATION_SERVICES = "CommunicationServices"
251 CONSUMER_STAPLES = "ConsumerStaples"
252 CONSUMER_DISCRETIONARY = "ConsumerDiscretionary"
253 ENERGY = "Energy"
254 FINANCIAL = "Financial"
255 HEALTH_CARE = "HealthCare"
256 INDUSTRIALS = "Industrials"
257 INFORMATION_TECHNOLOGY = "InformationTechnology"
258 MATERIALS = "Materials"
259 REAL_ESTATE = "RealEstate"
260 UTILITIES = "Utilities"
263@_unique
264class ESGRating(_MyEnum): # see MSCI ESG ratings
265 AAA = "AAA"
266 AA = "AA"
267 A = "A"
268 BBB = "BBB"
269 BB = "BB"
270 B = "B"
271 CCC = "CCC"
274@_unique
275class Rating(_MyEnum):
276 # cf. https://www.moneyland.ch/de/vergleich-rating-agenturen
277 AAA = "AAA"
278 AA_PLUS = "AA+"
279 AA = "AA"
280 AA_MINUS = "AA-"
281 A_PLUS = "A+"
282 A = "A"
283 A_MINUS = "A-"
284 BBB_PLUS = "BBB+"
285 BBB = "BBB"
286 BBB_MINUS = "BBB-"
287 BB_PLUS = "BB+"
288 BB = "BB"
289 BB_MINUS = "BB-"
290 B_PLUS = "B+"
291 B = "B"
292 B_MINUS = "B-"
293 CCC_PLUS = "CCC+"
294 CCC = "CCC"
295 CCC_MINUS = "CCC-"
296 CC = "CC"
297 C = "C"
298 D = "D"
299 NONE = "NONE" # not rated
302# class ProductType:
303# BOND = 'BOND'
304# CALLABLE_BOND = 'CALLABLE_BOND'
307class PricerType:
308 ANALYTIC = "ANALYTIC"
309 PDE = "PDE"
310 MONTE_CARLO = "MONTE_CARLO"
311 COMBO = "COMBO"
314@_unique
315class VolatilityStickyness(_MyEnum):
316 NONE = "NONE"
317 StickyStrike = "StickyStrike"
318 StickyXStrike = "StickyXStrike"
319 StickyFwdMoneyness = "StickyFwdMoneyness"
322@_unique
323class Currency(_MyEnum):
324 AED = "AED"
325 AFN = "AFN"
326 ALL = "ALL"
327 AMD = "AMD"
328 ANG = "ANG"
329 AOA = "AOA"
330 ARS = "ARS"
331 AUD = "AUD"
332 AWG = "AWG"
333 AZN = "AZN"
334 BAM = "BAM"
335 BBD = "BBD"
336 BDT = "BDT"
337 BGN = "BGN"
338 BHD = "BHD"
339 BIF = "BIF"
340 BMD = "BMD"
341 BND = "BND"
342 BOB = "BOB"
343 BRL = "BRL"
344 BSD = "BSD"
345 BTN = "BTN"
346 BWP = "BWP"
347 BYR = "BYR"
348 BZD = "BZD"
349 CAD = "CAD"
350 CDF = "CDF"
351 CHF = "CHF"
352 CLP = "CLP"
353 CNH = "CNH"
354 CNY = "CNY"
355 COP = "COP"
356 CRC = "CRC"
357 CUC = "CUC"
358 CUP = "CUP"
359 CVE = "CVE"
360 CZK = "CZK"
361 DJF = "DJF"
362 DKK = "DKK"
363 DOP = "DOP"
364 DZD = "DZD"
365 EGP = "EGP"
366 ERN = "ERN"
367 ETB = "ETB"
368 EUR = "EUR"
369 FJD = "FJD"
370 FKP = "FKP"
371 GBP = "GBP"
372 GEL = "GEL"
373 GGP = "GGP"
374 GHS = "GHS"
375 GIP = "GIP"
376 GMD = "GMD"
377 GNF = "GNF"
378 GTQ = "GTQ"
379 GYD = "GYD"
380 HKD = "HKD"
381 HNL = "HNL"
382 HRK = "HRK"
383 HTG = "HTG"
384 HUF = "HUF"
385 IDR = "IDR"
386 ILS = "ILS"
387 IMP = "IMP"
388 INR = "INR"
389 IQD = "IQD"
390 IRR = "IRR"
391 ISK = "ISK"
392 JEP = "JEP"
393 JMD = "JMD"
394 JOD = "JOD"
395 JPY = "JPY"
396 KES = "KES"
397 KGS = "KGS"
398 KHR = "KHR"
399 KMF = "KMF"
400 KPW = "KPW"
401 KRW = "KRW"
402 KWD = "KWD"
403 KYD = "KYD"
404 KZT = "KZT"
405 LAK = "LAK"
406 LBP = "LBP"
407 LKR = "LKR"
408 LRD = "LRD"
409 LSL = "LSL"
410 LTL = "LTL"
411 LVL = "LVL"
412 LYD = "LYD"
413 MAD = "MAD"
414 MDL = "MDL"
415 MGA = "MGA"
416 MKD = "MKD"
417 MMK = "MMK"
418 MNT = "MNT"
419 MOP = "MOP"
420 MRO = "MRO"
421 MUR = "MUR"
422 MVR = "MVR"
423 MWK = "MWK"
424 MXN = "MXN"
425 MYR = "MYR"
426 MZN = "MZN"
427 NAD = "NAD"
428 NGN = "NGN"
429 NIO = "NIO"
430 NOK = "NOK"
431 NPR = "NPR"
432 NZD = "NZD"
433 OMR = "OMR"
434 PAB = "PAB"
435 PEN = "PEN"
436 PGK = "PGK"
437 PHP = "PHP"
438 PKR = "PKR"
439 PLN = "PLN"
440 PYG = "PYG"
441 QAR = "QAR"
442 RON = "RON"
443 RSD = "RSD"
444 RUB = "RUB"
445 RWF = "RWF"
446 SAR = "SAR"
447 SBD = "SBD"
448 SCR = "SCR"
449 SDG = "SDG"
450 SEK = "SEK"
451 SGD = "SGD"
452 SHP = "SHP"
453 SLL = "SLL"
454 SOS = "SOS"
455 SPL = "SPL"
456 SRD = "SRD"
457 STD = "STD"
458 SVC = "SVC"
459 SYP = "SYP"
460 SZL = "SZL"
461 THB = "THB"
462 TJS = "TJS"
463 TMT = "TMT"
464 TND = "TND"
465 TOP = "TOP"
466 TRY = "TRY"
467 TTD = "TTD"
468 TVD = "TVD"
469 TWD = "TWD"
470 TZS = "TZS"
471 UAH = "UAH"
472 UGX = "UGX"
473 USD = "USD"
474 UYU = "UYU"
475 UZS = "UZS"
476 VEF = "VEF"
477 VND = "VND"
478 VUV = "VUV"
479 WST = "WST"
480 XAF = "XAF"
481 XAG = "XAG"
482 XAU = "XAU"
483 XPD = "XPD"
484 XPT = "XPT"
485 XCD = "XCD"
486 XDR = "XDR"
487 XOF = "XOF"
488 XPF = "XPF"
489 YER = "YER"
490 ZAR = "ZAR"
491 ZMW = "ZMW"
492 ZWD = "ZWD"
495class Country(_MyEnum):
496 DE = "DE"
497 FR = "FR"
498 CA = "CA"
499 US = "US"
500 GB = "GB"
501 JP = "JP"
502 CN = "CN"
505class IrLegType(_MyEnum):
506 """Enums object for the type of interest rate swap legs."""
508 FIXED = "FIXED"
509 FLOAT = "FLOAT"
510 OIS = "OIS"
512 @staticmethod
513 def from_string(s: str) -> str:
514 s = s.upper()
515 if s in (IrLegType.FIXED, IrLegType.FLOAT, IrLegType.OIS):
516 return s
517 raise ValueError(f"Unknown leg type '{s}'")
519 # @staticmethod
520 # def to_string(cls, value: str) -> str:
521 # return value.upper()
524class Instrument(_MyEnum):
525 """Enums object for the type of instrument."""
527 IRS = "IRS"
528 TBS = "TBS"
529 BS = "BS"
530 DEPOSIT = "DEPOSIT"
531 OIS = "OIS"
532 FRA = "FRA"
533 FXF = "FXF"
536@dataclass(frozen=True)
537class IRIndexMetadata:
538 name: str
539 currency: str
540 tenor: str
541 spot_days: int
542 business_day_convention: str
543 day_count_convention: str
544 roll_convention: str
545 calendar: str
546 aliases: List[str]
549class InterestRateIndex(_MyEnum):
550 EUR1M = IRIndexMetadata(
551 name="EURIBOR 1M",
552 currency="EUR",
553 tenor="1M",
554 spot_days=2,
555 business_day_convention="ModifiedFollowing",
556 day_count_convention="ACT360",
557 roll_convention="EOM",
558 calendar="TARGET",
559 aliases=["EUR1M", " EUR 1M", "EURIBOR 1M"],
560 )
561 EUR3M = IRIndexMetadata(
562 name="EURIBOR 3M",
563 currency="EUR",
564 tenor="3M",
565 spot_days=2,
566 business_day_convention="ModifiedFollowing",
567 day_count_convention="ACT360",
568 roll_convention="EOM",
569 calendar="TARGET",
570 aliases=["EUR3M", " EUR 3M", "EURIBOR 3M", "EURIBOR_3M"],
571 )
572 EUR6M = IRIndexMetadata(
573 name="EURIBOR 6M",
574 currency="EUR",
575 tenor="6M",
576 spot_days=2,
577 business_day_convention="ModifiedFollowing",
578 day_count_convention="ACT360",
579 roll_convention="EOM",
580 calendar="TARGET",
581 aliases=["EUR6M", "EUR 6M", "EURIBOR 6M"],
582 )
583 EUR12M = IRIndexMetadata(
584 name="EURIBOR 12M",
585 currency="EUR",
586 tenor="12M",
587 spot_days=2,
588 business_day_convention="ModifiedFollowing",
589 day_count_convention="ACT360",
590 roll_convention="EOM",
591 calendar="TARGET",
592 aliases=["EUR12M", "EUR 12M", "EURIBOR 12M", "EUR1Y", "EUR_1Y", "EURIBOR_1Y"],
593 )
594 ESTR = IRIndexMetadata(
595 name="€STR",
596 currency="EUR",
597 tenor="O/N",
598 spot_days=0,
599 business_day_convention="Following",
600 day_count_convention="ACT360",
601 roll_convention="None",
602 calendar="TARGET",
603 aliases=["EURSTR", "EUR STR", "€STR", "EUR1D", "EUR O/N"],
604 )
607def get_index_by_alias(alias: str) -> InterestRateIndex:
608 alias = alias.strip().upper()
609 for index in InterestRateIndex:
610 value = index.value
611 aliases = [a.upper() for a in value.aliases]
612 if alias in aliases or alias == index.name.upper():
613 print("erfolgreich")
614 str = index.value.name
615 return index
616 raise ValueError(f"Unknown index alias: {alias}")