Skip to content

Commit

Permalink
Correct the survivor benefit calculation + tests. (#351)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregable authored Mar 20, 2024
1 parent 68388d3 commit 878e89a
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 23 deletions.
97 changes: 97 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,3 +600,100 @@ export const FULL_RETIREMENT_AGE: Array<{
delayedIncreaseAnnual: 0.08,
},
];

/**
* Full retirement age (FRA) for survivor benefits for each year of birth.
* - `minYear` and `maxYear` are inclusive
* - `ageYears` and `ageMonths` are the age at FRA.
* My understanding is that these dates are all just +2 years from the
* corresponding retirement age.
*/
export const FULL_RETIREMENT_AGE_SURVIVOR: Array<{
minYear: number;
maxYear: number;
ageYears: number;
ageMonths: number;
}> =
/* https://www.ssa.gov/pubs/EN-05-10084.pdf
* https://www.ssa.gov/benefits/survivors/survivorchartred.html
*/
[
{
minYear: 0,
maxYear: 1939,
ageYears: 65,
ageMonths: 0,
},
{
minYear: 1940,
maxYear: 1940,
ageYears: 65,
ageMonths: 2,
},
{
minYear: 1941,
maxYear: 1941,
ageYears: 65,
ageMonths: 4,
},
{
minYear: 1942,
maxYear: 1942,
ageYears: 65,
ageMonths: 6,
},
{
minYear: 1943,
maxYear: 1943,
ageYears: 65,
ageMonths: 8,
},
{
minYear: 1944,
maxYear: 1944,
ageYears: 65,
ageMonths: 10,
},
{
minYear: 1945,
maxYear: 1956,
ageYears: 66,
ageMonths: 0,
},
{
minYear: 1957,
maxYear: 1957,
ageYears: 66,
ageMonths: 2,
},
{
minYear: 1958,
maxYear: 1958,
ageYears: 66,
ageMonths: 4,
},
{
minYear: 1959,
maxYear: 1959,
ageYears: 66,
ageMonths: 6,
},
{
minYear: 1960,
maxYear: 1960,
ageYears: 66,
ageMonths: 8,
},
{
minYear: 1961,
maxYear: 1961,
ageYears: 66,
ageMonths: 10,
},
{
minYear: 1962,
maxYear: 10000,
ageYears: 67,
ageMonths: 0,
},
];
60 changes: 57 additions & 3 deletions src/lib/recipient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ export class Recipient {
*/
private normalRetirementAge_: MonthDuration;

/**
* The recipient's normal retirement age for survivor benefits.
*
* Recalculated when birthdate is updated.
*/
private survivorNormalRetirementAge_: MonthDuration;

/**
* The recipient's annual delayed retirement increase.
*
Expand Down Expand Up @@ -264,6 +271,14 @@ export class Recipient {
months: retirementAgeBracket.ageMonths,
});

const survivorRetirementAgeBracket = this.survivorRetirementAgeBracket();

// Recompute the survivor NRA based on the new birthdate.
this.survivorNormalRetirementAge_ = MonthDuration.initFromYearsMonths({
years: survivorRetirementAgeBracket.ageYears,
months: survivorRetirementAgeBracket.ageMonths,
});

// Recompute the delayed retirement increase based on the new birthdate.
this.delayedRetirementIncrease_ =
retirementAgeBracket.delayedIncreaseAnnual;
Expand Down Expand Up @@ -291,6 +306,28 @@ export class Recipient {
return retirementAgeBracket;
}

private survivorRetirementAgeBracket(): {
minYear: number;
maxYear: number;
ageYears: number;
ageMonths: number;
delayedIncreaseAnnual: number;
} {
// Find the retirement age bracket data for this recipient.
let retirementAgeBracket = undefined;
for (let i = 0; i < constants.FULL_RETIREMENT_AGE_SURVIVOR.length; ++i) {
let ageBracket = constants.FULL_RETIREMENT_AGE_SURVIVOR[i];
if (
this.birthdate_.ssaBirthYear() >= ageBracket.minYear &&
this.birthdate_.ssaBirthYear() <= ageBracket.maxYear
) {
retirementAgeBracket = ageBracket;
}
}
console.assert(retirementAgeBracket !== undefined);
return retirementAgeBracket;
}

/*
* Recipient's normal retirement age.
*/
Expand All @@ -311,6 +348,22 @@ export class Recipient {
.addDuration(this.normalRetirementAge());
}

/*
* Recipient's normal retirement age for survivor benefits.
*/
survivorNormalRetirementAge(): MonthDuration {
return this.survivorNormalRetirementAge_;
}

/*
* Recipient's normal retirement date for survivor benefits.
*/
survivorNormalRetirementDate(): MonthDate {
return this.birthdate_
.ssaBirthMonthDate()
.addDuration(this.survivorNormalRetirementAge());
}

/**
* The early retirement reduction factor changes from 6.67%/yr for years
* earlier than 3 years before normal retirement age to 5%/yr for the 3 years
Expand Down Expand Up @@ -691,7 +744,8 @@ export class Recipient {
if (deceasedFilingDate.greaterThanOrEqual(deceasedDeathDate)) {
// If the deceased recipient did not file for benefits before death:
if (deceasedDeathDate.lessThan(deceased.normalRetirementDate())) {
// If the deceased died before Normal Retirement Age, the survivor benefitis based on the deceased recipient's PIA.
// If the deceased died before Normal Retirement Age, the survivor
// benefit is based on the deceased recipient's PIA.
baseSurvivorBenefit = deceased.pia().primaryInsuranceAmount();
} else {
// If the deceased died after Normal Retirement Age, the survivor
Expand All @@ -718,10 +772,10 @@ export class Recipient {
// base amount based on the survivor's age between 60 and Full Retirement
// Age.
const survivorAge = this.birthdate.ageAtSsaDate(survivorFilingDate);
if (survivorAge.greaterThanOrEqual(this.normalRetirementAge())) {
if (survivorAge.greaterThanOrEqual(this.survivorNormalRetirementAge())) {
return baseSurvivorBenefit;
} else {
const monthsBetween60AndNRA = this.normalRetirementAge()
const monthsBetween60AndNRA = this.survivorNormalRetirementAge()
.subtract(MonthDuration.initFromYearsMonths({ years: 60, months: 0 }))
.asMonths();
const monthsBetweenAge60AndSurvivorAge = survivorAge
Expand Down
Loading

0 comments on commit 878e89a

Please sign in to comment.