lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master f7a1129 08/15: Store policy fees as currency


From: Greg Chicares
Subject: [lmi-commits] [lmi] master f7a1129 08/15: Store policy fees as currency
Date: Mon, 25 Jan 2021 09:58:05 -0500 (EST)

branch: master
commit f7a112964078a33fdbb431ac14a31c5e1d071ec1
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Store policy fees as currency
    
    Rounded amounts on all bases as they arise, replacing the separate
    final step that used to be required.
    
    Changed the unit test's "extra" policy fee from $0.60 to $0.50, to
    emphasize that midpoint fees must be rounded even if all their
    components were exactly representable in floating point.
---
 basicvalues.cpp  |  4 +--
 ihs_basicval.cpp | 14 +++++-----
 loads.cpp        | 84 ++++++++++++++++++++++++++++++++++++--------------------
 loads.hpp        |  4 +--
 loads_test.cpp   | 61 +++++++++++++++++++++-------------------
 5 files changed, 98 insertions(+), 69 deletions(-)

diff --git a/basicvalues.cpp b/basicvalues.cpp
index 9206717..701b548 100644
--- a/basicvalues.cpp
+++ b/basicvalues.cpp
@@ -204,7 +204,7 @@ currency BasicValues::GetModalTgtPrem
                 )[a_year]
         );
     z *= MortalityRates_->MonthlyCoiRates(mce_gen_curr)[a_year];
-    z += Loads_->monthly_policy_fee(mce_gen_curr)[a_year];
+    z += dblize(Loads_->monthly_policy_fee(mce_gen_curr)[a_year]);
 //    z += AdbRate;
 //    z *= 1.0 + WpRate;
     z /= 1.0 - Loads_->target_premium_load(mce_gen_curr)[a_year];
@@ -274,7 +274,7 @@ currency BasicValues::GetModalTgtSpecAmt
     z *= 1.0 - Loads_->target_premium_load(mce_gen_curr)[0];
 //    z /= WpRate;
 //    z -= AdbRate;
-    z -= Loads_->monthly_policy_fee(mce_gen_curr)[0];
+    z -= dblize(Loads_->monthly_policy_fee(mce_gen_curr)[0]);
     z /= MortalityRates_->MonthlyCoiRates(mce_gen_curr)[0];
     z *=
         (   1.0
diff --git a/ihs_basicval.cpp b/ihs_basicval.cpp
index 47ab721..659509e 100644
--- a/ihs_basicval.cpp
+++ b/ihs_basicval.cpp
@@ -494,9 +494,9 @@ void BasicValues::Init7702()
             ,yare_input_.SpecifiedAmount[0] + yare_input_.TermRiderAmount
             ,yare_input_.SpecifiedAmount[0] + yare_input_.TermRiderAmount
             ,effective_dbopt_7702(yare_input_.DeathBenefitOption[0], 
Effective7702DboRop)
-            ,Loads_->annual_policy_fee    (mce_gen_curr)
-            ,Loads_->monthly_policy_fee   (mce_gen_curr)
-            ,Loads_->specified_amount_load(mce_gen_curr)
+            ,dblize(Loads_->annual_policy_fee  (mce_gen_curr))
+            ,dblize(Loads_->monthly_policy_fee (mce_gen_curr))
+            ,Loads_->specified_amount_load     (mce_gen_curr)
             ,SpecAmtLoadLimit
             ,local_mly_charge_add
             ,local_adb_limit
@@ -1157,9 +1157,9 @@ std::pair<double,double> BasicValues::approx_mly_ded
         mly_ded += r * std::min(specamt, SpecAmtLoadLimit);
         }
 
-    mly_ded += Loads_->monthly_policy_fee(mce_gen_curr)[year];
+    mly_ded += dblize(Loads_->monthly_policy_fee(mce_gen_curr)[year]);
 
-    double ann_ded = Loads_->annual_policy_fee(mce_gen_curr)[year];
+    double ann_ded = dblize(Loads_->annual_policy_fee(mce_gen_curr)[year]);
 
     if(yare_input_.WaiverOfPremiumBenefit)
         {
@@ -1211,7 +1211,7 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
     ,currency termamt
     ) const
 {
-    if(0.0 != Loads_->annual_policy_fee(mce_gen_curr)[year])
+    if(C0 != Loads_->annual_policy_fee(mce_gen_curr)[year])
         {
         return {0.0, 0.0};
         }
@@ -1251,7 +1251,7 @@ std::pair<double,double> BasicValues::approx_mly_ded_ex
         }
 
     // Paid by er.
-    er_ded += Loads_->monthly_policy_fee(mce_gen_curr)[year];
+    er_ded += dblize(Loads_->monthly_policy_fee(mce_gen_curr)[year]);
 
     if(yare_input_.WaiverOfPremiumBenefit)
         {
diff --git a/loads.cpp b/loads.cpp
index a310661..021de45 100644
--- a/loads.cpp
+++ b/loads.cpp
@@ -35,6 +35,35 @@
 #include "oecumenic_enumerations.hpp"
 #include "premium_tax.hpp"
 
+namespace
+{
+void query_into
+    (product_database const& database
+    ,e_database_key          key
+    ,std::vector<currency>&  v
+    ,round_to<double> const& round_minutiae
+    )
+{
+    std::vector<double> z;
+    database.query_into(key, z);
+    v = round_minutiae.c(z);
+}
+
+void assign_midpoint
+    (std::vector<currency>      & out
+    ,std::vector<currency> const& in_0
+    ,std::vector<currency> const& in_1
+    ,round_to<double>      const& round_minutiae
+    )
+{
+    std::vector<double> const v0 = dblize(in_0);
+    std::vector<double> const v1 = dblize(in_1);
+    std::vector<double>       z;
+    ::assign_midpoint(z, v0, v1);
+    out = round_minutiae.c(z);
+}
+} // Unnamed namespace.
+
 /// Ctor for production branch.
 
 Loads::Loads(BasicValues& V)
@@ -57,12 +86,12 @@ Loads::Loads(BasicValues& V)
         ,V.round_minutiae()
         ,V.yare_input_.ExtraCompensationOnPremium
         ,V.yare_input_.ExtraCompensationOnAssets
-        ,V.yare_input_.ExtraMonthlyCustodialFee
+        ,V.round_minutiae().c(V.yare_input_.ExtraMonthlyCustodialFee)
         ,V.GetGuarSpecAmtLoadTable()
         ,V.GetCurrSpecAmtLoadTable()
         );
     Allocate(length);
-    Initialize(V.database());
+    Initialize(V.database(), details);
     Calculate(details);
 }
 
@@ -115,13 +144,15 @@ void Loads::Allocate(int length)
 
 /// Set various data members from product database.
 
-void Loads::Initialize(product_database const& database)
+void Loads::Initialize(product_database const& database, load_details const& 
details)
 {
+    round_to<double> const& r = details.round_minutiae_;
+
     database.query_into(DB_LoadRfdProportion , 
refundable_sales_load_proportion_   );
     database.query_into(DB_DacTaxPremLoad    , dac_tax_load_                   
    );
 
-    database.query_into(DB_GuarMonthlyPolFee , monthly_policy_fee_   
[mce_gen_guar]);
-    database.query_into(DB_GuarAnnualPolFee  , annual_policy_fee_    
[mce_gen_guar]);
+    query_into(database, DB_GuarMonthlyPolFee, monthly_policy_fee_   
[mce_gen_guar], r);
+    query_into(database, DB_GuarAnnualPolFee , annual_policy_fee_    
[mce_gen_guar], r);
     database.query_into(DB_GuarSpecAmtLoad   , 
specified_amount_load_[mce_gen_guar]);
     database.query_into(DB_GuarAcctValLoad   , 
separate_account_load_[mce_gen_guar]);
     database.query_into(DB_GuarPremLoadTgt   , target_premium_load_  
[mce_gen_guar]);
@@ -129,8 +160,8 @@ void Loads::Initialize(product_database const& database)
     database.query_into(DB_GuarPremLoadTgtRfd, target_sales_load_    
[mce_gen_guar]);
     database.query_into(DB_GuarPremLoadExcRfd, excess_sales_load_    
[mce_gen_guar]);
 
-    database.query_into(DB_CurrMonthlyPolFee , monthly_policy_fee_   
[mce_gen_curr]);
-    database.query_into(DB_CurrAnnualPolFee  , annual_policy_fee_    
[mce_gen_curr]);
+    query_into(database, DB_CurrMonthlyPolFee, monthly_policy_fee_   
[mce_gen_curr], r);
+    query_into(database, DB_CurrAnnualPolFee , annual_policy_fee_    
[mce_gen_curr], r);
     database.query_into(DB_CurrSpecAmtLoad   , 
specified_amount_load_[mce_gen_curr]);
     database.query_into(DB_CurrAcctValLoad   , 
separate_account_load_[mce_gen_curr]);
     database.query_into(DB_CurrPremLoadTgt   , target_premium_load_  
[mce_gen_curr]);
@@ -143,6 +174,8 @@ void Loads::Initialize(product_database const& database)
 
 void Loads::Calculate(load_details const& details)
 {
+    round_to<double> const& r = details.round_minutiae_;
+
     premium_tax_load_.assign(details.length_, details.premium_tax_load_);
 
     for(int j = mce_gen_curr; j != mc_n_gen_bases; ++j)
@@ -275,6 +308,7 @@ void Loads::Calculate(load_details const& details)
     // consistent with this constraint.
 
     monthly_policy_fee_[mce_gen_curr] += details.VectorExtraPolFee_;
+
     for(int j = 0; j < details.length_; ++j)
         {
         if
@@ -299,8 +333,8 @@ void Loads::Calculate(load_details const& details)
 
     if(details.NeedMidpointRates_)
         {
-        assign_midpoint(monthly_policy_fee_   [mce_gen_mdpt], 
monthly_policy_fee_   [mce_gen_guar], monthly_policy_fee_   [mce_gen_curr]);
-        assign_midpoint(annual_policy_fee_    [mce_gen_mdpt], 
annual_policy_fee_    [mce_gen_guar], annual_policy_fee_    [mce_gen_curr]);
+        assign_midpoint(monthly_policy_fee_   [mce_gen_mdpt], 
monthly_policy_fee_   [mce_gen_guar], monthly_policy_fee_   [mce_gen_curr], r);
+        assign_midpoint(annual_policy_fee_    [mce_gen_mdpt], 
annual_policy_fee_    [mce_gen_guar], annual_policy_fee_    [mce_gen_curr], r);
         assign_midpoint(specified_amount_load_[mce_gen_mdpt], 
specified_amount_load_[mce_gen_guar], specified_amount_load_[mce_gen_curr]);
         assign_midpoint(separate_account_load_[mce_gen_mdpt], 
separate_account_load_[mce_gen_guar], separate_account_load_[mce_gen_curr]);
         assign_midpoint(target_premium_load_  [mce_gen_mdpt], 
target_premium_load_  [mce_gen_guar], target_premium_load_  [mce_gen_curr]);
@@ -310,18 +344,6 @@ void Loads::Calculate(load_details const& details)
         assign_midpoint(target_total_load_    [mce_gen_mdpt], 
target_total_load_    [mce_gen_guar], target_total_load_    [mce_gen_curr]);
         assign_midpoint(excess_total_load_    [mce_gen_mdpt], 
excess_total_load_    [mce_gen_guar], excess_total_load_    [mce_gen_curr]);
         }
-
-    // Round policy fees. No known product specifies any policy fee
-    // in fractional cents. However, if the monthly policy fee is
-    // $3.25 (current) and $5.00 (guaranteed), the midpoint shouldn't
-    // be $4.125, because subtracting that from the account value
-    // would make it a non-integral number of cents.
-
-    for(int j = mce_gen_curr; j < mc_n_gen_bases; ++j)
-        {
-        monthly_policy_fee_ [j] = details.round_minutiae_(monthly_policy_fee_ 
[j]);
-        annual_policy_fee_  [j] = details.round_minutiae_(annual_policy_fee_  
[j]);
-        }
 }
 
 /// Amortize premium tax.
@@ -351,26 +373,28 @@ void Loads::AmortizePremiumTax(load_details const&)
 
 Loads::Loads(product_database const& database, bool NeedMidpointRates)
 {
+    round_to<double> const& r {2, r_to_nearest};
+
     monthly_policy_fee_   .resize(mc_n_gen_bases);
     target_premium_load_  .resize(mc_n_gen_bases);
     excess_premium_load_  .resize(mc_n_gen_bases);
     specified_amount_load_.resize(mc_n_gen_bases);
 
-    database.query_into(DB_GuarMonthlyPolFee, monthly_policy_fee_   
[mce_gen_guar]);
-    database.query_into(DB_GuarPremLoadTgt  , target_premium_load_  
[mce_gen_guar]);
-    database.query_into(DB_GuarPremLoadExc  , excess_premium_load_  
[mce_gen_guar]);
-    database.query_into(DB_GuarSpecAmtLoad  , 
specified_amount_load_[mce_gen_guar]);
+    query_into(database, DB_GuarMonthlyPolFee, monthly_policy_fee_   
[mce_gen_guar], r);
+    database.query_into(DB_GuarPremLoadTgt   , target_premium_load_  
[mce_gen_guar]);
+    database.query_into(DB_GuarPremLoadExc   , excess_premium_load_  
[mce_gen_guar]);
+    database.query_into(DB_GuarSpecAmtLoad   , 
specified_amount_load_[mce_gen_guar]);
 
-    database.query_into(DB_CurrMonthlyPolFee, monthly_policy_fee_   
[mce_gen_curr]);
-    database.query_into(DB_CurrPremLoadTgt  , target_premium_load_  
[mce_gen_curr]);
-    database.query_into(DB_CurrPremLoadExc  , excess_premium_load_  
[mce_gen_curr]);
-    database.query_into(DB_CurrSpecAmtLoad  , 
specified_amount_load_[mce_gen_curr]);
+    query_into(database, DB_CurrMonthlyPolFee, monthly_policy_fee_   
[mce_gen_curr], r);
+    database.query_into(DB_CurrPremLoadTgt   , target_premium_load_  
[mce_gen_curr]);
+    database.query_into(DB_CurrPremLoadExc   , excess_premium_load_  
[mce_gen_curr]);
+    database.query_into(DB_CurrSpecAmtLoad   , 
specified_amount_load_[mce_gen_curr]);
 
     // This ctor ignores tabular specified-amount loads.
 
     if(NeedMidpointRates)
         {
-        assign_midpoint(monthly_policy_fee_   [mce_gen_mdpt], 
monthly_policy_fee_   [mce_gen_guar], monthly_policy_fee_   [mce_gen_curr]);
+        assign_midpoint(monthly_policy_fee_   [mce_gen_mdpt], 
monthly_policy_fee_   [mce_gen_guar], monthly_policy_fee_   [mce_gen_curr], r);
         assign_midpoint(target_premium_load_  [mce_gen_mdpt], 
target_premium_load_  [mce_gen_guar], target_premium_load_  [mce_gen_curr]);
         assign_midpoint(excess_premium_load_  [mce_gen_mdpt], 
excess_premium_load_  [mce_gen_guar], excess_premium_load_  [mce_gen_curr]);
         assign_midpoint(specified_amount_load_[mce_gen_mdpt], 
specified_amount_load_[mce_gen_guar], specified_amount_load_[mce_gen_curr]);
diff --git a/loads.hpp b/loads.hpp
index 8e99166..2fd5662 100644
--- a/loads.hpp
+++ b/loads.hpp
@@ -76,10 +76,10 @@ class Loads
     Loads() = default; // Ctor for unit testing.
 
     void Allocate(int length);
-    void Initialize(product_database const&);
+    void Initialize(product_database const&, load_details const&);
     void Calculate(load_details const&);
 
-    void AmortizePremiumTax(load_details const& details);
+    void AmortizePremiumTax(load_details const&);
 
     std::vector<double> refundable_sales_load_proportion_;
 
diff --git a/loads_test.cpp b/loads_test.cpp
index 3245074..3fa44ed 100644
--- a/loads_test.cpp
+++ b/loads_test.cpp
@@ -67,7 +67,7 @@ struct LoadsTest
         {}
 
     void Allocate  () {loads_.Allocate(details_.length_);}
-    void Initialize() {loads_.Initialize(database_);}
+    void Initialize() {loads_.Initialize(database_, details_);}
     void Calculate () {loads_.Calculate(details_);}
 
     void Reinitialize();
@@ -83,26 +83,26 @@ struct LoadsTest
 
 void LoadsTest::Reinitialize()
 {
-    loads_.refundable_sales_load_proportion_    = std::vector<double>(length, 
0.50000);
-    loads_.dac_tax_load_                        = std::vector<double>(length, 
0.00500);
-
-    loads_.monthly_policy_fee_   [mce_gen_guar] = std::vector<double>(length, 
8.00000);
-    loads_.annual_policy_fee_    [mce_gen_guar] = std::vector<double>(length, 
2.00000);
-    loads_.specified_amount_load_[mce_gen_guar] = std::vector<double>(length, 
0.00003);
-    loads_.separate_account_load_[mce_gen_guar] = std::vector<double>(length, 
0.00130);
-    loads_.target_premium_load_  [mce_gen_guar] = std::vector<double>(length, 
0.04000);
-    loads_.excess_premium_load_  [mce_gen_guar] = std::vector<double>(length, 
0.03000);
-    loads_.target_sales_load_    [mce_gen_guar] = std::vector<double>(length, 
0.30000);
-    loads_.excess_sales_load_    [mce_gen_guar] = std::vector<double>(length, 
0.15000);
-
-    loads_.monthly_policy_fee_   [mce_gen_curr] = std::vector<double>(length, 
5.25000);
-    loads_.annual_policy_fee_    [mce_gen_curr] = std::vector<double>(length, 
1.00000);
-    loads_.specified_amount_load_[mce_gen_curr] = std::vector<double>(length, 
0.00002);
-    loads_.separate_account_load_[mce_gen_curr] = std::vector<double>(length, 
0.00110);
-    loads_.target_premium_load_  [mce_gen_curr] = std::vector<double>(length, 
0.02000);
-    loads_.excess_premium_load_  [mce_gen_curr] = std::vector<double>(length, 
0.01000);
-    loads_.target_sales_load_    [mce_gen_curr] = std::vector<double>(length, 
0.10000);
-    loads_.excess_sales_load_    [mce_gen_curr] = std::vector<double>(length, 
0.05000);
+    loads_.refundable_sales_load_proportion_    = std::vector<double>  
(length, 0.50000);
+    loads_.dac_tax_load_                        = std::vector<double>  
(length, 0.00500);
+
+    loads_.monthly_policy_fee_   [mce_gen_guar] = 
std::vector<currency>(length, from_cents(800));
+    loads_.annual_policy_fee_    [mce_gen_guar] = 
std::vector<currency>(length, from_cents(200));
+    loads_.specified_amount_load_[mce_gen_guar] = std::vector<double>  
(length, 0.00003);
+    loads_.separate_account_load_[mce_gen_guar] = std::vector<double>  
(length, 0.00130);
+    loads_.target_premium_load_  [mce_gen_guar] = std::vector<double>  
(length, 0.04000);
+    loads_.excess_premium_load_  [mce_gen_guar] = std::vector<double>  
(length, 0.03000);
+    loads_.target_sales_load_    [mce_gen_guar] = std::vector<double>  
(length, 0.30000);
+    loads_.excess_sales_load_    [mce_gen_guar] = std::vector<double>  
(length, 0.15000);
+
+    loads_.monthly_policy_fee_   [mce_gen_curr] = 
std::vector<currency>(length, from_cents(525));
+    loads_.annual_policy_fee_    [mce_gen_curr] = 
std::vector<currency>(length, from_cents(100));
+    loads_.specified_amount_load_[mce_gen_curr] = std::vector<double>  
(length, 0.00002);
+    loads_.separate_account_load_[mce_gen_curr] = std::vector<double>  
(length, 0.00110);
+    loads_.target_premium_load_  [mce_gen_curr] = std::vector<double>  
(length, 0.02000);
+    loads_.excess_premium_load_  [mce_gen_curr] = std::vector<double>  
(length, 0.01000);
+    loads_.target_sales_load_    [mce_gen_curr] = std::vector<double>  
(length, 0.10000);
+    loads_.excess_sales_load_    [mce_gen_curr] = std::vector<double>  
(length, 0.05000);
 }
 
 void LoadsTest::TestVectorLengths(char const* file, int line)
@@ -158,8 +158,13 @@ void LoadsTest::TestCalculations(char const* file, int 
line)
 {
     INVOKE_BOOST_TEST(materially_equal(0.500000, 
loads_.refundable_sales_load_proportion()[0]), file, line);
 
-    INVOKE_BOOST_TEST(materially_equal(6.920000, loads_.monthly_policy_fee    
(mce_gen_mdpt)[0]), file, line);
-    INVOKE_BOOST_TEST(materially_equal(1.500000, loads_.annual_policy_fee     
(mce_gen_mdpt)[0]), file, line);
+    // (8.00 + 5.25 + 0.50) / 2 = 13.75 / 2 = 6.875, rounded to cents
+#   if defined CURRENCY_UNIT_IS_CENTS
+    INVOKE_BOOST_TEST(from_cents(688) == loads_.monthly_policy_fee 
(mce_gen_mdpt)[0] , file, line);
+#   else  // !defined CURRENCY_UNIT_IS_CENTS
+    INVOKE_BOOST_TEST(materially_equal(6.88, dblize(loads_.monthly_policy_fee 
(mce_gen_mdpt)[0])), file, line);
+#   endif // !defined CURRENCY_UNIT_IS_CENTS
+    INVOKE_BOOST_TEST(from_cents(150) == loads_.annual_policy_fee  
(mce_gen_mdpt)[0] , file, line);
     INVOKE_BOOST_TEST(materially_equal(0.000625, loads_.specified_amount_load 
(mce_gen_mdpt)[0]), file, line);
     // 12 bp and 19 bp, both converted to monthly, then added together.
     INVOKE_BOOST_TEST(materially_equal(0.0002581402795930, 
loads_.separate_account_load (mce_gen_mdpt)[0]), file, line);
@@ -193,11 +198,11 @@ int test_main(int, char*[])
 {
     round_to<double> round_interest_rate(0, r_not_at_all);
     round_to<double> round_minutiae     (2, r_to_nearest);
-    std::vector<double> extra_comp_load  (length, 0.0170);
-    std::vector<double> extra_asset_comp (length, 0.0019);
-    std::vector<double> extra_policy_fee (length, 0.6000);
-    std::vector<double> guar_specamt_load(length, 0.0007);
-    std::vector<double> curr_specamt_load(length, 0.0005);
+    std::vector<double>   extra_comp_load  (length, 0.0170);
+    std::vector<double>   extra_asset_comp (length, 0.0019);
+    std::vector<currency> extra_policy_fee (length, from_cents(50));
+    std::vector<double>   guar_specamt_load(length, 0.0007);
+    std::vector<double>   curr_specamt_load(length, 0.0005);
 
     load_details details
         (length                 // length_



reply via email to

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