Earnings -> AIME
Translate "working" getAIME() into React App or notebook.
If code works, localize to the correct demo screen page; move to Tests board -> getAIME() Test
More info on the formula: Formula: [(beneficiary’s earnings for the year being indexed) x (average wages of all workers for indexing year)]/[average wages of all worker for the year being indexed]
Please note that an individual's earnings are always indexed to the average wage level two years prior to the year of first eligibility. Thus, for a person retiring at age 62 in 2019, the person's earnings would be indexed to the average wage index for 2017 ($50,321.89). There is no rounding in each year’s indexed earnings. The ending monthly average, AIME, is rounded down to the nearest $1.00 (see Social Security Act § 215(e)(2); 20 CFR § 404.211(f)(3)).
For comparison, the primary insurance amount (PIA) is rounded down to the nearest dime (Social Security Act § 215(a)(1)(A); 20 CFR § 404.212(c)). The PIA is calculated based on the bend points in the year that a worker attains age 62, becomes disabled or dies.
Indexing table: https://www.ssa.gov/oact/cola/awifactors.html @supjohn
Sebastian contributed this draft javascript version of the algorithm to me earlier this week:
/*
* Calculates the beneficiary's AIME from their earnings records
* Unlike the getAIME() function, this function generates an exact number,
* rather than a ballpark estimate
* Parameters: earnings, an array of objects containing a year and the amount earned.
* indexingYear, the second year before the beneficiary's year of attainment of age 62
* indexTable, an array of objects containing a year, the maximum earnings creditable for that year,
* and the average earnings across all workers in that year
* Returns: AIME, representing the user's AIME, which is rounded down to an integer
*/
function getAIMEFromEarnings(earnings, indexingYear, indexTable)
{
let earningsMap = {}; //This map will contain key-value pairs of year-amount earned
if (Array.isArray(earnings)) {
earnings.forEach((earning) => {
earningsMap[earning.year] = earning.amount;
});
} //else return error?
let averageMap = {}; //This map will contain key-value pairs of year-average earnings
if (Array.isArray(indexTable)) {
earnings.forEach((earning) => {
if (earning,year > 1950 && earning.year > indexingYear-39 && earning.year < indexingYear+2) { //only years after 1950, after the beneficiary turned 21, and before the beneficiary turned 62
averageMap[earning.year] = earning.average;
}
});
}//else return error?
let maximumMap = {}; //This map will contain key-value pairs of year-maximum
if (Array.isArray(indexTable)) {
earnings.forEach((earning) => {
if (earning,year >= 1950 && earning.year >= indexingYear-39 && earning.year < indexingYear+2) { //only years after 1950, after the beneficiary turned 21, and before the beneficiary turned 62
maximumMap[earning.year] = earning.maximum;
}
});
}//else return error?
//Check that averageMap and maximumMap have the same length and set of keys?
//adjust each year's earning to match the indexing year, then add the modified yearly earnings to an array
let earningsYears = Object.keys(indexTable);
let validEarnings = {}; //array of each year of valid earnings;
// specific year or ordering ceases to be relevant
if (Array.isArray(earningsYears)) {
earningsYears.forEach((earningsYear) => {
if (earningsYear != indexingYear) {
validEarnings.push(Math.min(earningsMap[earningsYear], maximumMap[earningsYear])*averageMap[indexingYear]/averageMap[earningsYear]);
}
else {
validEarnings.push(Math.min(earningsMap[earningsYear], maximumMap[earningsYear])); //I'm not sure if this correctly handles situations where a year is missing in earningsMap
}
});
}
//Remove the 5 lowest members of validEarnings
let i;
for (i = 0; i < 5; i++){
if (validEarnings.length = 0) break;
let min = validEarnings[0];
let mindex = 0;
if (validEarnings.length > 1) {
let j;
for (j = 1; j < validEarnings.length; j++){
if (validEarnings[j] < min) {
mindex = j;
min = validEarnings[j];
}
}
}
validEarnings.splice(j,1);
}
//Sum the earning into AIME
let AIME = 0; //Starts at 0, to facilitate later summation
validEarnings.forEach((earning) => {
AIME += earning;
});
//Divide AIME by number of months ()
AIME = AIME / (validEarnings.length*12);
return Math.floor(AIME);
}
I used it and integrated a modified version into the notebook today
Now it is in the notebook as getAIMEFromEarnings() with the value showing as calcedAIMEfromEarnings.
You can try it with the earnings from the Full Retirement (XML) example, but you have to set the fellow's correct birthdate, which is in 1947. His AIME is calculated to $3,347. We should do thorough code review of my code and also check that the implementation matches Anne's description above.
https://docs.google.com/spreadsheets/d/1hGYzX8F_LfgT2pUHjYgGm3FUKSUNQYctI4--VF17kMw/edit?usp=sharing
ALM's AIME calculation spreadsheet @thadk