lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master b0e9416 4/5: Record a negative experiment


From: Greg Chicares
Subject: [lmi-commits] [lmi] master b0e9416 4/5: Record a negative experiment
Date: Thu, 6 Jun 2019 08:24:17 -0400 (EDT)

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

    Record a negative experiment
    
    This commit will soon be reverted. Its purpose is to demonstrate an
    alternative approach, to discuss why it would be a mistake, and to
    explain insights of enduring value that might not otherwise have been
    gained.
---
 verify_products.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/verify_products.cpp b/verify_products.cpp
index 86b9524..cc476c9 100644
--- a/verify_products.cpp
+++ b/verify_products.cpp
@@ -53,8 +53,18 @@ void verify_one_cell
     input["Smoking"    ] = smoking;
 
     int const min_age = 
product_database(yare_input(input)).query<int>(DB_MinIssAge);
+    input["UseDOB"     ] = "No"; // Prevent Reconcile() from changing age.
     input["IssueAge"   ] = value_cast<std::string>(min_age);
 
+#if 0
+    input.Reconcile();
+    LMI_ASSERT(product_name == input["ProductName"].str());
+    LMI_ASSERT(gender       == input["Gender"     ].str());
+    LMI_ASSERT(smoking      == input["Smoking"    ].str());
+    LMI_ASSERT("No"         == input["UseDOB"     ].str());
+    LMI_ASSERT(min_age      == input.issue_age());
+#endif // 0
+
     yare_input       const yi(input);
     product_database const db(yi);
     auto const era    = db.query<oenum_cso_era   >(DB_CsoEra);
@@ -81,6 +91,90 @@ void verify_one_cell
         return;
         }
 
+    input.Reconcile();
+    if(product_name != input["ProductName"].str())
+        std::cout << product_name << " != " << input["ProductName"].str() << 
std::endl;
+    if(gender       != input["Gender"     ].str())
+        std::cout << gender       << " != " << input["Gender"     ].str() << 
std::endl;
+    if(smoking      != input["Smoking"    ].str())
+        std::cout << smoking      << " != " << input["Smoking"    ].str() << 
std::endl;
+    if("No"         != input["UseDOB"     ].str())
+        std::cout << "No"         << " != " << input["UseDOB"     ].str() << 
std::endl;
+    if(min_age      != input.issue_age()         )
+        std::cout << min_age      << " != " << input.issue_age() << std::endl;
+
+    // These four distinct diagnostics are observed with about a
+    // hundred real-world products:
+    //
+    // Female != Unisex
+    // Male != Unisex
+    // Unisex != Female
+    // Unismoke != Smoker
+    //
+    // All seem harmless:
+    //
+    // female or male forced to unisex: This occurs when
+    //   Add({DB_Irc7702QAxisGender  , true});
+    //   Add({DB_AllowSexDistinct    , false});
+    //   Add({DB_AllowUnisex         , true});
+    // i.e., for products that are issued only on a unisex basis,
+    // but which have 7702 tables for male and female as well as
+    // unisex, merely because it seemed expedient to copy the whole
+    // multidimensional set. The sex-distinct tables are superfluous
+    // and will never be read, so this oddity is benign.
+    //
+    // unisex forced to female: This occurs when
+    //   Add({DB_Irc7702QAxisGender  , true});
+    //   Add({DB_AllowSexDistinct    , [everywhere but MT]});
+    //   Add({DB_AllowUnisex         , false});
+    // so that all insureds are either male or female (except in MT),
+    // while unisex is forbidden (even in MT--so such a product cannot
+    // be issued in MT at all), yet unisex 7702 tables are provided.
+    // Those extra (unisex) tables are superfluous but harmless.
+    //
+    // unismoke forced to smoker: This occurs when
+    //   Add({DB_Irc7702QAxisSmoking , true});
+    //   Add({DB_AllowSmokeDistinct  , true});
+    //   Add({DB_AllowUnismoke       , [conditionally true or false]});
+    // so that all insureds are either smoker or nonsmoker (except,
+    // say, for guaranteed-issue underwriting), and in the particular
+    // scenario tested (say, medical underwriting), unisex is entered
+    // but forced to change to a different classification (smoker,
+    // only because it's the first available enumerator). Unisex 7702
+    // rates are provided, and might be required in a different
+    // scenario, so all is well.
+    //
+    // Conclusions.
+    //
+    // Reconcile() should not be called here at all. Products have
+    // two different sets of gender axes, one for underwriting, and
+    // another for 7702. Those axes needn't be the same: e.g., a
+    // product may be issued only on a sex-distinct basis, yet use
+    // unisex 7702 tables--to stay within IRS Notice 88-128's safe
+    // harbor, or to use more liberal rates for one market segment
+    // at the cost of disadvantaging another. The smoking axes may
+    // differ likewise. Calling Reconcile() enforces the underwriting
+    // axes, but it is the 7702 axes that are tested here.
+    //
+    // DB_Irc7702QAxisGender and DB_Irc7702QAxisSmoking are not
+    // adequate to describe all permissible variations; arguably
+    // something beyond that simple boolean pair would be desirable,
+    // e.g., because they cannot represent a smoker-distinct-only
+    // product that needs no unismoke tables. In practice, though,
+    // unismoke tables generally are provided, even when superfluous.
+    // Furthermore, while some products may not allow unisex rating,
+    // an exception is usually made for MT, so a more complicated
+    // paradigm does not seem practically necessary.
+    //
+    // It is often convenient to provide a full set of 7702 q tables
+    // even for a product that cannot use some of them: ideally, a
+    // single set of {1980, 2001, 2017} X {ALB, ANB} tables could
+    // then be shared by all products. The presence of superfluous
+    // tables is not an anomaly, and product verification need take
+    // no note of it. (It is not generally possible to share 7PP and
+    // corridor tables tables across products, though, because those
+    // tables depend on maturity age.)
+
     std::vector<double> const v0 = cso_table
         (era
         ,oe_orthodox



reply via email to

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