lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master 2b6e1dc: Use unrounded deductions for ee/er-s


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 2b6e1dc: Use unrounded deductions for ee/er-split list bills
Date: Sun, 18 Jun 2017 10:30:00 -0400 (EDT)

branch: master
commit 2b6e1dc0f338cf3a01dacf3df3c5d09cd52aa9a2
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Use unrounded deductions for ee/er-split list bills
---
 basic_values.hpp |  23 +++--
 ihs_acctval.cpp  |  30 +++----
 ihs_avstrtgy.cpp |  15 ++--
 ihs_basicval.cpp | 262 +++++++++++++++++++++++--------------------------------
 4 files changed, 136 insertions(+), 194 deletions(-)

diff --git a/basic_values.hpp b/basic_values.hpp
index f3c24ef..22055bd 100644
--- a/basic_values.hpp
+++ b/basic_values.hpp
@@ -229,30 +229,22 @@ class LMI_SO BasicValues
         ,double      a_bft_amt
         ,double      a_specamt
         ) const;
-    double GetModalPremMlyDedEe
-        (int         a_year
-        ,mcenum_mode a_mode
-        ,double      a_specamt
-        ) const;
-    double GetModalPremMlyDedEr
-        (int         a_year
-        ,mcenum_mode a_mode
-        ,double      a_specamt
-        ) const;
-    double GetListBillPremMlyDed
+    std::pair<double,double> GetModalPremMlyDedEx
         (int         year
         ,mcenum_mode mode
         ,double      specamt
+        ,double      termamt
         ) const;
-    double GetListBillPremMlyDedEe
+    double GetListBillPremMlyDed
         (int         year
         ,mcenum_mode mode
         ,double      specamt
         ) const;
-    double GetListBillPremMlyDedEr
+    std::pair<double,double> GetListBillPremMlyDedEx
         (int         year
         ,mcenum_mode mode
         ,double      specamt
+        ,double      termamt
         ) const;
     double GetModalSpecAmtMax      (double annualized_pmt) const;
     double GetModalSpecAmtTgt      (double annualized_pmt) const;
@@ -392,6 +384,11 @@ class LMI_SO BasicValues
         (int    year
         ,double specamt
         ) const;
+    std::pair<double,double> approx_mly_ded_ex
+        (int    year
+        ,double specamt
+        ,double termamt
+        ) const;
     double GetModalPremMlyDed
         (int         year
         ,mcenum_mode mode
diff --git a/ihs_acctval.cpp b/ihs_acctval.cpp
index c0dfd92..28ee376 100644
--- a/ihs_acctval.cpp
+++ b/ihs_acctval.cpp
@@ -975,20 +975,15 @@ void AccountValue::InitializeSpecAmt()
         }
     else
         {
-        InvariantValues().EeModalMinimumPremium[Year] = GetModalPremMlyDedEe
-            (Year
-            ,InvariantValues().ErMode[Year].value()
-            ,InvariantValues().TermSpecAmt[Year]
-            );
-        InvariantValues().ErModalMinimumPremium[Year] = GetModalPremMlyDedEr
+        auto const z = GetModalPremMlyDedEx
             (Year
             ,InvariantValues().ErMode[Year].value()
             ,InvariantValues().SpecAmt[Year]
+            ,InvariantValues().TermSpecAmt[Year]
             );
-        InvariantValues().ModalMinimumPremium[Year] =
-              InvariantValues().EeModalMinimumPremium[Year]
-            + InvariantValues().ErModalMinimumPremium[Year]
-            ;
+        InvariantValues().EeModalMinimumPremium[Year] = z.first;
+        InvariantValues().ErModalMinimumPremium[Year] = z.second;
+        InvariantValues().ModalMinimumPremium[Year] = z.first + z.second;
         }
 
     // No-lapse premium generally changes whenever specamt changes for
@@ -1211,20 +1206,15 @@ void AccountValue::set_list_bill_premium()
         }
     else
         {
-        InvariantValues().EeListBillPremium = GetListBillPremMlyDedEe
-            (Year
-            ,InvariantValues().ErMode[Year].value()
-            ,InvariantValues().TermSpecAmt[Year]
-            );
-        InvariantValues().ErListBillPremium = GetListBillPremMlyDedEr
+        auto const z = GetListBillPremMlyDedEx
             (Year
             ,InvariantValues().ErMode[Year].value()
             ,InvariantValues().SpecAmt[Year]
+            ,InvariantValues().TermSpecAmt[Year]
             );
-        InvariantValues().ListBillPremium =
-              InvariantValues().EeListBillPremium
-            + InvariantValues().ErListBillPremium
-            ;
+        InvariantValues().EeListBillPremium = z.first;
+        InvariantValues().ErListBillPremium = z.second;
+        InvariantValues().ListBillPremium = z.first + z.second;
         }
 }
 
diff --git a/ihs_avstrtgy.cpp b/ihs_avstrtgy.cpp
index db0628b..3c580ee 100644
--- a/ihs_avstrtgy.cpp
+++ b/ihs_avstrtgy.cpp
@@ -220,6 +220,12 @@ double AccountValue::DoPerformPmtStrategy
             {
             if(SplitMinPrem)
                 {
+                auto const z = GetModalPremMlyDedEx
+                    (Year
+                    ,a_CurrentMode
+                    ,ActualSpecAmt
+                    ,TermSpecAmt
+                    );
                 if(UnsplitSplitMinPrem)
                     {
                     // Normally, if min prem is defined separately
@@ -229,10 +235,7 @@ double AccountValue::DoPerformPmtStrategy
                     // subplan designed for a single payor (e.g.,
                     // "voluntary" group coverage), "minimum" means
                     // the total: what was "split" must be "unsplit".
-                    return
-                          GetModalPremMlyDedEe(Year, a_CurrentMode, 
TermSpecAmt)
-                        + GetModalPremMlyDedEr(Year, a_CurrentMode, 
ActualSpecAmt)
-                        ;
+                    return z.first + z.second;
                     }
                 else if(mce_solve_ee_prem == a_SolveForWhichPrem)
                     {
@@ -240,11 +243,11 @@ double AccountValue::DoPerformPmtStrategy
                     // which represents the payment mode chosen by the
                     // plan sponsor; but lmi has the extra flexibility
                     // to behave reasonably if it's not so entered.
-                    return GetModalPremMlyDedEe(Year, a_CurrentMode, 
TermSpecAmt);
+                    return z.first;
                     }
                 else if(mce_solve_er_prem == a_SolveForWhichPrem)
                     {
-                    return GetModalPremMlyDedEr(Year, a_CurrentMode, 
ActualSpecAmt);
+                    return z.second;
                     }
                 else
                     {
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index b54e96e..e99d5b3 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -1186,159 +1186,86 @@ std::pair<double,double> BasicValues::approx_mly_ded
     return std::make_pair(ann_ded, mly_ded);
 }
 
-/// Determine an approximate "pay as you go" modal premium.
-
-double BasicValues::GetModalPremMlyDed
-    (int         year
-    ,mcenum_mode mode
-    ,double      specamt
-    ) const
-{
-    auto const deductions = approx_mly_ded(year, specamt);
-    double const ann_ded = deductions.first;
-    double const mly_ded = deductions.second;
-    double const v12 = mly_ded_discount_factor(year, mode);
-    double const annuity = (1.0 - std::pow(v12, 12.0 / mode)) / (1.0 - v12);
-    return round_min_premium()(ann_ded + mly_ded * annuity);
-}
-
-// The "-Ee" and "-Er" variants are written with preprocessor
-// conditionals for ease of comparison to the unsuffixed original.
+/// Determine approximate monthly deductions split between ee and er.
+///
+/// This function is similar to approx_mly_ded(), but splits monthly
+/// deductions between employee and employer in a plausible way. It is
+/// intended for use with group UL products for which the ee and er
+/// typically pay these approximate monthly deductions; it and the
+/// modal-premium function implemented in terms of it are of narrow
+/// applicability and generally not useful with other products.
+///
+/// Any once-a-year monthly deduction is deliberately ignored for
+/// simplicity--if any annual charge is not zero, this function simply
+/// sets the deductions it calculates to zero. Once-a-year deductions
+/// (e.g., a fee to offset underwriting costs for private placements)
+/// are extraordinary, and occur only on products for which a split
+/// between ee and er would not be wanted.
 
-double BasicValues::GetModalPremMlyDedEe
-    (int         a_year
-    ,mcenum_mode a_mode
-    ,double      a_specamt
+std::pair<double,double> BasicValues::approx_mly_ded_ex
+    (int    year
+    ,double specamt
+    ,double termamt
     ) const
 {
-    double z = a_specamt * DBDiscountRate[a_year];
-    z *= GetCurrentTermRates()[a_year];
-#if 0
-    if(yare_input_.AccidentalDeathBenefit)
-        {
-        double r = MortalityRates_->AdbRates()[a_year];
-        z += r * std::min(a_specamt, AdbLimit);
-        }
-#endif // 0
-    if(yare_input_.SpouseRider)
-        {
-        double r = MortalityRates_->SpouseRiderRates(mce_gen_curr)[a_year];
-        z += r * yare_input_.SpouseRiderAmount;
-        }
-
-    if(yare_input_.ChildRider)
+    if(0.0 != Loads_->annual_policy_fee(mce_gen_curr)[year])
         {
-        double r = MortalityRates_->ChildRiderRates()[a_year];
-        z += r * yare_input_.ChildRiderAmount;
+        return {0.0, 0.0};
         }
 
-#if 0
-    if(true) // Written thus for parallelism and to keep 'r' local.
-        {
-        double r = Loads_->specified_amount_load(mce_gen_curr)[a_year];
-        z += r * std::min(a_specamt, SpecAmtLoadLimit);
-        }
-
-    z += Loads_->monthly_policy_fee(mce_gen_curr)[a_year];
-
-    double annual_charge = Loads_->annual_policy_fee(mce_gen_curr)[a_year];
-#endif // 0
-
-    if(yare_input_.WaiverOfPremiumBenefit)
-        {
-        double const r = MortalityRates_->WpRates()[a_year];
-        switch(WaiverChargeMethod)
-            {
-            case oe_waiver_times_specamt:
-                {
-                z += r * std::min(a_specamt, WpLimit);
-                }
-                break;
-            case oe_waiver_times_deductions:
-                {
-                z *= 1.0 + r;
-#if 0
-                annual_charge *= 1.0 + r;
-#endif // 0
-                }
-                break;
-            default:
-                {
-                alarum()
-                    << "Case '"
-                    << WaiverChargeMethod
-                    << "' not found."
-                    << LMI_FLUSH
-                    ;
-                }
-            }
-        }
-
-    z /= 1.0 - Loads_->target_premium_load_maximum_premium_tax()[a_year];
+    double ee_ded = termamt * DBDiscountRate[year];
+    double er_ded = specamt * DBDiscountRate[year];
 
-    double const v12 = DBDiscountRate[a_year];
-    z *= (1.0 - std::pow(v12, 12.0 / a_mode)) / (1.0 - v12);
-
-#if 0
-    z += annual_charge;
-#endif // 0
-
-    return round_min_premium()(z);
-}
-
-double BasicValues::GetModalPremMlyDedEr
-    (int         a_year
-    ,mcenum_mode a_mode
-    ,double      a_specamt
-    ) const
-{
-    double z = a_specamt * DBDiscountRate[a_year];
-    z *= GetBandedCoiRates(mce_gen_curr, a_specamt)[a_year];
+    ee_ded *= GetCurrentTermRates()[year];
+    er_ded *= GetBandedCoiRates(mce_gen_curr, specamt)[year];
 
+    // Paid by er.
     if(yare_input_.AccidentalDeathBenefit)
         {
-        double r = MortalityRates_->AdbRates()[a_year];
-        z += r * std::min(a_specamt, AdbLimit);
+        double const r = MortalityRates_->AdbRates()[year];
+        er_ded += r * std::min(specamt, AdbLimit);
         }
 
-#if 0
+    // Paid by ee.
     if(yare_input_.SpouseRider)
         {
-        double r = MortalityRates_->SpouseRiderRates(mce_gen_curr)[a_year];
-        z += r * yare_input_.SpouseRiderAmount;
+        double const r = MortalityRates_->SpouseRiderRates(mce_gen_curr)[year];
+        ee_ded += r * yare_input_.SpouseRiderAmount;
         }
 
+    // Paid by ee.
     if(yare_input_.ChildRider)
         {
-        double r = MortalityRates_->ChildRiderRates()[a_year];
-        z += r * yare_input_.ChildRiderAmount;
+        double const r = MortalityRates_->ChildRiderRates()[year];
+        ee_ded += r * yare_input_.ChildRiderAmount;
         }
-#endif // 0
 
+    // Paid by er.
     if(true) // Written thus for parallelism and to keep 'r' local.
         {
-        double r = Loads_->specified_amount_load(mce_gen_curr)[a_year];
-        z += r * std::min(a_specamt, SpecAmtLoadLimit);
+        double const r = Loads_->specified_amount_load(mce_gen_curr)[year];
+        er_ded += r * std::min(specamt, SpecAmtLoadLimit);
         }
 
-    z += Loads_->monthly_policy_fee(mce_gen_curr)[a_year];
-
-    double annual_charge = Loads_->annual_policy_fee(mce_gen_curr)[a_year];
+    // Paid by er.
+    er_ded += Loads_->monthly_policy_fee(mce_gen_curr)[year];
 
     if(yare_input_.WaiverOfPremiumBenefit)
         {
-        double const r = MortalityRates_->WpRates()[a_year];
+        double const r = MortalityRates_->WpRates()[year];
         switch(WaiverChargeMethod)
             {
             case oe_waiver_times_specamt:
                 {
-                z += r * std::min(a_specamt, WpLimit);
+                // Paid by er. (In this case, WP excludes term.)
+                er_ded += r * std::min(specamt, WpLimit);
                 }
                 break;
             case oe_waiver_times_deductions:
                 {
-                z *= 1.0 + r;
-                annual_charge *= 1.0 + r;
+                // Paid by ee and er both.
+                ee_ded *= 1.0 + r;
+                er_ded *= 1.0 + r;
                 }
                 break;
             default:
@@ -1353,14 +1280,47 @@ double BasicValues::GetModalPremMlyDedEr
             }
         }
 
-    z /= 1.0 - Loads_->target_premium_load_maximum_premium_tax()[a_year];
+    double const load = 
Loads_->target_premium_load_maximum_premium_tax()[year];
+    ee_ded /= 1.0 - load;
+    er_ded /= 1.0 - load;
 
-    double const v12 = DBDiscountRate[a_year];
-    z *= (1.0 - std::pow(v12, 12.0 / a_mode)) / (1.0 - v12);
+    return std::make_pair(ee_ded, er_ded);
+}
 
-    z += annual_charge;
+/// Determine an approximate "pay as you go" modal premium.
 
-    return round_min_premium()(z);
+double BasicValues::GetModalPremMlyDed
+    (int         year
+    ,mcenum_mode mode
+    ,double      specamt
+    ) const
+{
+    auto const deductions = approx_mly_ded(year, specamt);
+    double const ann_ded = deductions.first;
+    double const mly_ded = deductions.second;
+    double const v12 = mly_ded_discount_factor(year, mode);
+    double const annuity = (1.0 - std::pow(v12, 12.0 / mode)) / (1.0 - v12);
+    return round_min_premium()(ann_ded + mly_ded * annuity);
+}
+
+/// Determine approximate ee and er "pay as you go" modal premiums.
+
+std::pair<double,double> BasicValues::GetModalPremMlyDedEx
+    (int         year
+    ,mcenum_mode mode
+    ,double      specamt
+    ,double      termamt
+    ) const
+{
+    auto const deductions = approx_mly_ded_ex(year, specamt, termamt);
+    double const ee_ded = deductions.first;
+    double const er_ded = deductions.second;
+    double const v12 = DBDiscountRate[year];
+    double const annuity = (1.0 - std::pow(v12, 12.0 / mode)) / (1.0 - v12);
+    return std::make_pair
+        (round_min_premium()(ee_ded * annuity)
+        ,round_min_premium()(er_ded * annuity)
+        );
 }
 
 /// Possibly off-anniversary premium to be shown on list bill.
@@ -1369,12 +1329,16 @@ double BasicValues::GetModalPremMlyDedEr
 ///
 /// Ascertain deductions at the current age, and then again at the
 /// next age iff that is less than the maturity age, otherwise
-/// assuming that deductions are zero after maturity.
+/// assuming that deductions are zero after maturity. Return their
+/// present value, discounted at an interest rate determined as of
+/// the list-bill date.
 ///
 /// Any once-a-year monthly deduction is deliberately ignored for
-/// simplicity. Such charges are extraordinary (e.g., a fee to
-/// offset underwriting costs for private placements), and occur
-/// only on products for which list bills would not be wanted.
+/// simplicity--if any annual charge is not zero, this function simply
+/// sets the premiums it calculates to zero. Once-a-year deductions
+/// (e.g., a fee to offset underwriting costs for private placements)
+/// are extraordinary, and occur only on products for which list bills
+/// would not be wanted.
 
 double BasicValues::GetListBillPremMlyDed
     (int         year
@@ -1400,52 +1364,40 @@ double BasicValues::GetListBillPremMlyDed
     return round_min_premium()(z);
 }
 
-double BasicValues::GetListBillPremMlyDedEe
+std::pair<double,double> BasicValues::GetListBillPremMlyDedEx
     (int         year
     ,mcenum_mode mode
     ,double      specamt
+    ,double      termamt
     ) const
 {
-    double const p0 = GetModalPremMlyDedEe(year, mce_monthly, specamt);
+    auto const p0 = approx_mly_ded_ex(year, specamt, termamt);
     int const next_year = 1 + year;
-    double const p1 =
+    auto const p1 =
         (next_year < GetLength())
-        ? GetModalPremMlyDedEe(next_year, mce_monthly, specamt)
-        : 0.0
+        ? approx_mly_ded_ex(next_year, specamt, termamt)
+        : decltype(p0)()
         ;
-    double const z = list_bill_premium
-        (p0
-        ,p1
+    double const ee_prem = list_bill_premium
+        (p0.first
+        ,p1.first
         ,mode
         ,yare_input_.EffectiveDate
         ,yare_input_.ListBillDate
         ,DBDiscountRate[year]
         );
-    return round_min_premium()(z);
-}
-
-double BasicValues::GetListBillPremMlyDedEr
-    (int         year
-    ,mcenum_mode mode
-    ,double      specamt
-    ) const
-{
-    double const p0 = GetModalPremMlyDedEr(year, mce_monthly, specamt);
-    int const next_year = 1 + year;
-    double const p1 =
-        (next_year < GetLength())
-        ? GetModalPremMlyDedEr(next_year, mce_monthly, specamt)
-        : 0.0
-        ;
-    double const z = list_bill_premium
-        (p0
-        ,p1
+    double const er_prem = list_bill_premium
+        (p0.second
+        ,p1.second
         ,mode
         ,yare_input_.EffectiveDate
         ,yare_input_.ListBillDate
         ,DBDiscountRate[year]
         );
-    return round_min_premium()(z);
+    return std::make_pair
+        (round_min_premium()(ee_prem)
+        ,round_min_premium()(er_prem)
+        );
 }
 
 //============================================================================



reply via email to

[Prev in Thread] Current Thread [Next in Thread]