Published
Edited
Jan 18, 2020
5 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
calculatedColaPercent = getAggregateColaFactor(yearOf62yo, retireDatePicked)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
perYearEarnings
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
parsedXmlFileText
Insert cell
Insert cell
Insert cell
function getSubstantialEarningsYears(earnings, substantialEarnings) {
// NOTE: Assumption - years of substantial earnings includes years where the earnings amount is equal to the substantial earnings value?
let years = 0;
let substantialEarningsMap = {};
if (Array.isArray(substantialEarnings)) {
substantialEarnings.forEach((earning) => {
substantialEarningsMap[earning.year]= earning.SubstantialEarnings;
});
}
let earningsYears = Object.keys(earnings);
if (Array.isArray(earningsYears)) {
earningsYears.forEach((earningsYear) => {
if (earnings[earningsYear] >= substantialEarningsMap[earningsYear]) {
years++;
}
});
}
if (years === 0) {
//MAYBE: default to 20 if no data
return 20
}
return years;
}
Insert cell
Insert cell
function getAIME(mpb, dob, retireDate) {
//INCORPORATE ME:
const yearOf62yo = dayjs(dob).add(62, 'years').year()
const pia = mpb / getBenefitReduction(dob, retireDate);
const bendP = [findBendPoints(dob).FirstDollarAmtPIA, findBendPoints(dob).SecondDollarAmtPIA];
let aime;
if (pia > bendP[0] * 0.9 + (bendP[1] - bendP[0]) * 0.32) {
aime =
bendP[1] +
(pia - (bendP[0] * 0.9 + bendP[1] * 0.32)) / 0.15;
} else if (pia > bendP[0] * 0.9) {
aime = bendP[0] + (pia - bendP[0] * 0.9) / 0.32;
} else {
aime = pia / 0.9;
}
return aime;
}
Insert cell
Insert cell
Insert cell
Insert cell
function getAggregateColaFactor(eligibilityYear, retireYearDate) {
//eligibilityYear is already a numberical year, not a date
const retireYear = dayjs(retireYearDate).year() //remember the year() here
//You're retiring before you're eligible for COLA
if (retireYear < eligibilityYear + 1 ) {
return 1;
}
// Always use eligibilityYear plus 1, when COLA begins to be applied.
const colaFactors = getColaFactors(eligibilityYear + 1, retireYear);

const reducer = (accumulator, current) => accumulator * (100 + current) * 0.01;
const aggColaIncRate = colaFactors.reduce(reducer, 100) * 0.01;

return aggColaIncRate;
}
Insert cell
Insert cell
function getColaFactors(yearStart, yearEnd) {

const colaTableFilter = ColaTable.filter(d => d.year >= yearStart && d.year <= yearEnd).map(d => d.Cola)

return colaTableFilter;
}
Insert cell
Insert cell
function getBenefitReduction(dob, retireDate) {
const birthYearDate = dayjs(dob);
const retireYearDate = dayjs(retireDate);
//Difference .diff(compared: Dayjs, unit: string (default: 'milliseconds'), float?: boolean)
const floatYearsAfter62yo = retireYearDate.diff(birthYearDate,'years',true)-62
const intYearsAfter62yo = retireYearDate.diff(birthYearDate,'years',false)-62
let reduction;
const rowOfReduction = benefitReductionTable.find(d => d.year === birthYearDate.year());
if (rowOfReduction) {
//calculate: that retirement year's credit per year / fraction of the extra year.
// Remember PctCreditForEachDelayYear is in whole numbers like 9.333%, not 0.09333
//TODO: extra days of the month should never affect floatYearsAfter62yo when retirement date set directly.
let extraMonthsCredit = 0
if ((floatYearsAfter62yo - intYearsAfter62yo) !== 0) {
const fractionOfYearMonths = (floatYearsAfter62yo - intYearsAfter62yo);
extraMonthsCredit = (rowOfReduction.PctCreditForEachDelayYear*0.01) * fractionOfYearMonths;
//console.log("on ",rowOfReduction.yearsFrom62[intYearsAfter62yo]," give them ",extraMonthsCredit," because ", fractionOfYearMonths," and ", rowOfReduction.PctCreditForEachDelayYear*0.01)
}
//sum basic calculation and extra month's credit
return rowOfReduction.yearsFrom62[intYearsAfter62yo] + extraMonthsCredit;
} else {
throw new Error("No reduction found – try experimental table");
}
}
Insert cell
function findBendPoints(dob) {
const yearOf62yo = dayjs(dob).add(62, 'years').year()
return bendPoints.find(d => d.year === yearOf62yo);
}
Insert cell
Insert cell
function getPIA(aime, dob, yearsSubstantialEarnings=null, isWEP=true) {
if (findBendPoints(dob) === undefined) {
throw new Error("Too young – bend points not yet set?")
}
//unpack the two bend points from the object into an array
const bendP = [findBendPoints(dob).FirstDollarAmtPIA, findBendPoints(dob).SecondDollarAmtPIA];
//between 40% and 90% factor is calculated
const firstFactor = firstPiaFactor(yearsSubstantialEarnings, isWEP);

let pia;
if (aime > bendP[1]) {
pia =
firstFactor * bendP[0] +
0.32 * (bendP[1] - bendP[0]) +
0.15 * (aime - bendP[1]);
} else if (aime > bendP[0]) {
pia = firstFactor * bendP[0] + 0.32 * (aime - bendP[0]);
} else {
pia = firstFactor * aime;
}
return pia;
}
Insert cell
Insert cell
function getWepMPB (aime, dob, retireDate, yearsSubstantialEarnings, pensionNonCoveredMonthly) {
const standardPIA = getPIA(aime, dob, null, false);
const wepPIA = getPIA(aime, dob, yearsSubstantialEarnings, true);
const wepDiff = standardPIA - wepPIA;
const wepReduction = getGuaranteeLimit(wepDiff, pensionNonCoveredMonthly);
const finalPIA = standardPIA - wepReduction;
const retireYear = dayjs(retireDate).year();
const mpb = finalPIA * getAggregateColaFactor(yearOf62yo, retireYear) * getBenefitReduction(dob, retireDate);

return mpb;
}
Insert cell
Insert cell
//between 40% and 90% factor is calculated inside wepCoeff
function firstPiaFactor(yse, isWEP) {
if (!isWEP) {
return 0.9
} else if (yse === null) {
throw new Error("Tally of years of substantial earnings are needed for WEP PIA factor")
}
//## SU: this "hardcoded" approach is obviously not ideal
let wep_coeff = .4
if (yse <= 20) { wep_coeff = .4 }
if (yse == 21) { wep_coeff = .45}
if (yse == 22) { wep_coeff = .5}
if (yse == 23) { wep_coeff = .55}
if (yse == 24) { wep_coeff = .6 }
if (yse == 25) { wep_coeff = .65 }
if (yse == 26) { wep_coeff = .7}
if (yse == 27) { wep_coeff = .75}
if (yse == 28) { wep_coeff = .8}
if (yse == 29) { wep_coeff = .85}
if (yse >= 30) { wep_coeff = .9}
return wep_coeff
}

Insert cell
Insert cell
function getGuaranteeLimit(wepDiff, pension) {
const guaranteeLimit = pension / 2;
return Math.min(wepDiff, guaranteeLimit);
}
Insert cell
Insert cell
Insert cell
Insert cell
/* Download from left cell menu and paste this into cachedSocialSecTables if an updated cache is needed */
// uncheck use table cache first. then run this cell:
// recacheSocialSecTables= ({ColaTable, benefitReductionTable, substantialEarningsMarks, bendPoints, maximumEarningsCreditable, averageWageIndexTable, actuarialValueLumpSumTable,fullRetirementAgeTable})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
standardPIA = getPIA(AIMEPicked, birthDatePicked, null, false).toFixed(2)
Insert cell
wepPIA = getPIA(AIMEPicked, birthDatePicked, yearsSubstantialEarningsPicked, true).toFixed(2)
Insert cell
wepDifference = (getPIA(AIMEPicked, birthDatePicked, null, false)-getPIA(AIMEPicked, birthDatePicked, yearsSubstantialEarningsPicked, true)).toFixed(2)
Insert cell
wepMPB = getWepMPB(AIMEPicked, birthDatePicked, retireDatePicked, yearsSubstantialEarningsPicked, pensionNonCoveredMonthly).toFixed(2)
Insert cell
eligibilityText = {
const v = ageToRetirePicked;
const indexedAge = benefitReductionTable.find(d => d.year === dayjs(birthDatePicked).year()).NormalRetirementAge
const phrases = [
' is younger than full retirement age: penalty applies',
' is at least full retirement age, potential bonus',
];
return v + phrases[v < benefitReductionTable.find(d => d.year === dayjs(birthDatePicked).year()).NormalRetirementAge ? 0 : 1] + " (" + indexedAge.toFixed(2) + " years old)"
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more