This page documents the complete step-by-step logic used in the IUL projection engine, from input data to final ledger output. All formulas, table lookups, and mechanics are described in detail, matching the codebase and README.
issue_age, gender, rate_classinitial_face_amount, death_benefit_option,
policy_end_age, corridor_factors, coi_table,
initial_db_option, db_option_b_end_yearmodal_premium_amount, annual_premium_outlay,
funding_start_policy_year, funding_end_policy_year,
premium_stop_yearweighted_average_interest_rate,
current_par_rate, guaranteed_min_interest_rate,
asset_charge_annual_pct, surrender_charge_pct_by_year,
surrender_charge_period_endillustrated_loan_ratelibr_start_policy_year, planned_annual_incomeAll fields are validated and used to drive the projection. Optional override tables (COI, corridor, surrender) can be injected for calibration.
_coi_rate_by_age_base(age) = max(0.001, 0.0008 + (age - 20) * 0.00009) * COI_FALLBACK_SCALE
av_unloaned = 0.0, av_collateral = 0.0,
loan_balance = 0.0, lapsed = Falseage = issue_age + policy_year - 1gross_annual_prem = modal_premium_amount * 12gross_monthly_prem = gross_annual_prem / 12policy_year <= funding_end_policy_year
base_rate_annual = weighted_average_interest_rate (or
guaranteed_min_interest_rate)bonus = interest_bonus_yrs_2_9 (years 2-9) or
interest_bonus_yrs_10_plus (years 10+)credited_rate_annual = base_rate_annual + bonus
loan_credit_rate_annual = loan_rate_annual - 0.005 (years 1-10), else
loan_rate_annual
loan_rate_monthly = loan_rate_annual / 12av_charge_monthly_rate = av_charge_annual / 12
coi_rate_annual = COI table[age] if provided, else fallback curvecoi_rate_monthly = coi_rate_annual / 12
monthly_rider_charge = (RIDER_CHARGE_PER_1000_ANNUAL * face / 1000) / 12
fixed_charges_monthly = MONTHLY_POLICY_FEE + MONTHLY_EXPENSE_CHARGE
av_total <= 0 and loan_balance <= 0 and
gross_monthly_prem <= 0, set lapsed = Truepremium_load = gross_monthly_prem * PREMIUM_LOAD_PCTnet_premium = gross_monthly_prem - premium_loadav_unloaned += net_premium
gross_db_month = max(DB option value, av_total * corridor_factor(age))nar = max(0, gross_db_month - av_total)
coi_charge = nar * coi_rate_monthlyav_charge = av_total * av_charge_monthly_ratemonthly_total_charges = fixed_charges_monthly + monthly_rider_charge + coi_charge + av_chargeav_unloaned first, then av_collateral. If
insufficient, set lapsed = True
loan_balance *= (1 + loan_rate_monthly) (if loan_balance > 0)
av_unloaned *= (1 + credited_rate_annual)av_collateral *= (1 + loan_credit_rate_annual)
policy_year >= libr_start_policy_year):planned_annual_loan = planned_annual_income * (1 + LOAN_PLANNED_BUFFER_PCT)loan_balance += planned_annual_loantransfer = min(planned_annual_loan, av_unloaned)av_unloaned -= transferav_collateral += transfer
surrender_charge_pct = surrender_charge_pct_by_year[policy_year] if provided, else
defaultsurrender_charge = surrender_charge_pct * av_end_yearcsv = max(0, av_end_year - surrender_charge - loan_balance)csv = max(csv, minimum_cash_surrender_value)
ndb = max(gross_db_year_end - loan_balance, minimum_net_death_benefit if in income phase)
year, age, premium, premium_load, net_premium, fixed_charges, coi_rate_annual, coi_charge_annual, av_begin, av_end, loan_balance, planned_annual_loan, csv, ndb
build_7702_corridor(), build_surrender_table_from_av_csv(),
infer_coi_table()calculations.py.