Case Study 2: Social Security Benefits Calculation
Background
The Social Security Administration (SSA) maintains one of the largest and most consequential COBOL systems in the world. The agency processes benefit calculations for over 70 million Americans receiving retirement, disability, and survivor benefits, disbursing more than $1.4 trillion annually. The core benefit computation engine, running on IBM z/OS mainframes, determines the exact monthly payment for every beneficiary based on their lifetime earnings history.
The Social Security benefit calculation is one of the most complex financial computations performed by any government system. It involves indexing decades of historical earnings to account for wage growth, selecting the highest-earning years, computing a weighted average, and applying a progressive formula with "bend points" that change annually. The calculation must then be adjusted for the age at which the worker begins receiving benefits, with reductions for early retirement and credits for delayed retirement.
This case study implements a complete Social Security retirement benefit calculator in COBOL, covering the computation of Average Indexed Monthly Earnings (AIME), the Primary Insurance Amount (PIA) using bend points, and early/late retirement adjustment factors. The program demonstrates how COBOL's packed-decimal arithmetic handles the precise, rule-driven calculations that determine benefits for millions of Americans.
Business Requirements
The Social Security benefit calculator must implement the following:
-
Earnings indexing: Adjust each year's earnings by a wage index factor to express past earnings in terms of current wage levels. Earnings are indexed to the worker's age 60, using the national average wage index.
-
AIME calculation: Select the highest 35 years of indexed earnings, sum them, and divide by 420 (35 years * 12 months) to produce the Average Indexed Monthly Earnings. Drop fractions from the result.
-
PIA calculation: Apply the PIA formula using two "bend points" that create a progressive benefit structure: - 90% of the first bend point amount of AIME - 32% of AIME between the first and second bend points - 15% of AIME above the second bend point
The PIA is rounded down to the next lower dime (10 cents).
-
Early retirement reduction: For workers claiming before their Full Retirement Age (FRA), reduce the PIA by 5/9 of 1% per month for the first 36 months early, and 5/12 of 1% per month for each additional month beyond 36.
-
Delayed retirement credits: For workers claiming after their FRA, increase the PIA by 2/3 of 1% per month (8% per year) for each month of delay, up to age 70.
The Social Security Benefit Formula
The Social Security benefit formula was designed to be progressive: workers with lower lifetime earnings receive a higher percentage of their earnings as benefits than higher-earning workers. This is accomplished through the bend point structure of the PIA formula.
For a worker turning 62 in 2024, the bend points are: - First bend point: $1,174 - Second bend point: $7,078
These bend points are adjusted annually based on changes in the national average wage index. The formula applies three different replacement rates to three segments of the AIME:
- 90% of the first $1,174 of AIME = maximum $1,056.60 from this segment
- 32% of AIME between $1,174 and $7,078 = maximum $1,889.28 from this segment
- 15% of AIME above $7,078
The sum of these three components is the PIA, which represents the monthly benefit at Full Retirement Age.
Complete COBOL Program
IDENTIFICATION DIVISION.
PROGRAM-ID. SSABENCALC.
AUTHOR. SSA BENEFIT SYSTEMS.
DATE-WRITTEN. 2024-09-01.
*================================================================*
* PROGRAM: SSABENCALC - SOCIAL SECURITY BENEFIT CALCULATOR *
* *
* PURPOSE: Calculate Social Security retirement benefits using *
* the AIME/PIA formula with bend points and *
* early/late retirement adjustments. *
* *
* PROCESSING: *
* 1. Read worker earnings history *
* 2. Index earnings to age-60 wage levels *
* 3. Select highest 35 years of indexed earnings *
* 4. Calculate AIME (Average Indexed Monthly Earnings) *
* 5. Calculate PIA (Primary Insurance Amount) *
* 6. Apply early/late retirement adjustment *
* 7. Produce benefit computation report *
* *
* INPUT: EARNHIST - Worker earnings history file *
* OUTPUT: BENCALC - Benefit computation report *
*================================================================*
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION ALL INTRINSIC.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT EARNINGS-HISTORY
ASSIGN TO EARNHIST
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-EARN-STATUS.
SELECT BENEFIT-REPORT
ASSIGN TO BENCALC
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-RPT-STATUS.
DATA DIVISION.
FILE SECTION.
FD EARNINGS-HISTORY
RECORDING MODE IS F
RECORD CONTAINS 80 CHARACTERS.
01 EARNINGS-INPUT-REC.
05 EI-SSN PIC X(09).
05 EI-WORKER-NAME PIC X(25).
05 EI-BIRTH-YEAR PIC 9(04).
05 EI-CLAIM-AGE PIC 9(02).
05 EI-FRA-AGE-YEARS PIC 9(02).
05 EI-FRA-AGE-MONTHS PIC 9(02).
05 EI-RECORD-TYPE PIC X(01).
88 EI-HEADER-REC VALUE 'H'.
88 EI-EARNINGS-REC VALUE 'E'.
88 EI-TRAILER-REC VALUE 'T'.
05 EI-EARN-YEAR PIC 9(04).
05 EI-ACTUAL-EARNINGS PIC 9(07)V99.
05 EI-WAGE-INDEX PIC 9(03)V9(06).
05 EI-SS-MAX-EARNINGS PIC 9(07)V99.
05 FILLER PIC X(08).
FD BENEFIT-REPORT
RECORDING MODE IS F
RECORD CONTAINS 132 CHARACTERS.
01 REPORT-LINE PIC X(132).
WORKING-STORAGE SECTION.
*----------------------------------------------------------------*
* FILE STATUS *
*----------------------------------------------------------------*
01 WS-FILE-STATUS.
05 WS-EARN-STATUS PIC X(02).
05 WS-RPT-STATUS PIC X(02).
01 WS-FLAGS.
05 WS-EOF-FLAG PIC X(01) VALUE 'N'.
88 END-OF-FILE VALUE 'Y'.
88 NOT-END-OF-FILE VALUE 'N'.
05 WS-PROCESSING PIC X(01) VALUE 'N'.
88 PROCESSING-WORKER VALUE 'Y'.
88 NOT-PROCESSING VALUE 'N'.
*----------------------------------------------------------------*
* WORKER IDENTIFICATION *
*----------------------------------------------------------------*
01 WS-WORKER-INFO.
05 WS-SSN PIC X(09).
05 WS-WORKER-NAME PIC X(25).
05 WS-BIRTH-YEAR PIC 9(04).
05 WS-CLAIM-AGE PIC 9(02).
05 WS-FRA-AGE-YEARS PIC 9(02).
05 WS-FRA-AGE-MONTHS PIC 9(02).
*----------------------------------------------------------------*
* EARNINGS HISTORY TABLE *
* Stores up to 50 years of earnings records *
*----------------------------------------------------------------*
01 WS-EARNINGS-TABLE.
05 WS-EARN-COUNT PIC 9(02) VALUE ZEROS.
05 WS-EARN-ENTRY OCCURS 50 TIMES.
10 WS-ET-YEAR PIC 9(04).
10 WS-ET-ACTUAL PIC S9(07)V99 COMP-3.
10 WS-ET-CAPPED PIC S9(07)V99 COMP-3.
10 WS-ET-WAGE-INDEX PIC S9(03)V9(06) COMP-3.
10 WS-ET-INDEXED PIC S9(09)V99 COMP-3.
10 WS-ET-SS-MAX PIC S9(07)V99 COMP-3.
10 WS-ET-SELECTED PIC X(01).
88 ET-SELECTED VALUE 'Y'.
88 ET-NOT-SELECTED VALUE 'N'.
*----------------------------------------------------------------*
* INDEXING CALCULATION FIELDS *
*----------------------------------------------------------------*
01 WS-INDEX-FIELDS.
05 WS-AGE60-YEAR PIC 9(04).
05 WS-AGE60-WAGE-INDEX PIC S9(03)V9(06) COMP-3.
05 WS-INDEX-FACTOR PIC S9(05)V9(08) COMP-3.
05 WS-IDX-COUNTER PIC 9(02).
*----------------------------------------------------------------*
* AIME CALCULATION FIELDS *
*----------------------------------------------------------------*
01 WS-AIME-FIELDS.
05 WS-TOP35-TOTAL PIC S9(11)V99 COMP-3
VALUE ZEROS.
05 WS-AIME PIC S9(07) COMP-3.
05 WS-YEARS-SELECTED PIC 9(02) VALUE ZEROS.
05 WS-COMPUTATION-YEARS PIC 9(02) VALUE 35.
*----------------------------------------------------------------*
* PIA CALCULATION FIELDS *
*----------------------------------------------------------------*
01 WS-PIA-FIELDS.
05 WS-BEND-POINT-1 PIC S9(05) COMP-3
VALUE 1174.
05 WS-BEND-POINT-2 PIC S9(05) COMP-3
VALUE 7078.
05 WS-PIA-SEGMENT-1 PIC S9(07)V99 COMP-3.
05 WS-PIA-SEGMENT-2 PIC S9(07)V99 COMP-3.
05 WS-PIA-SEGMENT-3 PIC S9(07)V99 COMP-3.
05 WS-PIA-RAW PIC S9(07)V99 COMP-3.
05 WS-PIA-ROUNDED PIC S9(07)V9 COMP-3.
05 WS-PIA-FINAL PIC S9(07)V99 COMP-3.
*----------------------------------------------------------------*
* RETIREMENT ADJUSTMENT FIELDS *
*----------------------------------------------------------------*
01 WS-RETIRE-FIELDS.
05 WS-FRA-TOTAL-MONTHS PIC S9(04) COMP-3.
05 WS-CLAIM-TOTAL-MONTHS PIC S9(04) COMP-3.
05 WS-MONTHS-DIFF PIC S9(04) COMP-3.
05 WS-MONTHS-EARLY PIC S9(04) COMP-3.
05 WS-MONTHS-LATE PIC S9(04) COMP-3.
05 WS-EARLY-REDUCTION PIC S9V9(06) COMP-3.
05 WS-EARLY-FIRST-36 PIC S9(04) COMP-3.
05 WS-EARLY-OVER-36 PIC S9(04) COMP-3.
05 WS-LATE-CREDIT PIC S9V9(06) COMP-3.
05 WS-ADJUSTED-BENEFIT PIC S9(07)V99 COMP-3.
05 WS-ADJUSTMENT-DESC PIC X(30).
*----------------------------------------------------------------*
* SORTING FIELDS FOR TOP-35 SELECTION *
*----------------------------------------------------------------*
01 WS-SORT-FIELDS.
05 WS-SORT-IDX-1 PIC 9(02).
05 WS-SORT-IDX-2 PIC 9(02).
05 WS-SORT-TEMP-EARN PIC S9(09)V99 COMP-3.
05 WS-SORT-TEMP-YEAR PIC 9(04).
05 WS-SORT-TEMP-INDEX PIC S9(03)V9(06) COMP-3.
05 WS-SORT-TEMP-ACTUAL PIC S9(07)V99 COMP-3.
05 WS-SORT-TEMP-CAPPED PIC S9(07)V99 COMP-3.
05 WS-SORT-TEMP-MAX PIC S9(07)V99 COMP-3.
05 WS-SORT-TEMP-SEL PIC X(01).
*----------------------------------------------------------------*
* COUNTERS *
*----------------------------------------------------------------*
01 WS-WORKERS-PROCESSED PIC 9(05) VALUE ZEROS.
*----------------------------------------------------------------*
* REPORT LINES *
*----------------------------------------------------------------*
01 WS-RPT-TITLE.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(80)
VALUE 'SOCIAL SECURITY ADMINISTRATION - '
'RETIREMENT BENEFIT COMPUTATION'.
05 FILLER PIC X(51) VALUE SPACES.
01 WS-RPT-SEPARATOR.
05 FILLER PIC X(132) VALUE ALL '='.
01 WS-RPT-WORKER-HDR.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(06) VALUE 'SSN: '.
05 WWH-SSN PIC X(09).
05 FILLER PIC X(05) VALUE SPACES.
05 FILLER PIC X(07) VALUE 'NAME: '.
05 WWH-NAME PIC X(25).
05 FILLER PIC X(05) VALUE SPACES.
05 FILLER PIC X(13) VALUE 'BIRTH YEAR: '.
05 WWH-BIRTH-YEAR PIC 9(04).
05 FILLER PIC X(05) VALUE SPACES.
05 FILLER PIC X(12) VALUE 'CLAIM AGE: '.
05 WWH-CLAIM-AGE PIC Z9.
05 FILLER PIC X(38) VALUE SPACES.
01 WS-RPT-FRA-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(28)
VALUE 'FULL RETIREMENT AGE (FRA): '.
05 WFRA-YEARS PIC Z9.
05 FILLER PIC X(17)
VALUE ' YEARS AND '.
05 WFRA-MONTHS PIC Z9.
05 FILLER PIC X(07) VALUE ' MONTHS'.
05 FILLER PIC X(71) VALUE SPACES.
01 WS-RPT-BLANK PIC X(132) VALUE SPACES.
01 WS-RPT-EARN-HDR.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(06) VALUE 'YEAR '.
05 FILLER PIC X(18)
VALUE ' ACTUAL EARNINGS '.
05 FILLER PIC X(18)
VALUE ' CAPPED EARNINGS '.
05 FILLER PIC X(14)
VALUE ' WAGE INDEX '.
05 FILLER PIC X(14)
VALUE ' INDEX FACTOR '.
05 FILLER PIC X(18)
VALUE ' INDEXED EARNINGS '.
05 FILLER PIC X(10)
VALUE ' SELECTED '.
05 FILLER PIC X(33) VALUE SPACES.
01 WS-RPT-EARN-DASH.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(06) VALUE '------'.
05 FILLER PIC X(18) VALUE ALL '-'.
05 FILLER PIC X(18) VALUE ALL '-'.
05 FILLER PIC X(14) VALUE ALL '-'.
05 FILLER PIC X(14) VALUE ALL '-'.
05 FILLER PIC X(18) VALUE ALL '-'.
05 FILLER PIC X(10) VALUE ALL '-'.
05 FILLER PIC X(33) VALUE SPACES.
01 WS-RPT-EARN-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 WEL-YEAR PIC 9(04).
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-ACTUAL PIC $ZZZ,ZZ9.99.
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-CAPPED PIC $ZZZ,ZZ9.99.
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-WAGE-IDX PIC ZZ9.999999.
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-IDX-FACTOR PIC Z9.999999.
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-INDEXED PIC $Z,ZZZ,ZZ9.99.
05 FILLER PIC X(02) VALUE SPACES.
05 WEL-SELECTED PIC X(03).
05 FILLER PIC X(36) VALUE SPACES.
01 WS-RPT-AIME-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(45)
VALUE 'AVERAGE INDEXED MONTHLY EARNINGS (AIME): $'.
05 WAIME-AMOUNT PIC ZZZ,ZZ9.
05 FILLER PIC X(79) VALUE SPACES.
01 WS-RPT-PIA-HDR.
05 FILLER PIC X(01) VALUE SPACE.
05 FILLER PIC X(50)
VALUE 'PRIMARY INSURANCE AMOUNT (PIA) CALCULATION:'.
05 FILLER PIC X(81) VALUE SPACES.
01 WS-RPT-PIA-SEGMENT.
05 FILLER PIC X(01) VALUE SPACE.
05 WPS-DESC PIC X(55).
05 FILLER PIC X(02) VALUE '$ '.
05 WPS-AMOUNT PIC Z,ZZ9.99.
05 FILLER PIC X(65) VALUE SPACES.
01 WS-RPT-BENEFIT-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 WBL-DESC PIC X(55).
05 FILLER PIC X(02) VALUE '$ '.
05 WBL-AMOUNT PIC Z,ZZ9.99.
05 FILLER PIC X(65) VALUE SPACES.
PROCEDURE DIVISION.
*================================================================*
* MAIN CONTROL *
*================================================================*
0000-MAIN-CONTROL.
PERFORM 1000-INITIALIZE
PERFORM 2000-PROCESS-WORKERS
UNTIL END-OF-FILE
PERFORM 9000-FINALIZE
STOP RUN
.
*================================================================*
* INITIALIZE - OPEN FILES *
*================================================================*
1000-INITIALIZE.
OPEN INPUT EARNINGS-HISTORY
OPEN OUTPUT BENEFIT-REPORT
IF WS-EARN-STATUS NOT = '00'
DISPLAY 'ERROR OPENING EARNINGS FILE: '
WS-EARN-STATUS
SET END-OF-FILE TO TRUE
END-IF
* Write report title
WRITE REPORT-LINE FROM WS-RPT-TITLE
WRITE REPORT-LINE FROM WS-RPT-SEPARATOR
PERFORM 8000-READ-EARNINGS
.
*================================================================*
* PROCESS EACH WORKER *
*================================================================*
2000-PROCESS-WORKERS.
* Expect a header record first
IF EI-HEADER-REC
ADD 1 TO WS-WORKERS-PROCESSED
PERFORM 2100-LOAD-WORKER-HEADER
PERFORM 2200-LOAD-EARNINGS
PERFORM 3000-INDEX-EARNINGS
PERFORM 4000-SELECT-TOP-35
PERFORM 5000-CALCULATE-AIME
PERFORM 6000-CALCULATE-PIA
PERFORM 7000-APPLY-RETIREMENT-ADJ
PERFORM 7500-WRITE-REPORT
ELSE
PERFORM 8000-READ-EARNINGS
END-IF
.
*================================================================*
* LOAD WORKER HEADER INFORMATION *
*================================================================*
2100-LOAD-WORKER-HEADER.
MOVE EI-SSN TO WS-SSN
MOVE EI-WORKER-NAME TO WS-WORKER-NAME
MOVE EI-BIRTH-YEAR TO WS-BIRTH-YEAR
MOVE EI-CLAIM-AGE TO WS-CLAIM-AGE
MOVE EI-FRA-AGE-YEARS TO WS-FRA-AGE-YEARS
MOVE EI-FRA-AGE-MONTHS TO WS-FRA-AGE-MONTHS
* Calculate age-60 year for indexing
COMPUTE WS-AGE60-YEAR = WS-BIRTH-YEAR + 60
* Reset earnings table
MOVE ZEROS TO WS-EARN-COUNT
MOVE ZEROS TO WS-TOP35-TOTAL
MOVE ZEROS TO WS-YEARS-SELECTED
PERFORM 8000-READ-EARNINGS
.
*================================================================*
* LOAD ALL EARNINGS RECORDS FOR THIS WORKER *
*================================================================*
2200-LOAD-EARNINGS.
PERFORM UNTIL NOT EI-EARNINGS-REC
OR END-OF-FILE
ADD 1 TO WS-EARN-COUNT
MOVE EI-EARN-YEAR TO
WS-ET-YEAR(WS-EARN-COUNT)
MOVE EI-ACTUAL-EARNINGS TO
WS-ET-ACTUAL(WS-EARN-COUNT)
MOVE EI-WAGE-INDEX TO
WS-ET-WAGE-INDEX(WS-EARN-COUNT)
MOVE EI-SS-MAX-EARNINGS TO
WS-ET-SS-MAX(WS-EARN-COUNT)
* Cap earnings at the Social Security maximum
* for that year
IF WS-ET-ACTUAL(WS-EARN-COUNT) >
WS-ET-SS-MAX(WS-EARN-COUNT)
MOVE WS-ET-SS-MAX(WS-EARN-COUNT) TO
WS-ET-CAPPED(WS-EARN-COUNT)
ELSE
MOVE WS-ET-ACTUAL(WS-EARN-COUNT) TO
WS-ET-CAPPED(WS-EARN-COUNT)
END-IF
SET ET-NOT-SELECTED(WS-EARN-COUNT) TO TRUE
PERFORM 8000-READ-EARNINGS
END-PERFORM
* If we hit a trailer record, read past it
IF EI-TRAILER-REC
PERFORM 8000-READ-EARNINGS
END-IF
.
*================================================================*
* INDEX EARNINGS TO AGE-60 WAGE LEVELS *
* *
* Each year's capped earnings are multiplied by the ratio of *
* the national average wage index in the worker's age-60 year *
* to the national average wage index in the earnings year. *
* *
* Indexed Earnings = Capped Earnings * (Age60 Index / Year Index)*
* *
* Earnings in years at or after age 60 are not indexed; they *
* are used at their actual (capped) amount. *
*================================================================*
3000-INDEX-EARNINGS.
* Find the wage index for the age-60 year
* (the last entry with year <= age-60 year)
MOVE ZEROS TO WS-AGE60-WAGE-INDEX
PERFORM VARYING WS-IDX-COUNTER FROM 1 BY 1
UNTIL WS-IDX-COUNTER > WS-EARN-COUNT
IF WS-ET-YEAR(WS-IDX-COUNTER) = WS-AGE60-YEAR
MOVE WS-ET-WAGE-INDEX(WS-IDX-COUNTER)
TO WS-AGE60-WAGE-INDEX
END-IF
END-PERFORM
* If age-60 index not found, use the closest prior year
IF WS-AGE60-WAGE-INDEX = ZEROS
PERFORM VARYING WS-IDX-COUNTER
FROM WS-EARN-COUNT BY -1
UNTIL WS-IDX-COUNTER < 1
OR WS-AGE60-WAGE-INDEX > ZEROS
IF WS-ET-YEAR(WS-IDX-COUNTER)
< WS-AGE60-YEAR
MOVE WS-ET-WAGE-INDEX(WS-IDX-COUNTER)
TO WS-AGE60-WAGE-INDEX
END-IF
END-PERFORM
END-IF
* Index each year's earnings
PERFORM VARYING WS-IDX-COUNTER FROM 1 BY 1
UNTIL WS-IDX-COUNTER > WS-EARN-COUNT
IF WS-ET-YEAR(WS-IDX-COUNTER) < WS-AGE60-YEAR
* Index earnings for years before age 60
IF WS-ET-WAGE-INDEX(WS-IDX-COUNTER) > ZEROS
COMPUTE WS-INDEX-FACTOR =
WS-AGE60-WAGE-INDEX /
WS-ET-WAGE-INDEX(WS-IDX-COUNTER)
COMPUTE
WS-ET-INDEXED(WS-IDX-COUNTER)
ROUNDED =
WS-ET-CAPPED(WS-IDX-COUNTER)
* WS-INDEX-FACTOR
ON SIZE ERROR
DISPLAY 'SIZE ERROR INDEXING'
' YEAR '
WS-ET-YEAR(WS-IDX-COUNTER)
END-COMPUTE
ELSE
MOVE WS-ET-CAPPED(WS-IDX-COUNTER)
TO WS-ET-INDEXED(WS-IDX-COUNTER)
END-IF
ELSE
* Earnings at or after age 60: use actual
* (capped) amount without indexing
MOVE WS-ET-CAPPED(WS-IDX-COUNTER)
TO WS-ET-INDEXED(WS-IDX-COUNTER)
END-IF
END-PERFORM
.
*================================================================*
* SELECT THE HIGHEST 35 YEARS OF INDEXED EARNINGS *
* *
* Sort the earnings table by indexed earnings in descending *
* order, then select the top 35 entries. If fewer than 35 *
* years have earnings, the missing years count as zero, *
* reducing the AIME. *
*================================================================*
4000-SELECT-TOP-35.
* Sort earnings by indexed amount (descending)
* Using simple bubble sort for clarity
PERFORM VARYING WS-SORT-IDX-1 FROM 1 BY 1
UNTIL WS-SORT-IDX-1 >= WS-EARN-COUNT
COMPUTE WS-SORT-IDX-2 =
WS-SORT-IDX-1 + 1
PERFORM VARYING WS-SORT-IDX-2
FROM WS-SORT-IDX-2 BY 1
UNTIL WS-SORT-IDX-2 > WS-EARN-COUNT
IF WS-ET-INDEXED(WS-SORT-IDX-2) >
WS-ET-INDEXED(WS-SORT-IDX-1)
* Swap all fields for the two entries
MOVE WS-ET-INDEXED(WS-SORT-IDX-1)
TO WS-SORT-TEMP-EARN
MOVE WS-ET-INDEXED(WS-SORT-IDX-2)
TO WS-ET-INDEXED(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-EARN
TO WS-ET-INDEXED(WS-SORT-IDX-2)
MOVE WS-ET-YEAR(WS-SORT-IDX-1)
TO WS-SORT-TEMP-YEAR
MOVE WS-ET-YEAR(WS-SORT-IDX-2)
TO WS-ET-YEAR(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-YEAR
TO WS-ET-YEAR(WS-SORT-IDX-2)
MOVE WS-ET-ACTUAL(WS-SORT-IDX-1)
TO WS-SORT-TEMP-ACTUAL
MOVE WS-ET-ACTUAL(WS-SORT-IDX-2)
TO WS-ET-ACTUAL(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-ACTUAL
TO WS-ET-ACTUAL(WS-SORT-IDX-2)
MOVE WS-ET-CAPPED(WS-SORT-IDX-1)
TO WS-SORT-TEMP-CAPPED
MOVE WS-ET-CAPPED(WS-SORT-IDX-2)
TO WS-ET-CAPPED(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-CAPPED
TO WS-ET-CAPPED(WS-SORT-IDX-2)
MOVE WS-ET-WAGE-INDEX(WS-SORT-IDX-1)
TO WS-SORT-TEMP-INDEX
MOVE WS-ET-WAGE-INDEX(WS-SORT-IDX-2)
TO WS-ET-WAGE-INDEX(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-INDEX
TO WS-ET-WAGE-INDEX(WS-SORT-IDX-2)
MOVE WS-ET-SS-MAX(WS-SORT-IDX-1)
TO WS-SORT-TEMP-MAX
MOVE WS-ET-SS-MAX(WS-SORT-IDX-2)
TO WS-ET-SS-MAX(WS-SORT-IDX-1)
MOVE WS-SORT-TEMP-MAX
TO WS-ET-SS-MAX(WS-SORT-IDX-2)
END-IF
END-PERFORM
END-PERFORM
* Select the top 35 years
MOVE ZEROS TO WS-YEARS-SELECTED
PERFORM VARYING WS-IDX-COUNTER FROM 1 BY 1
UNTIL WS-IDX-COUNTER > WS-EARN-COUNT
OR WS-YEARS-SELECTED >= WS-COMPUTATION-YEARS
SET ET-SELECTED(WS-IDX-COUNTER) TO TRUE
ADD 1 TO WS-YEARS-SELECTED
ADD WS-ET-INDEXED(WS-IDX-COUNTER)
TO WS-TOP35-TOTAL
END-PERFORM
.
*================================================================*
* CALCULATE AIME (AVERAGE INDEXED MONTHLY EARNINGS) *
* *
* AIME = Total of highest 35 years / 420 (35 * 12) *
* The result is truncated (not rounded) to a whole dollar. *
*================================================================*
5000-CALCULATE-AIME.
* Divide total by 420 months and truncate
* (SSA rules: drop cents from AIME)
COMPUTE WS-AIME =
WS-TOP35-TOTAL / 420
.
*================================================================*
* CALCULATE PIA (PRIMARY INSURANCE AMOUNT) *
* *
* PIA formula with 2024 bend points: *
* 90% of first $1,174 of AIME *
* + 32% of AIME between $1,174 and $7,078 *
* + 15% of AIME above $7,078 *
* *
* The PIA is rounded down to the next lower dime. *
*================================================================*
6000-CALCULATE-PIA.
* Segment 1: 90% of AIME up to first bend point
IF WS-AIME > WS-BEND-POINT-1
COMPUTE WS-PIA-SEGMENT-1 ROUNDED =
WS-BEND-POINT-1 * 0.90
ON SIZE ERROR
DISPLAY 'SIZE ERROR: PIA SEG 1'
END-COMPUTE
ELSE
COMPUTE WS-PIA-SEGMENT-1 ROUNDED =
WS-AIME * 0.90
ON SIZE ERROR
DISPLAY 'SIZE ERROR: PIA SEG 1'
END-COMPUTE
END-IF
* Segment 2: 32% of AIME between bend points
IF WS-AIME > WS-BEND-POINT-2
COMPUTE WS-PIA-SEGMENT-2 ROUNDED =
(WS-BEND-POINT-2 - WS-BEND-POINT-1)
* 0.32
ON SIZE ERROR
DISPLAY 'SIZE ERROR: PIA SEG 2'
END-COMPUTE
ELSE IF WS-AIME > WS-BEND-POINT-1
COMPUTE WS-PIA-SEGMENT-2 ROUNDED =
(WS-AIME - WS-BEND-POINT-1) * 0.32
ON SIZE ERROR
DISPLAY 'SIZE ERROR: PIA SEG 2'
END-COMPUTE
ELSE
MOVE ZEROS TO WS-PIA-SEGMENT-2
END-IF
* Segment 3: 15% of AIME above second bend point
IF WS-AIME > WS-BEND-POINT-2
COMPUTE WS-PIA-SEGMENT-3 ROUNDED =
(WS-AIME - WS-BEND-POINT-2) * 0.15
ON SIZE ERROR
DISPLAY 'SIZE ERROR: PIA SEG 3'
END-COMPUTE
ELSE
MOVE ZEROS TO WS-PIA-SEGMENT-3
END-IF
* Sum the three segments
COMPUTE WS-PIA-RAW =
WS-PIA-SEGMENT-1 + WS-PIA-SEGMENT-2
+ WS-PIA-SEGMENT-3
* Round DOWN to the next lower dime (SSA rules)
* Multiply by 10, truncate to integer, divide by 10
COMPUTE WS-PIA-ROUNDED =
FUNCTION INTEGER(WS-PIA-RAW * 10) / 10
MOVE WS-PIA-ROUNDED TO WS-PIA-FINAL
.
*================================================================*
* APPLY EARLY/LATE RETIREMENT ADJUSTMENT *
* *
* EARLY RETIREMENT (before FRA): *
* First 36 months early: reduce by 5/9 of 1% per month *
* Each additional month: reduce by 5/12 of 1% per month *
* *
* DELAYED RETIREMENT (after FRA): *
* Increase by 2/3 of 1% per month (8% per year) *
* Maximum delay until age 70 *
*================================================================*
7000-APPLY-RETIREMENT-ADJ.
* Convert FRA and claim age to total months
* FRA example: 67 years 0 months = 804 months
COMPUTE WS-FRA-TOTAL-MONTHS =
(WS-FRA-AGE-YEARS * 12) + WS-FRA-AGE-MONTHS
* Claim age in months (assuming claiming at beginning
* of the claim-age year, i.e., 0 additional months)
COMPUTE WS-CLAIM-TOTAL-MONTHS =
WS-CLAIM-AGE * 12
* Calculate difference
COMPUTE WS-MONTHS-DIFF =
WS-FRA-TOTAL-MONTHS - WS-CLAIM-TOTAL-MONTHS
IF WS-MONTHS-DIFF > 0
* EARLY RETIREMENT
MOVE WS-MONTHS-DIFF TO WS-MONTHS-EARLY
MOVE ZEROS TO WS-MONTHS-LATE
* First 36 months: 5/9 of 1% per month
IF WS-MONTHS-EARLY > 36
MOVE 36 TO WS-EARLY-FIRST-36
COMPUTE WS-EARLY-OVER-36 =
WS-MONTHS-EARLY - 36
ELSE
MOVE WS-MONTHS-EARLY TO WS-EARLY-FIRST-36
MOVE ZEROS TO WS-EARLY-OVER-36
END-IF
* Calculate total reduction factor
* 5/9 of 1% = 0.005556 per month for first 36
* 5/12 of 1% = 0.004167 per month beyond 36
COMPUTE WS-EARLY-REDUCTION =
(WS-EARLY-FIRST-36 * 5 / 9 / 100) +
(WS-EARLY-OVER-36 * 5 / 12 / 100)
* Apply reduction
COMPUTE WS-ADJUSTED-BENEFIT ROUNDED =
WS-PIA-FINAL * (1 - WS-EARLY-REDUCTION)
ON SIZE ERROR
DISPLAY 'SIZE ERROR: EARLY ADJ'
END-COMPUTE
MOVE 'EARLY RETIREMENT REDUCTION'
TO WS-ADJUSTMENT-DESC
ELSE IF WS-MONTHS-DIFF < 0
* DELAYED RETIREMENT
COMPUTE WS-MONTHS-LATE =
0 - WS-MONTHS-DIFF
MOVE ZEROS TO WS-MONTHS-EARLY
* Cap delay at age 70
COMPUTE WS-IDX-COUNTER =
(70 * 12) - WS-FRA-TOTAL-MONTHS
IF WS-MONTHS-LATE > WS-IDX-COUNTER
MOVE WS-IDX-COUNTER TO WS-MONTHS-LATE
END-IF
* 2/3 of 1% per month = 0.006667 per month
COMPUTE WS-LATE-CREDIT =
WS-MONTHS-LATE * 2 / 3 / 100
* Apply delayed retirement credits
COMPUTE WS-ADJUSTED-BENEFIT ROUNDED =
WS-PIA-FINAL * (1 + WS-LATE-CREDIT)
ON SIZE ERROR
DISPLAY 'SIZE ERROR: DELAY ADJ'
END-COMPUTE
MOVE 'DELAYED RETIREMENT CREDIT'
TO WS-ADJUSTMENT-DESC
ELSE
* Claiming at exactly FRA
MOVE WS-PIA-FINAL TO WS-ADJUSTED-BENEFIT
MOVE 'AT FULL RETIREMENT AGE'
TO WS-ADJUSTMENT-DESC
END-IF
* Round to nearest penny (benefit amounts are
* rounded down to next lower dime by SSA rules,
* but the adjusted amount is truncated to dollars)
COMPUTE WS-ADJUSTED-BENEFIT =
FUNCTION INTEGER(WS-ADJUSTED-BENEFIT * 10)
/ 10
.
*================================================================*
* WRITE COMPLETE BENEFIT COMPUTATION REPORT *
*================================================================*
7500-WRITE-REPORT.
WRITE REPORT-LINE FROM WS-RPT-BLANK
* Worker identification
MOVE WS-SSN TO WWH-SSN
MOVE WS-WORKER-NAME TO WWH-NAME
MOVE WS-BIRTH-YEAR TO WWH-BIRTH-YEAR
MOVE WS-CLAIM-AGE TO WWH-CLAIM-AGE
WRITE REPORT-LINE FROM WS-RPT-WORKER-HDR
* FRA information
MOVE WS-FRA-AGE-YEARS TO WFRA-YEARS
MOVE WS-FRA-AGE-MONTHS TO WFRA-MONTHS
WRITE REPORT-LINE FROM WS-RPT-FRA-LINE
WRITE REPORT-LINE FROM WS-RPT-BLANK
* Earnings history table
WRITE REPORT-LINE FROM WS-RPT-EARN-HDR
WRITE REPORT-LINE FROM WS-RPT-EARN-DASH
PERFORM VARYING WS-IDX-COUNTER FROM 1 BY 1
UNTIL WS-IDX-COUNTER > WS-EARN-COUNT
MOVE WS-ET-YEAR(WS-IDX-COUNTER)
TO WEL-YEAR
MOVE WS-ET-ACTUAL(WS-IDX-COUNTER)
TO WEL-ACTUAL
MOVE WS-ET-CAPPED(WS-IDX-COUNTER)
TO WEL-CAPPED
MOVE WS-ET-WAGE-INDEX(WS-IDX-COUNTER)
TO WEL-WAGE-IDX
* Calculate and display the index factor
IF WS-ET-YEAR(WS-IDX-COUNTER) < WS-AGE60-YEAR
AND WS-ET-WAGE-INDEX(WS-IDX-COUNTER) > 0
COMPUTE WEL-IDX-FACTOR =
WS-AGE60-WAGE-INDEX /
WS-ET-WAGE-INDEX(WS-IDX-COUNTER)
ELSE
MOVE 1.000000 TO WEL-IDX-FACTOR
END-IF
MOVE WS-ET-INDEXED(WS-IDX-COUNTER)
TO WEL-INDEXED
IF ET-SELECTED(WS-IDX-COUNTER)
MOVE 'YES' TO WEL-SELECTED
ELSE
MOVE ' ' TO WEL-SELECTED
END-IF
WRITE REPORT-LINE FROM WS-RPT-EARN-LINE
END-PERFORM
WRITE REPORT-LINE FROM WS-RPT-EARN-DASH
WRITE REPORT-LINE FROM WS-RPT-BLANK
* AIME
MOVE WS-AIME TO WAIME-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-AIME-LINE
WRITE REPORT-LINE FROM WS-RPT-BLANK
* PIA calculation detail
WRITE REPORT-LINE FROM WS-RPT-PIA-HDR
MOVE ' 90% OF FIRST $1,174 OF AIME'
TO WPS-DESC
MOVE WS-PIA-SEGMENT-1 TO WPS-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-PIA-SEGMENT
MOVE ' 32% OF AIME FROM $1,174 TO $7,078'
TO WPS-DESC
MOVE WS-PIA-SEGMENT-2 TO WPS-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-PIA-SEGMENT
MOVE ' 15% OF AIME ABOVE $7,078'
TO WPS-DESC
MOVE WS-PIA-SEGMENT-3 TO WPS-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-PIA-SEGMENT
MOVE ' PIA (ROUNDED DOWN TO DIME)'
TO WPS-DESC
MOVE WS-PIA-FINAL TO WPS-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-PIA-SEGMENT
WRITE REPORT-LINE FROM WS-RPT-BLANK
* Retirement adjustment
MOVE WS-ADJUSTMENT-DESC TO WBL-DESC
MOVE WS-ADJUSTED-BENEFIT TO WBL-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-BENEFIT-LINE
IF WS-MONTHS-EARLY > 0
MOVE ' MONTHS BEFORE FRA:'
TO WBL-DESC
COMPUTE WBL-AMOUNT = WS-MONTHS-EARLY
WRITE REPORT-LINE FROM WS-RPT-BENEFIT-LINE
END-IF
IF WS-MONTHS-LATE > 0
MOVE ' MONTHS AFTER FRA:'
TO WBL-DESC
COMPUTE WBL-AMOUNT = WS-MONTHS-LATE
WRITE REPORT-LINE FROM WS-RPT-BENEFIT-LINE
END-IF
* Final monthly benefit
WRITE REPORT-LINE FROM WS-RPT-BLANK
MOVE 'MONTHLY RETIREMENT BENEFIT'
TO WBL-DESC
MOVE WS-ADJUSTED-BENEFIT TO WBL-AMOUNT
WRITE REPORT-LINE FROM WS-RPT-BENEFIT-LINE
* Annual benefit
MOVE 'ESTIMATED ANNUAL BENEFIT'
TO WBL-DESC
COMPUTE WBL-AMOUNT ROUNDED =
WS-ADJUSTED-BENEFIT * 12
WRITE REPORT-LINE FROM WS-RPT-BENEFIT-LINE
WRITE REPORT-LINE FROM WS-RPT-SEPARATOR
.
*================================================================*
* READ NEXT EARNINGS RECORD *
*================================================================*
8000-READ-EARNINGS.
READ EARNINGS-HISTORY
AT END
SET END-OF-FILE TO TRUE
END-READ
.
*================================================================*
* FINALIZE AND CLOSE FILES *
*================================================================*
9000-FINALIZE.
CLOSE EARNINGS-HISTORY
BENEFIT-REPORT
DISPLAY 'BENEFIT CALCULATION COMPLETE'
DISPLAY ' WORKERS PROCESSED: '
WS-WORKERS-PROCESSED
.
The JCL to execute the benefit calculator follows:
//SSACALC JOB (ACCT),'SSA BENEFIT CALC',CLASS=A,
// MSGCLASS=X,NOTIFY=&SYSUID
//*
//*================================================================*
//* SOCIAL SECURITY BENEFIT CALCULATION *
//*================================================================*
//*
//STEP01 EXEC PGM=SSABENCALC
//STEPLIB DD DSN=SSA.BENEFIT.LOADLIB,DISP=SHR
//EARNHIST DD DSN=SSA.EARNINGS.HISTORY,DISP=SHR
//BENCALC DD DSN=SSA.BENEFIT.REPORT,DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT DD SYSOUT=*
How the Program Works
Earnings Indexing (3000-INDEX-EARNINGS)
The most conceptually significant step is earnings indexing. Wages earned in 1990 had a different purchasing power than wages earned in 2020. To make earnings from different years comparable, SSA adjusts each year's earnings by the ratio of the national average wage index in the worker's age-60 year to the index in the earnings year.
For example, if the age-60 wage index is 60,575.07 and the 1990 wage index was 21,027.98, the index factor is 60,575.07 / 21,027.98 = 2.880. Earnings of $30,000 in 1990 become $86,400 in indexed terms. This ensures that a worker who earned a middle-class income in 1990 is not penalized relative to a worker earning a middle-class income in 2020.
Earnings in years at or after age 60 are not indexed -- they are used at their actual capped amount. This is because the wage index used for indexing is the age-60 index, so indexing would be meaningless for those years.
Top-35 Selection (4000-SELECT-TOP-35)
After indexing, the program sorts all earnings years by indexed amount in descending order using a bubble sort. The top 35 years are then selected. If a worker has fewer than 35 years of earnings, the missing years are effectively counted as zero, which reduces the AIME. This is why years with zero earnings significantly impact the final benefit.
The sort uses a straightforward bubble sort for clarity. In a production system processing millions of records, a more efficient sorting algorithm or the COBOL SORT verb might be used, but for a 50-element table, bubble sort is adequate.
AIME Calculation (5000-CALCULATE-AIME)
The AIME is simply the sum of the top 35 years of indexed earnings divided by 420 (35 years * 12 months). The critical detail is that the result is truncated to a whole dollar -- not rounded. SSA rules specify truncation, and this must be implemented exactly. Using ROUNDED here would produce an incorrect AIME for roughly half of all workers, potentially resulting in a PIA that is $0.10 to $1.00 too high or too low per month. Over decades of benefit payments, this matters.
PIA Calculation (6000-CALCULATE-PIA)
The PIA formula applies three replacement rates to three segments of the AIME, separated by the bend points. The progressive structure means low-income workers receive a higher percentage of their earnings as benefits:
- A worker with an AIME of $1,000 gets 90% = $900/month
- A worker with an AIME of $5,000 gets 90% of $1,174 + 32% of $3,826 = $1,056.60 + $1,224.32 = $2,280.92/month (45.6% of AIME)
- A worker with an AIME of $10,000 gets all three segments totaling approximately $3,418/month (34.2% of AIME)
The PIA is rounded down to the next lower dime, not to the nearest dime. This is another SSA-specific rounding rule that must be implemented precisely.
Retirement Adjustment (7000-APPLY-RETIREMENT-ADJ)
The adjustment for early or late retirement is among the most intricate parts of the calculation:
Early retirement: The reduction is not uniform. The first 36 months before FRA are penalized at 5/9 of 1% per month (approximately 6.67% per year). Months beyond 36 are penalized at 5/12 of 1% per month (5% per year). For a worker with an FRA of 67 claiming at 62 (60 months early), the reduction is: (36 * 5/9/100) + (24 * 5/12/100) = 0.20 + 0.10 = 0.30, or a 30% reduction.
Delayed retirement: Credits of 2/3 of 1% per month (8% per year) are added for each month of delay past FRA, up to age 70. A worker with FRA 67 who delays to 70 gets 36 months of credits = 24% increase.
Precision Considerations
The Social Security benefit calculation has several unique precision requirements:
-
Earnings are capped annually: Before indexing, each year's earnings are limited to the Social Security taxable maximum for that year. This cap changes annually.
-
AIME is truncated to whole dollars: Unlike most financial calculations where rounding to the nearest cent is appropriate, the AIME specifically requires truncation.
-
PIA is rounded down to the next lower dime: The PIA uses an unusual rounding rule that drops to the next lower 10-cent increment, not the nearest dime.
-
Index factors require extended decimal precision: The ratio of two wage index values can produce a factor with many significant digits (e.g., 2.879856...). The index factor field uses PIC S9(05)V9(08) to carry 8 decimal places.
-
Early retirement reduction uses fractional arithmetic: The 5/9 and 5/12 fractions produce repeating decimals. Computing these precisely requires sufficient decimal places in the intermediate fields.
Discussion Questions
-
Why does SSA index earnings to the worker's age-60 year rather than to the current year? What impact would indexing to the current year have on benefit amounts?
-
The AIME is truncated to whole dollars rather than rounded. How much difference could this make to a retiree's monthly benefit over 20 years of benefit receipt?
-
Why is the PIA formula progressive (higher replacement rates for lower earnings)? What policy goal does this serve?
-
A worker has 30 years of earnings and 5 years of zero earnings in the top-35 computation. How do the zero years affect the AIME, and what strategies might a worker use to mitigate this?
-
The early retirement reduction rates (5/9 of 1% for the first 36 months, 5/12 of 1% thereafter) create a non-linear reduction. Why is the penalty steeper for the months closest to FRA?
Connection to Chapter Concepts
This case study directly applies these Chapter 35 concepts:
- Government benefit formulas: The AIME/PIA formula demonstrates the complex, rule-driven calculations typical of government entitlement systems (Section 35.4).
- Table-driven parameters: Bend points, wage indexes, and earnings caps are stored in tables that change annually (Section 35.4).
- COMP-3 for all monetary calculations: All earnings, index factors, and benefit amounts use packed decimal (Section 35.1).
- Specialized rounding rules: SSA's truncation-to-whole-dollar (AIME) and round-down-to-dime (PIA) requirements differ from standard financial rounding (Section 35.4).
- COBOL intrinsic functions: INTEGER is used for truncation, demonstrating how COBOL's built-in functions implement non-standard rounding rules (Section 35.4).
- Batch processing architecture: The program reads sequential earnings records and produces formatted reports, following the standard government batch processing pattern (Section 35.5).
- OCCURS for earnings history: The earnings table stores up to 50 years of data, accessed by subscript during indexing and sorting (Section 35.1).
- ON SIZE ERROR: Overflow protection on all indexed earnings calculations guards against corrupted data or extreme index factors (Section 35.2).