[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_
- [lmi-commits] [lmi] master updated (a111350 -> 9d991b0), Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 6d719d2 10/15: Refactor an assertion, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 2084088 09/15: Record speed measurements, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master da240e8 03/15: Eliminate a return code that was always zero, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master d280c59 05/15: Fix a unit test, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 7b51fab 07/15: Adapt to currency class, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master f7a1129 08/15: Store policy fees as currency,
Greg Chicares <=
- [lmi-commits] [lmi] master 2666c48 06/15: Test rounding double to currency, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 5ed8f75 04/15: Refactor a unit test, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 6a6ebe3 11/15: Adapt to currency class, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 1d5c923 01/15: Change some internal names, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 1e5d1ea 12/15: Adapt to currency class, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 27bd748 13/15: Write $0.00 as a currency rather than floating-point constant, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 9d991b0 15/15: Round currency as such, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master 306ba4e 02/15: Introduce optional (for now) currency class, Greg Chicares, 2021/01/25
- [lmi-commits] [lmi] master fd106fc 14/15: Reformat, Greg Chicares, 2021/01/25