Source code for ace.core.definitions

from enum import Enum
from ace.core.basic_functions import classic_round


[docs] class StopOption(Enum): FIXED_NUMBER_YEAR = 1 AGE_BASED_TERM = 2 TERM_UNTIL_MATURITY = 3
[docs] class Gender(Enum): MALE = 1 FEMALE = 2
[docs] @staticmethod def from_char(value: str) -> 'Gender': match value: case "M": return Gender.MALE case "F": return Gender.FEMALE case _: raise ValueError(f"Gender {value} does not exists")
[docs] class UnderwritingClass(Enum): STANDARD = 1 PREFERRED = 2 STANDARD_PLUS = 6 PREFERRED_PLUS = 19
[docs] @staticmethod def from_string(value: str) -> 'UnderwritingClass': match value.upper(): case "STANDARD": return UnderwritingClass.STANDARD case "PREFERRED": return UnderwritingClass.PREFERRED case "STANDARD_PLUS": return UnderwritingClass.STANDARD_PLUS case "PREFERRED_PLUS" | "OPTIMUM": return UnderwritingClass.PREFERRED_PLUS case _: raise ValueError(f"UnderwritingClass {value} does not exists")
[docs] class TobaccoPremiumBasis(Enum): NON_SMOKER = 1 SMOKER = 2
[docs] def is_smoking(self): return self == TobaccoPremiumBasis.SMOKER
[docs] @staticmethod def from_string(value: str) -> 'TobaccoPremiumBasis': match value.upper(): case "NS": return TobaccoPremiumBasis.NON_SMOKER case "SM": return TobaccoPremiumBasis.SMOKER case _: raise ValueError(f"TobaccoPremiumBasis {value} does not exists")
[docs] class CoverageType(Enum): # The order of the coverage type here is the order they will be processed BASE_COVERAGE = 4 ADDITIONAL_TERM_INSURANCE = 8 CHILD_TERM_RIDER = 22 ACCIDENTAL_DEATH_BENEFIT = 34 TOTAL_DISABILITY_WAIVER = 65 PAYOR_DEATH_AND_DISABILITY_WAIVER = 13 OWNER_WAIVER_DEATH = 48 OWNER_WAIVER_DISABILITY = 49 OWNER_WAIVER_DEATH_AND_DISABILITY = 50
[docs] def get_order_index(self) -> int: return coverage_type_order_dict[self]
[docs] def is_rider(self) -> bool: return self != CoverageType.BASE_COVERAGE
[docs] def is_waiver(self) -> bool: return self in [ CoverageType.TOTAL_DISABILITY_WAIVER, CoverageType.PAYOR_DEATH_AND_DISABILITY_WAIVER, CoverageType.OWNER_WAIVER_DEATH, CoverageType.OWNER_WAIVER_DISABILITY, CoverageType.OWNER_WAIVER_DEATH_AND_DISABILITY, ]
coverage_type_order_dict = {coverageType: index for index, coverageType in enumerate(list(CoverageType))}
[docs] class AgeCalculationType(Enum): AGE_NEXT_BIRTHDAY = 1 AGE_LAST_BIRTHDAY = 2 AGE_NEAREST_IN_MONTHS = 4
[docs] class ProductType(Enum): TERM = 0
[docs] class ProductSubType(Enum): TERM_SIT = 0, TERM100 = 1, TERM_SWITCH = 2, YOURTERM = 3,
[docs] def get_product_type(self) -> ProductType: if self in [ ProductSubType.TERM_SIT, ProductSubType.TERM100, ProductSubType.TERM_SWITCH, ProductSubType.YOURTERM ]: return ProductType.TERM raise NotImplementedError()
[docs] class ProductCode(Enum): TERM_SIT = "RBCI_TERM_SIT" TERM_SIT_HO = "RBCI_TERM_SIT_HO" TERM_TERM100 = "RBCI_TERM_TERM100" TERM_TERM100_BROKER = "RBCI_TERM_TERM100_BROKER" TERM_TERM100_HO = "RBCI_TERM_TERM100_HO" TERM_TERMSWITCH = "RBCI_TERM_TERMSWITCH" TERM_TERMSWITCH_BROKER = "RBCI_TERM_TERMSWITCH_BROKER" TERM_TERMSWITCH_HO = "RBCI_TERM_TERMSWITCH_HO" TERM_YOURTERM = "RBCI_TERM_YOURTERM" TERM_YOURTERM_BROKER = "RBCI_TERM_YOURTERM_BROKER" TERM_YOURTERM_HO = "RBCI_TERM_YOURTERM_HO"
[docs] def get_product_type(self) -> ProductType: return self.get_product_sub_type().get_product_type()
[docs] def get_product_sub_type(self) -> ProductSubType: if self in [ ProductCode.TERM_SIT, ProductCode.TERM_SIT_HO ]: return ProductSubType.TERM_SIT elif self in [ ProductCode.TERM_TERM100, ProductCode.TERM_TERM100_BROKER, ProductCode.TERM_TERM100_HO ]: return ProductSubType.TERM100 elif self in [ ProductCode.TERM_TERMSWITCH, ProductCode.TERM_TERMSWITCH_BROKER, ProductCode.TERM_TERMSWITCH_HO ]: return ProductSubType.TERM_SWITCH elif self in [ ProductCode.TERM_YOURTERM, ProductCode.TERM_YOURTERM_BROKER, ProductCode.TERM_YOURTERM_HO ]: return ProductSubType.YOURTERM raise NotImplementedError()
[docs] class ModalFactor(Enum): """ modal factors that are used to adjust the base premium for different payment frequencies. """ ANNUAL = 1.00 MONTHLY = 0.09
[docs] def apply_for_array_with_rounding(self, values: list[float], number_of_digits: int = 2): return [classic_round(self.apply(value), number_of_digits) for value in values]
[docs] def apply(self, value: float) -> float: return value * self.value
[docs] def revert(self, value: float) -> float: return value / self.value
[docs] class Frequency(Enum): """ number of times a payment occurs in a year. """ ANNUAL = 1 MONTHLY = 12
[docs] def get_payment_mode(self) -> "PaymentMode": match self: case Frequency.ANNUAL: return PaymentMode.ANNUAL case Frequency.MONTHLY: return PaymentMode.MONTHLY
[docs] def get_modal_factor(self) -> ModalFactor: match self: case Frequency.ANNUAL: return ModalFactor.ANNUAL case Frequency.MONTHLY: return ModalFactor.MONTHLY raise ValueError(f"No modal factor for {self}")
[docs] class PaymentMode(Enum): ANNUAL = 1 MONTHLY = 4
[docs] def get_frequency(self) -> Frequency: match self: case PaymentMode.ANNUAL: return Frequency.ANNUAL case PaymentMode.MONTHLY: return Frequency.MONTHLY
[docs] class LifeClass(Enum): OPTIMUM_NON_SMOKER = "Opt NS" PREFERRED_NON_SMOKER = "Pref NS" STANDARD_NON_SMOKER = "Std NS" PREFERRED_SMOKER = "Pref SM" STANDARD_SMOKER = "Std SM"
[docs] @staticmethod def get_from_string(life_class: str) -> 'LifeClass': match life_class: case "Opt NS": return LifeClass.OPTIMUM_NON_SMOKER case "Pref NS" | "Prf NS": return LifeClass.PREFERRED_NON_SMOKER case "Std NS": return LifeClass.STANDARD_NON_SMOKER case "Pref SM" | "Prf SM": return LifeClass.PREFERRED_SMOKER case "Std SM": return LifeClass.STANDARD_SMOKER
[docs] @staticmethod def get_life_class(underwriting_class: UnderwritingClass, smoking_status: TobaccoPremiumBasis) -> 'LifeClass': if underwriting_class == UnderwritingClass.STANDARD: if smoking_status == TobaccoPremiumBasis.NON_SMOKER: return LifeClass.STANDARD_NON_SMOKER if smoking_status == TobaccoPremiumBasis.SMOKER: return LifeClass.STANDARD_SMOKER if underwriting_class == UnderwritingClass.PREFERRED: if smoking_status == TobaccoPremiumBasis.NON_SMOKER: return LifeClass.PREFERRED_NON_SMOKER if smoking_status == TobaccoPremiumBasis.SMOKER: return LifeClass.PREFERRED_SMOKER if underwriting_class == UnderwritingClass.PREFERRED_PLUS: if smoking_status == TobaccoPremiumBasis.NON_SMOKER: return LifeClass.OPTIMUM_NON_SMOKER raise ValueError(f"No life class for {underwriting_class} and {smoking_status}")
# TODO: find a better name / no abbreviations
[docs] class IllusTxnCode(Enum): """ Holds the constants for different IllustrationTxn primary and secondary codes """ PRIMARY_TAX_BRACKET = 2 PRIMARY_SCHEDULED_DEPOSIT = 3 PRIMARY_DISTRIBUTION = 5 PRIMARY_SPECIFIED_COVERAGE_AMOUNT = 7 SECONDARY_SPECIFIED_AMOUNT = 20 SECONDARY_SPECIFIED_PREMIUM = 30 SECONDARY_SCHEDULED_LOAN_AMOUNT = 53 SECONDARY_INTEREST_RATE = 80 SECONDARY_SPECIFIED_AMOUNT_OF_TOTAL_COVERAGE = 137
[docs] class SolveType(Enum): SOLVE_PREMIUM = 0 SOLVE_COVERAGE = 1
[docs] class LivesType(Enum): SINGLE = 1 JOINT_FIRST_TO_DIE = 2 JOINT_LAST_TO_DIE = 3
[docs] def is_joint(self) -> bool: return self in [ LivesType.JOINT_FIRST_TO_DIE, LivesType.JOINT_LAST_TO_DIE, ]
[docs] class JointAgeType(Enum): JOINT_EXACT = 1 JOINT_FRASIER = 2 JOINT_EQUIVALENT = 3 JOINT_SINGLE = 4 # ESA
[docs] class DiscountType(Enum): TERM_CI_BUNDLE = "RBCI_TermCI_Bundle" COLLEAGUES_AS_CLIENTS_SAVINGS = "RBCI_Clients_Savings" TERM_RENEWAL_SAVINGS_PROGRAM = "RBCI_Term_Renewal_Savings" CONVERSION_CARRYOVER = "RBCI_Conversion_Carryover"