Case Study 1: Mortgage Payment Calculator

Background

Heartland Federal Credit Union (HFCU) serves approximately 185,000 members across seven branch locations in the upper Midwest. The credit union originates approximately 3,200 mortgage loans per year, ranging from modest first-time buyer homes at $120,000 to rural properties exceeding $750,000. HFCU offers both fixed-rate and adjustable-rate mortgages, and its loan officers rely on an IBM z/OS mainframe system to generate accurate payment calculations and amortization schedules at the point of sale.

The mortgage payment calculator is one of the most frequently executed programs in the credit union's COBOL application suite. Loan officers invoke it dozens of times per day during member consultations, and the nightly batch cycle uses the same calculation engine to generate disclosure documents for new loan applications. Accuracy is paramount: the Truth in Lending Act (Regulation Z) requires that disclosed payment amounts match actual billing amounts to the penny. A single rounding error that produces a payment amount even one cent different from the disclosed amount can trigger regulatory findings during NCUA examinations.

This case study presents a complete COBOL mortgage payment calculator that handles both fixed-rate and adjustable-rate mortgages, generates monthly amortization schedules with principal and interest splits, and demonstrates the proper use of COMP-3 arithmetic, ROUNDED, and ON SIZE ERROR for financial precision.

Business Requirements

The mortgage calculator must satisfy the following requirements:

  1. Fixed-rate mortgage payment calculation: Given a loan amount, annual interest rate, and term in months, compute the exact monthly payment using the standard amortization formula.

  2. Adjustable-rate mortgage (ARM) support: For 5/1 ARMs, calculate the initial payment at the fixed introductory rate, then recalculate the payment when the rate adjusts after the initial fixed period.

  3. Amortization schedule generation: For each payment in the schedule, compute the interest portion, the principal portion, and the remaining balance after the payment. Accumulate totals for interest paid and principal paid.

  4. Final payment adjustment: The last payment must be adjusted so the remaining balance reaches exactly zero. Due to rounding in each monthly calculation, the final payment is almost never equal to the regular monthly payment.

  5. Penny-exact arithmetic: All monetary calculations must use packed decimal (COMP-3) fields with the ROUNDED phrase. Intermediate rate calculations must carry at least 10 decimal places.

The Amortization Formula

The standard fixed-rate mortgage payment formula is:

M = P * [r(1 + r)^n] / [(1 + r)^n - 1]

Where: - M = monthly payment - P = loan principal (amount borrowed) - r = monthly interest rate (annual rate / 12) - n = total number of monthly payments

Each monthly payment is then split into interest and principal components: - Interest portion = Remaining Balance * Monthly Rate - Principal portion = Monthly Payment - Interest Portion - New Balance = Previous Balance - Principal Portion

Complete COBOL Program

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  MORTCALC.
       AUTHOR.      HFCU MORTGAGE SYSTEMS.
       DATE-WRITTEN. 2024-03-15.
      *================================================================*
      * PROGRAM: MORTCALC - MORTGAGE PAYMENT CALCULATOR                *
      *                                                                *
      * PURPOSE: Calculate monthly mortgage payments for fixed-rate    *
      *          and adjustable-rate mortgages. Generate amortization  *
      *          schedules showing principal/interest split per month. *
      *                                                                *
      * PROCESSING:                                                    *
      *   1. Read mortgage parameters from input file                  *
      *   2. Calculate monthly payment using amortization formula      *
      *   3. Generate month-by-month amortization schedule             *
      *   4. Handle ARM rate adjustments at specified intervals        *
      *   5. Adjust final payment for exact zero balance               *
      *   6. Produce formatted report with summary totals              *
      *                                                                *
      * INPUT:  MORTPARM - Mortgage parameter file                     *
      * OUTPUT: MORTRPT  - Amortization schedule report                *
      *================================================================*

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION ALL INTRINSIC.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT MORTGAGE-INPUT
               ASSIGN TO MORTPARM
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-INPUT-STATUS.

           SELECT MORTGAGE-REPORT
               ASSIGN TO MORTRPT
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-REPORT-STATUS.

       DATA DIVISION.
       FILE SECTION.

       FD  MORTGAGE-INPUT
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS.
       01  MORTGAGE-INPUT-REC.
           05  MI-LOAN-NUMBER         PIC X(10).
           05  MI-BORROWER-NAME       PIC X(25).
           05  MI-LOAN-AMOUNT         PIC 9(07)V99.
           05  MI-ANNUAL-RATE         PIC 99V9(4).
           05  MI-TERM-MONTHS         PIC 9(03).
           05  MI-LOAN-TYPE           PIC X(01).
               88  MI-FIXED-RATE      VALUE 'F'.
               88  MI-ARM-LOAN        VALUE 'A'.
           05  MI-ARM-FIXED-MONTHS    PIC 9(03).
           05  MI-ARM-ADJ-RATE        PIC 99V9(4).
           05  MI-ARM-RATE-CAP        PIC 99V9(4).
           05  FILLER                 PIC X(12).

       FD  MORTGAGE-REPORT
           RECORDING MODE IS F
           RECORD CONTAINS 132 CHARACTERS.
       01  REPORT-LINE                PIC X(132).

       WORKING-STORAGE SECTION.

      *----------------------------------------------------------------*
      * FILE STATUS AND CONTROL FLAGS                                  *
      *----------------------------------------------------------------*
       01  WS-FILE-STATUS.
           05  WS-INPUT-STATUS        PIC X(02).
           05  WS-REPORT-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-ERROR-FLAG          PIC X(01) VALUE 'N'.
               88  CALC-ERROR         VALUE 'Y'.
               88  NO-CALC-ERROR      VALUE 'N'.

      *----------------------------------------------------------------*
      * MORTGAGE CALCULATION FIELDS - ALL COMP-3 FOR PRECISION         *
      * These fields carry extended decimal precision for              *
      * intermediate calculations to prevent cumulative rounding       *
      * errors across hundreds of monthly payment calculations.        *
      *----------------------------------------------------------------*
       01  WS-MORTGAGE-FIELDS.
           05  WS-LOAN-AMOUNT        PIC S9(09)V99   COMP-3.
           05  WS-ANNUAL-RATE        PIC S9(02)V9(06) COMP-3.
           05  WS-MONTHLY-RATE       PIC S9(02)V9(10) COMP-3.
           05  WS-TERM-MONTHS        PIC S9(04)       COMP-3.
           05  WS-REMAINING-MONTHS   PIC S9(04)       COMP-3.
           05  WS-MONTHLY-PAYMENT    PIC S9(07)V99   COMP-3.
           05  WS-CURRENT-BALANCE    PIC S9(09)V99   COMP-3.
           05  WS-INTEREST-PORTION   PIC S9(07)V99   COMP-3.
           05  WS-PRINCIPAL-PORTION  PIC S9(07)V99   COMP-3.

      *----------------------------------------------------------------*
      * INTERMEDIATE CALCULATION FIELDS WITH EXTENDED PRECISION        *
      *----------------------------------------------------------------*
       01  WS-CALC-WORK.
           05  WS-POWER-FACTOR       PIC S9(05)V9(12) COMP-3.
           05  WS-NUMERATOR          PIC S9(15)V9(10) COMP-3.
           05  WS-DENOMINATOR        PIC S9(15)V9(10) COMP-3.
           05  WS-GROWTH-FACTOR      PIC S9(03)V9(12) COMP-3.
           05  WS-PERIOD-CTR         PIC S9(04)       COMP-3.

      *----------------------------------------------------------------*
      * ARM (ADJUSTABLE RATE MORTGAGE) FIELDS                          *
      *----------------------------------------------------------------*
       01  WS-ARM-FIELDS.
           05  WS-ARM-FIXED-MONTHS   PIC S9(04)       COMP-3.
           05  WS-ARM-ADJ-RATE       PIC S9(02)V9(06) COMP-3.
           05  WS-ARM-RATE-CAP       PIC S9(02)V9(06) COMP-3.
           05  WS-ARM-NEW-RATE       PIC S9(02)V9(06) COMP-3.
           05  WS-ARM-ADJUSTED       PIC X(01) VALUE 'N'.
               88  ARM-RATE-ADJUSTED  VALUE 'Y'.
               88  ARM-NOT-ADJUSTED   VALUE 'N'.

      *----------------------------------------------------------------*
      * CUMULATIVE TRACKING FIELDS                                     *
      *----------------------------------------------------------------*
       01  WS-TOTALS.
           05  WS-TOTAL-INTEREST     PIC S9(09)V99 COMP-3
                                     VALUE ZEROS.
           05  WS-TOTAL-PRINCIPAL    PIC S9(09)V99 COMP-3
                                     VALUE ZEROS.
           05  WS-TOTAL-PAYMENTS     PIC S9(11)V99 COMP-3
                                     VALUE ZEROS.
           05  WS-PAYMENT-COUNT      PIC S9(04) COMP-3
                                     VALUE ZEROS.

      *----------------------------------------------------------------*
      * LOOP AND CONTROL FIELDS                                        *
      *----------------------------------------------------------------*
       01  WS-CONTROLS.
           05  WS-CURRENT-MONTH      PIC 9(04).
           05  WS-LOANS-PROCESSED    PIC 9(04) VALUE ZEROS.
           05  WS-LINE-COUNT         PIC 9(03) VALUE 99.
           05  WS-PAGE-NUMBER        PIC 9(03) VALUE 0.
           05  WS-LINES-PER-PAGE     PIC 9(03) VALUE 55.

      *----------------------------------------------------------------*
      * REPORT FORMAT LINES                                            *
      *----------------------------------------------------------------*
       01  WS-RPT-HEADER-1.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(50)
               VALUE 'HEARTLAND FEDERAL CREDIT UNION'.
           05  FILLER                 PIC X(50)
               VALUE 'MORTGAGE AMORTIZATION SCHEDULE'.
           05  FILLER                 PIC X(20) VALUE SPACES.
           05  FILLER                 PIC X(06) VALUE 'PAGE: '.
           05  WH1-PAGE              PIC ZZ9.
           05  FILLER                 PIC X(02) VALUE SPACES.

       01  WS-RPT-HEADER-2.
           05  FILLER                 PIC X(132) VALUE ALL '='.

       01  WS-RPT-LOAN-LINE-1.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(14) VALUE 'LOAN NUMBER: '.
           05  WLL1-LOAN-NUM         PIC X(10).
           05  FILLER                 PIC X(05) VALUE SPACES.
           05  FILLER                 PIC X(11) VALUE 'BORROWER: '.
           05  WLL1-BORROWER         PIC X(25).
           05  FILLER                 PIC X(05) VALUE SPACES.
           05  FILLER                 PIC X(06) VALUE 'TYPE: '.
           05  WLL1-LOAN-TYPE        PIC X(15).
           05  FILLER                 PIC X(40) VALUE SPACES.

       01  WS-RPT-LOAN-LINE-2.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(16) VALUE 'LOAN AMOUNT:  $'.
           05  WLL2-AMOUNT           PIC Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(05) VALUE SPACES.
           05  FILLER                 PIC X(14) VALUE 'ANNUAL RATE: '.
           05  WLL2-RATE             PIC Z9.9999.
           05  FILLER                 PIC X(01) VALUE '%'.
           05  FILLER                 PIC X(05) VALUE SPACES.
           05  FILLER                 PIC X(08) VALUE 'TERM:  '.
           05  WLL2-TERM             PIC ZZ9.
           05  FILLER                 PIC X(07) VALUE ' MONTHS'.
           05  FILLER                 PIC X(05) VALUE SPACES.
           05  FILLER                 PIC X(12) VALUE 'PAYMENT:  $'.
           05  WLL2-PAYMENT          PIC ZZ,ZZ9.99.
           05  FILLER                 PIC X(22) VALUE SPACES.

       01  WS-RPT-COL-HDR.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(08) VALUE 'PAYMENT '.
           05  FILLER                 PIC X(18) VALUE '     PAYMENT      '.
           05  FILLER                 PIC X(18) VALUE '    INTEREST      '.
           05  FILLER                 PIC X(18) VALUE '    PRINCIPAL     '.
           05  FILLER                 PIC X(22) VALUE '      BALANCE         '.
           05  FILLER                 PIC X(22) VALUE '   CUM INTEREST       '.
           05  FILLER                 PIC X(25) VALUE SPACES.

       01  WS-RPT-COL-DASH.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(08) VALUE '--------'.
           05  FILLER                 PIC X(18) VALUE '------------------'.
           05  FILLER                 PIC X(18) VALUE '------------------'.
           05  FILLER                 PIC X(18) VALUE '------------------'.
           05  FILLER                 PIC X(22) VALUE '----------------------'.
           05  FILLER                 PIC X(22) VALUE '----------------------'.
           05  FILLER                 PIC X(25) VALUE SPACES.

       01  WS-RPT-DETAIL.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  WD-PMT-NUM            PIC ZZZ9.
           05  FILLER                 PIC X(04) VALUE SPACES.
           05  WD-PMT-AMOUNT         PIC $ZZZ,ZZ9.99.
           05  FILLER                 PIC X(04) VALUE SPACES.
           05  WD-INTEREST           PIC $ZZZ,ZZ9.99.
           05  FILLER                 PIC X(04) VALUE SPACES.
           05  WD-PRINCIPAL          PIC $ZZZ,ZZ9.99.
           05  FILLER                 PIC X(04) VALUE SPACES.
           05  WD-BALANCE            PIC $Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(04) VALUE SPACES.
           05  WD-CUM-INT            PIC $Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(25) VALUE SPACES.

       01  WS-RPT-ARM-NOTICE.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(40)
               VALUE '*** RATE ADJUSTMENT: NEW RATE = '.
           05  WARN-NEW-RATE         PIC Z9.9999.
           05  FILLER                 PIC X(20)
               VALUE '%, NEW PAYMENT = $'.
           05  WARN-NEW-PMT          PIC ZZ,ZZ9.99.
           05  FILLER                 PIC X(05) VALUE ' ***'.
           05  FILLER                 PIC X(42) VALUE SPACES.

       01  WS-RPT-SUMMARY.
           05  FILLER                 PIC X(132) VALUE ALL '-'.

       01  WS-RPT-SUM-LINE-1.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(28)
               VALUE 'TOTAL PRINCIPAL PAID:     $'.
           05  WSUM-PRINCIPAL        PIC Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(10) VALUE SPACES.
           05  FILLER                 PIC X(28)
               VALUE 'TOTAL INTEREST PAID:      $'.
           05  WSUM-INTEREST         PIC Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(31) VALUE SPACES.

       01  WS-RPT-SUM-LINE-2.
           05  FILLER                 PIC X(01) VALUE SPACE.
           05  FILLER                 PIC X(28)
               VALUE 'TOTAL ALL PAYMENTS:       $'.
           05  WSUM-ALL-PMTS         PIC Z,ZZZ,ZZ9.99.
           05  FILLER                 PIC X(10) VALUE SPACES.
           05  FILLER                 PIC X(28)
               VALUE 'NUMBER OF PAYMENTS:        '.
           05  WSUM-PMT-COUNT        PIC ZZ,ZZ9.
           05  FILLER                 PIC X(38) VALUE SPACES.

       01  WS-BLANK-LINE             PIC X(132) VALUE SPACES.

       PROCEDURE DIVISION.

      *================================================================*
      * MAIN CONTROL PARAGRAPH                                         *
      *================================================================*
       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-MORTGAGES
               UNTIL END-OF-FILE
           PERFORM 9000-FINALIZE
           STOP RUN
           .

      *================================================================*
      * INITIALIZE - OPEN FILES AND READ FIRST RECORD                  *
      *================================================================*
       1000-INITIALIZE.
           OPEN INPUT  MORTGAGE-INPUT
           OPEN OUTPUT MORTGAGE-REPORT

           IF WS-INPUT-STATUS NOT = '00'
               DISPLAY 'ERROR OPENING INPUT FILE: '
                   WS-INPUT-STATUS
               SET END-OF-FILE TO TRUE
           END-IF

           IF WS-REPORT-STATUS NOT = '00'
               DISPLAY 'ERROR OPENING REPORT FILE: '
                   WS-REPORT-STATUS
               SET END-OF-FILE TO TRUE
           END-IF

           PERFORM 8000-READ-INPUT
           .

      *================================================================*
      * PROCESS EACH MORTGAGE RECORD                                   *
      *================================================================*
       2000-PROCESS-MORTGAGES.
           ADD 1 TO WS-LOANS-PROCESSED

      *    Reset totals for each new loan
           MOVE ZEROS TO WS-TOTAL-INTEREST
           MOVE ZEROS TO WS-TOTAL-PRINCIPAL
           MOVE ZEROS TO WS-TOTAL-PAYMENTS
           MOVE ZEROS TO WS-PAYMENT-COUNT
           SET NO-CALC-ERROR TO TRUE
           SET ARM-NOT-ADJUSTED TO TRUE

      *    Load parameters and calculate payment
           PERFORM 3000-LOAD-PARAMETERS
           PERFORM 4000-CALCULATE-PAYMENT
           PERFORM 5000-WRITE-LOAN-HEADER
           PERFORM 6000-GENERATE-SCHEDULE
           PERFORM 7000-WRITE-SUMMARY

           PERFORM 8000-READ-INPUT
           .

      *================================================================*
      * LOAD MORTGAGE PARAMETERS FROM INPUT RECORD                     *
      *================================================================*
       3000-LOAD-PARAMETERS.
           MOVE MI-LOAN-AMOUNT       TO WS-LOAN-AMOUNT
           MOVE MI-LOAN-AMOUNT       TO WS-CURRENT-BALANCE
           MOVE MI-TERM-MONTHS       TO WS-TERM-MONTHS
           MOVE MI-TERM-MONTHS       TO WS-REMAINING-MONTHS

      *    Convert annual rate from percentage to decimal
      *    Example: 6.5000 -> 0.065000
           COMPUTE WS-ANNUAL-RATE =
               MI-ANNUAL-RATE / 100

      *    Calculate monthly rate with extended precision
      *    Example: 0.065000 / 12 = 0.005416666666
           COMPUTE WS-MONTHLY-RATE =
               WS-ANNUAL-RATE / 12

      *    Load ARM-specific fields if applicable
           IF MI-ARM-LOAN
               MOVE MI-ARM-FIXED-MONTHS TO WS-ARM-FIXED-MONTHS
               COMPUTE WS-ARM-ADJ-RATE =
                   MI-ARM-ADJ-RATE / 100
               COMPUTE WS-ARM-RATE-CAP =
                   MI-ARM-RATE-CAP / 100
           END-IF
           .

      *================================================================*
      * CALCULATE MONTHLY PAYMENT USING AMORTIZATION FORMULA           *
      *                                                                *
      * M = P * [r * (1+r)^n] / [(1+r)^n - 1]                        *
      *                                                                *
      * Uses iterative multiplication for (1+r)^n to maintain         *
      * packed-decimal precision throughout the calculation.           *
      *================================================================*
       4000-CALCULATE-PAYMENT.
      *    Calculate growth factor: (1 + monthly rate)
           COMPUTE WS-GROWTH-FACTOR =
               1 + WS-MONTHLY-RATE

      *    Calculate (1+r)^n through iterative multiplication
      *    This preserves COMP-3 precision instead of using **
      *    which may convert to floating-point internally
           MOVE 1.0 TO WS-POWER-FACTOR
           PERFORM VARYING WS-PERIOD-CTR
               FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-REMAINING-MONTHS
               MULTIPLY WS-POWER-FACTOR BY WS-GROWTH-FACTOR
                   GIVING WS-POWER-FACTOR ROUNDED
           END-PERFORM

      *    Numerator: P * r * (1+r)^n
           COMPUTE WS-NUMERATOR ROUNDED =
               WS-CURRENT-BALANCE * WS-MONTHLY-RATE
               * WS-POWER-FACTOR
               ON SIZE ERROR
                   SET CALC-ERROR TO TRUE
                   DISPLAY 'SIZE ERROR IN NUMERATOR CALC'
           END-COMPUTE

      *    Denominator: (1+r)^n - 1
           COMPUTE WS-DENOMINATOR =
               WS-POWER-FACTOR - 1
               ON SIZE ERROR
                   SET CALC-ERROR TO TRUE
                   DISPLAY 'SIZE ERROR IN DENOMINATOR CALC'
           END-COMPUTE

      *    Monthly payment: M = numerator / denominator
           IF NOT CALC-ERROR
               COMPUTE WS-MONTHLY-PAYMENT ROUNDED =
                   WS-NUMERATOR / WS-DENOMINATOR
                   ON SIZE ERROR
                       SET CALC-ERROR TO TRUE
                       DISPLAY 'SIZE ERROR IN PAYMENT CALC'
               END-COMPUTE
           END-IF
           .

      *================================================================*
      * WRITE LOAN HEADER INFORMATION TO REPORT                        *
      *================================================================*
       5000-WRITE-LOAN-HEADER.
      *    Force new page for each loan
           MOVE 99 TO WS-LINE-COUNT
           PERFORM 5500-CHECK-PAGE

      *    Loan information line 1
           MOVE MI-LOAN-NUMBER      TO WLL1-LOAN-NUM
           MOVE MI-BORROWER-NAME    TO WLL1-BORROWER
           IF MI-FIXED-RATE
               MOVE 'FIXED RATE'    TO WLL1-LOAN-TYPE
           ELSE
               MOVE 'ADJUSTABLE ARM' TO WLL1-LOAN-TYPE
           END-IF
           WRITE REPORT-LINE FROM WS-RPT-LOAN-LINE-1
           ADD 1 TO WS-LINE-COUNT

      *    Loan information line 2
           MOVE WS-LOAN-AMOUNT      TO WLL2-AMOUNT
           COMPUTE WLL2-RATE = MI-ANNUAL-RATE
           MOVE WS-TERM-MONTHS      TO WLL2-TERM
           MOVE WS-MONTHLY-PAYMENT  TO WLL2-PAYMENT
           WRITE REPORT-LINE FROM WS-RPT-LOAN-LINE-2
           ADD 1 TO WS-LINE-COUNT

           WRITE REPORT-LINE FROM WS-BLANK-LINE
           WRITE REPORT-LINE FROM WS-RPT-COL-HDR
           WRITE REPORT-LINE FROM WS-RPT-COL-DASH
           ADD 3 TO WS-LINE-COUNT
           .

      *================================================================*
      * CHECK PAGE BREAK AND PRINT PAGE HEADER IF NEEDED               *
      *================================================================*
       5500-CHECK-PAGE.
           IF WS-LINE-COUNT >= WS-LINES-PER-PAGE
               ADD 1 TO WS-PAGE-NUMBER
               MOVE WS-PAGE-NUMBER TO WH1-PAGE
               IF WS-PAGE-NUMBER > 1
                   WRITE REPORT-LINE FROM WS-BLANK-LINE
                       AFTER ADVANCING PAGE
               END-IF
               WRITE REPORT-LINE FROM WS-RPT-HEADER-1
               WRITE REPORT-LINE FROM WS-RPT-HEADER-2
               WRITE REPORT-LINE FROM WS-BLANK-LINE
               MOVE 3 TO WS-LINE-COUNT
           END-IF
           .

      *================================================================*
      * GENERATE COMPLETE AMORTIZATION SCHEDULE                        *
      *================================================================*
       6000-GENERATE-SCHEDULE.
           PERFORM VARYING WS-CURRENT-MONTH
               FROM 1 BY 1
               UNTIL WS-CURRENT-MONTH > WS-TERM-MONTHS
               OR WS-CURRENT-BALANCE <= ZEROS
               OR CALC-ERROR

               PERFORM 5500-CHECK-PAGE

      *        Check for ARM rate adjustment
               IF MI-ARM-LOAN
                   AND ARM-NOT-ADJUSTED
                   AND WS-CURRENT-MONTH > WS-ARM-FIXED-MONTHS
                   PERFORM 6500-ADJUST-ARM-RATE
               END-IF

      *        Calculate this month's payment allocation
               PERFORM 6100-CALC-MONTHLY-ALLOCATION
           END-PERFORM
           .

      *================================================================*
      * CALCULATE ONE MONTH'S INTEREST/PRINCIPAL ALLOCATION            *
      *================================================================*
       6100-CALC-MONTHLY-ALLOCATION.
      *    Step 1: Interest = remaining balance * monthly rate
           COMPUTE WS-INTEREST-PORTION ROUNDED =
               WS-CURRENT-BALANCE * WS-MONTHLY-RATE
               ON SIZE ERROR
                   SET CALC-ERROR TO TRUE
                   DISPLAY 'SIZE ERROR IN INTEREST CALC'
           END-COMPUTE

      *    Step 2: Principal = payment - interest
           COMPUTE WS-PRINCIPAL-PORTION =
               WS-MONTHLY-PAYMENT - WS-INTEREST-PORTION
               ON SIZE ERROR
                   SET CALC-ERROR TO TRUE
                   DISPLAY 'SIZE ERROR IN PRINCIPAL CALC'
           END-COMPUTE

      *    Step 3: Final payment adjustment
      *    The last payment must be adjusted so the balance
      *    reaches exactly zero. Accumulated rounding across
      *    hundreds of payments means the final regular payment
      *    would leave a small residual balance (positive or
      *    negative). We detect this and adjust accordingly.
           IF WS-PRINCIPAL-PORTION >= WS-CURRENT-BALANCE
               MOVE WS-CURRENT-BALANCE TO WS-PRINCIPAL-PORTION
               COMPUTE WS-MONTHLY-PAYMENT =
                   WS-PRINCIPAL-PORTION + WS-INTEREST-PORTION
           END-IF

      *    Step 4: Update the remaining balance
           SUBTRACT WS-PRINCIPAL-PORTION FROM WS-CURRENT-BALANCE

      *    Step 5: Guard against tiny negative balances from
      *    rounding
           IF WS-CURRENT-BALANCE < ZEROS
               ADD WS-CURRENT-BALANCE TO WS-PRINCIPAL-PORTION
               MOVE ZEROS TO WS-CURRENT-BALANCE
           END-IF

      *    Step 6: Accumulate totals
           ADD WS-INTEREST-PORTION  TO WS-TOTAL-INTEREST
           ADD WS-PRINCIPAL-PORTION TO WS-TOTAL-PRINCIPAL
           ADD WS-MONTHLY-PAYMENT   TO WS-TOTAL-PAYMENTS
           ADD 1                    TO WS-PAYMENT-COUNT

      *    Step 7: Write detail line
           PERFORM 6200-WRITE-DETAIL
           .

      *================================================================*
      * WRITE ONE DETAIL LINE OF THE AMORTIZATION SCHEDULE             *
      *================================================================*
       6200-WRITE-DETAIL.
           MOVE WS-CURRENT-MONTH     TO WD-PMT-NUM
           MOVE WS-MONTHLY-PAYMENT   TO WD-PMT-AMOUNT
           MOVE WS-INTEREST-PORTION  TO WD-INTEREST
           MOVE WS-PRINCIPAL-PORTION TO WD-PRINCIPAL
           MOVE WS-CURRENT-BALANCE   TO WD-BALANCE
           MOVE WS-TOTAL-INTEREST    TO WD-CUM-INT

           WRITE REPORT-LINE FROM WS-RPT-DETAIL
           ADD 1 TO WS-LINE-COUNT
           .

      *================================================================*
      * ADJUST ARM RATE AFTER FIXED PERIOD EXPIRES                     *
      *                                                                *
      * When the introductory fixed period ends on an adjustable-rate  *
      * mortgage, the rate changes to the adjustment rate. The monthly *
      * payment is recalculated based on the remaining balance and     *
      * remaining term at the new rate.                                *
      *================================================================*
       6500-ADJUST-ARM-RATE.
           SET ARM-RATE-ADJUSTED TO TRUE

      *    Apply rate cap if adjustment exceeds maximum
           IF WS-ARM-ADJ-RATE > WS-ARM-RATE-CAP
               MOVE WS-ARM-RATE-CAP TO WS-ARM-NEW-RATE
           ELSE
               MOVE WS-ARM-ADJ-RATE TO WS-ARM-NEW-RATE
           END-IF

      *    Update the monthly rate
           COMPUTE WS-MONTHLY-RATE =
               WS-ARM-NEW-RATE / 12

      *    Calculate remaining months
           COMPUTE WS-REMAINING-MONTHS =
               WS-TERM-MONTHS - WS-CURRENT-MONTH + 1

      *    Recalculate monthly payment for remaining balance
      *    and remaining term at the new rate
           PERFORM 4000-CALCULATE-PAYMENT

      *    Write rate adjustment notice
           COMPUTE WARN-NEW-RATE = WS-ARM-NEW-RATE * 100
           MOVE WS-MONTHLY-PAYMENT TO WARN-NEW-PMT
           WRITE REPORT-LINE FROM WS-RPT-ARM-NOTICE
           ADD 1 TO WS-LINE-COUNT
           .

      *================================================================*
      * WRITE LOAN SUMMARY TOTALS                                      *
      *================================================================*
       7000-WRITE-SUMMARY.
           WRITE REPORT-LINE FROM WS-RPT-SUMMARY

           MOVE WS-TOTAL-PRINCIPAL TO WSUM-PRINCIPAL
           MOVE WS-TOTAL-INTEREST  TO WSUM-INTEREST
           WRITE REPORT-LINE FROM WS-RPT-SUM-LINE-1

           MOVE WS-TOTAL-PAYMENTS  TO WSUM-ALL-PMTS
           MOVE WS-PAYMENT-COUNT   TO WSUM-PMT-COUNT
           WRITE REPORT-LINE FROM WS-RPT-SUM-LINE-2

           WRITE REPORT-LINE FROM WS-BLANK-LINE
           .

      *================================================================*
      * READ NEXT MORTGAGE INPUT RECORD                                *
      *================================================================*
       8000-READ-INPUT.
           READ MORTGAGE-INPUT
               AT END
                   SET END-OF-FILE TO TRUE
           END-READ
           .

      *================================================================*
      * CLOSE FILES AND DISPLAY SUMMARY                                *
      *================================================================*
       9000-FINALIZE.
           CLOSE MORTGAGE-INPUT
                 MORTGAGE-REPORT

           DISPLAY 'MORTGAGE CALCULATION COMPLETE'
           DISPLAY '  LOANS PROCESSED: ' WS-LOANS-PROCESSED
           .

The JCL to execute this program on a z/OS system follows:

//MORTCALC JOB (ACCT),'MORTGAGE CALC',CLASS=A,
//         MSGCLASS=X,NOTIFY=&SYSUID
//*
//*================================================================*
//* MORTGAGE PAYMENT CALCULATOR - EXECUTION JCL                    *
//*================================================================*
//*
//STEP01   EXEC PGM=MORTCALC
//STEPLIB  DD   DSN=HFCU.MORTGAGE.LOADLIB,DISP=SHR
//MORTPARM DD   DSN=HFCU.MORTGAGE.INPUT,DISP=SHR
//MORTRPT  DD   DSN=HFCU.MORTGAGE.REPORT,DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(5,2),RLSE),
//         DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD   SYSOUT=*

How the Program Works

Parameter Loading (3000-LOAD-PARAMETERS)

The program reads each mortgage record and converts the annual interest rate from a percentage to a decimal by dividing by 100. It then derives the monthly rate by dividing by 12. The monthly rate field carries 10 decimal places to preserve precision through subsequent calculations. For ARM loans, the adjustment rate and rate cap are also loaded.

Payment Calculation (4000-CALCULATE-PAYMENT)

Instead of using the COBOL exponentiation operator (**), which may internally convert COMP-3 values to floating-point, the program computes (1 + r)^n through iterative multiplication in a PERFORM VARYING loop. This keeps all arithmetic in packed decimal, eliminating the floating-point conversion that could introduce binary representation errors. The result is then used in the standard amortization formula to compute the monthly payment, rounded to the nearest cent.

Amortization Schedule (6000/6100)

For each month, the program calculates interest on the remaining balance, derives the principal portion by subtracting interest from the payment, and reduces the balance. The final payment is automatically adjusted when the principal portion would exceed the remaining balance, ensuring the schedule ends at exactly zero.

ARM Rate Adjustment (6500-ADJUST-ARM-RATE)

When the fixed period of an adjustable-rate mortgage expires, the program applies the new rate (subject to the rate cap) and recalculates the monthly payment based on the remaining balance and remaining term. The recalculation reuses the same 4000-CALCULATE-PAYMENT paragraph, demonstrating modular design. A notice line is inserted in the report showing the new rate and payment.

Precision Considerations

Several design choices in this program ensure penny-exact results:

  1. WS-MONTHLY-RATE uses PIC SV9(10): Dividing 6.5% by 12 yields 0.00541666..., a repeating decimal. Ten decimal places capture this with sufficient precision.

  2. WS-POWER-FACTOR uses PIC S9(5)V9(12): The growth factor raised to the power of 360 can produce values like 6.989+, requiring both integer and extensive decimal capacity.

  3. Iterative multiplication instead of exponentiation: Keeps all intermediate results in COMP-3, avoiding any floating-point conversion.

  4. ROUNDED on every monetary COMPUTE: Ensures half-cent values round to the nearest cent rather than being truncated downward.

  5. ON SIZE ERROR on critical calculations: Detects any overflow condition that would corrupt the result.

  6. Final payment adjustment: The explicit check in paragraph 6100 guarantees the schedule balances to zero regardless of accumulated rounding across hundreds of payments.

Discussion Questions

  1. Why does the program use iterative multiplication (a PERFORM VARYING loop) instead of the ** operator to compute the power factor? What would happen if the ** operator internally converted COMP-3 values to floating-point?

  2. Explain why the final payment in an amortization schedule is almost never equal to the regular monthly payment. How large can this difference be for a typical 30-year mortgage?

  3. When an ARM rate adjusts, the program recalculates the payment based on the remaining balance and remaining term. Why is this recalculation necessary? What would happen if the original payment amount continued at the new rate?

  4. The program uses PIC S9(02)V9(10) for the monthly rate and PIC S9(05)V9(12) for the power factor. What would happen if these fields used only 2 decimal places (PIC V99)?

  5. Consider a scenario where two loan officers calculate a payment for the same mortgage parameters on different systems -- one running this COBOL program, the other using a spreadsheet with floating-point arithmetic. Under what circumstances might they get different payment amounts, and what are the regulatory implications?

Connection to Chapter Concepts

This case study directly applies these Chapter 33 concepts:

  • COMP-3 packed decimal: All monetary and rate fields use COMP-3 for exact decimal arithmetic (Section 33.2).
  • ROUNDED phrase: Every COMPUTE that produces a monetary result uses ROUNDED (Section 33.3).
  • ON SIZE ERROR: Overflow detection on all critical calculations (Section 33.3).
  • Intermediate precision: Rate and power factor fields carry 10-12 decimal places (Section 33.2).
  • Amortization formula: The standard M = P[r(1+r)^n] / [(1+r)^n - 1] formula implemented with proper decimal handling (Section 33.4).
  • Final payment reconciliation: Adjusting the last payment for exact zero balance (Section 33.4).
  • Iterative compounding: Using PERFORM VARYING instead of ** for packed-decimal precision (Section 33.3).