[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)
+ );
}
//============================================================================
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [lmi-commits] [lmi] master 2b6e1dc: Use unrounded deductions for ee/er-split list bills,
Greg Chicares <=