Step 1A — Illustration Inputs
Step 1A — Define Illustration Inputs¶
Purpose of This Step
Before any illustration logic can be written, we must define a single, consistent input object that represents all assumptions needed to run a MYGA illustration.
In production systems, this input layer is critical for: - standardization - validation - separation of concerns
This step establishes the contract between the user interface and the illustration engine.
What This Step Does¶
This step defines an IllustrationInputs structure that:
- captures all policy- and product-level assumptions
- standardizes withdrawal behavior inputs
- optionally supports MVA-related inputs
- validates inputs before projection begins
All downstream steps (projection, withdrawals, MVA, surrender value, reserves) will depend on this object.
Business Requirements (BRD)¶
1. Input Categories¶
The illustration inputs must cover the following categories:
| Category | Fields | Purpose |
|---|---|---|
| Policy Info | premium, issue_age, gender | drives duration and future extensions |
| Product ID | product_code | selects default schedules / terms |
| Crediting | initial_rate, renewal_rate | MYGA growth mechanics |
| Withdrawals | method + value | models policyholder behavior |
| Projection Control | projection_years | determines output horizon |
| MVA Controls | index rates, overrides | supports MVA calculation |
2. Required vs Optional Fields¶
Required fields (no defaults):
| Field | Description |
|---|---|
| product_code | identifies MYGA product |
| premium | single premium amount |
| issue_age | age at issue |
| gender | used for later extensions |
| initial_rate | guaranteed rate |
| renewal_rate | post-guarantee rate |
| withdrawal_method | behavior model |
Optional fields (with defaults):
| Field | Default | Notes |
|---|---|---|
| projection_years | 15 | 0 means “use product default” later |
| withdrawal_value | 0.0 | interpreted by withdrawal method |
| mva_* fields | None | optional for early steps |
3. Withdrawal Method Standardization¶
Withdrawal behavior must be expressed using standardized methods.
| Method | Meaning | Example |
|---|---|---|
| pct_of_boy_av | % of BOY account value | 5% of AV |
| fixed_amount | fixed dollar withdrawal | $2,000 |
| prior_year_interest_credited | interest-only withdrawal | last year’s interest |
This avoids hardcoding behavior into the engine.
4. Validation Rules (Minimum)¶
The input object must enforce basic validity:
| Rule | Reason |
|---|---|
| premium > 0 | avoid invalid policies |
| issue_age ∈ [0,120] | realistic bounds |
| rates ≥ 0 | no negative rates |
| projection_years ≥ 0 | 0 = product default |
| withdrawal_value ≥ 0 | no negative withdrawals |
| pct_of_boy_av ≤ 10% | align with free withdrawal |
Validation should occur before any projection logic runs.
5. Role in the Illustration Engine¶
The illustration engine will later use these inputs as follows:
| Input | Used For |
|---|---|
| initial_rate / renewal_rate | crediting logic |
| withdrawal_method / value | BOY withdrawal calculation |
| projection_years | number of rows |
| mva_* | MVA factor and adjustment |
This makes Step 1A the foundation layer of the entire project.
Starter Code (What You Are Expected to Write)¶
Below is a starter implementation you can build on. You are encouraged to extend validation and structure over time.
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional, Literal
WithdrawalMethod = Literal[
"pct_of_boy_av",
"fixed_amount",
"prior_year_interest_credited",
]
GenderCategory = Literal["M", "F"]
@dataclass(frozen=True)
class IllustrationInputs:
# --- required fields ---
product_code: str
premium: float
issue_age: int
gender: GenderCategory
initial_rate: float
renewal_rate: float
withdrawal_method: WithdrawalMethod
# --- optional / default fields ---
projection_years: int = 15
withdrawal_value: float = 0.0
# optional MVA inputs
mva_initial_index_rate: Optional[float] = None
mva_current_index_rate: Optional[float] = None
mva_months_remaining_override: Optional[int] = None
def validate(self) -> None:
if self.premium <= 0:
raise ValueError("premium must be > 0")
if not (0 <= self.issue_age <= 120):
raise ValueError("issue_age must be between 0 and 120")
if self.initial_rate < 0 or self.renewal_rate < 0:
raise ValueError("rates must be >= 0")
if self.projection_years < 0:
raise ValueError("projection_years must be >= 0")
if self.withdrawal_value < 0:
raise ValueError("withdrawal_value must be >= 0")
if self.withdrawal_method == "pct_of_boy_av":
if not (0.0 <= self.withdrawal_value <= 0.10):
raise ValueError(
"pct_of_boy_av withdrawal_value must be between 0 and 0.10"
)
Example Usage¶
inputs = IllustrationInputs(
product_code="MYGA_5",
premium=100_000,
issue_age=60,
gender="M",
initial_rate=0.045,
renewal_rate=0.035,
withdrawal_method="pct_of_boy_av",
withdrawal_value=0.05,
)
inputs.validate()
Deliverable for Step 1A¶
By the end of this step, you should have:
- a validated
IllustrationInputsobject - standardized withdrawal methods
- basic input validation
- a reproducible way to pass assumptions into the illustration engine
This object will be reused in every subsequent step of the project.