13 min read

Financial calculations represent the heart of COBOL's original purpose and its enduring strength. When Grace Hopper and her colleagues designed COBOL in 1959, they built a language that treats decimal arithmetic as a first-class citizen rather than...

Chapter 33: Financial Calculations in COBOL

Part VII - COBOL in Financial Systems

Financial calculations represent the heart of COBOL's original purpose and its enduring strength. When Grace Hopper and her colleagues designed COBOL in 1959, they built a language that treats decimal arithmetic as a first-class citizen rather than an afterthought. Today, over six decades later, COBOL processes an estimated $3 trillion in daily commerce precisely because its arithmetic model aligns perfectly with the requirements of financial computing. This chapter explores the full spectrum of financial calculations in COBOL, from simple interest computations to complex amortization schedules, from tax bracket calculations to present value analysis. Every example presents complete, working code that you can compile and run on any standard COBOL compiler.


33.1 Why COBOL for Financial Calculations

The Floating-Point Problem

Before examining COBOL's strengths, it is essential to understand why languages based on IEEE 754 floating-point arithmetic present serious problems for financial work. Consider what happens in many languages when you add 0.1 and 0.2:

0.1 + 0.2 = 0.30000000000000004

This occurs because 0.1 and 0.2 cannot be represented exactly in binary floating-point. While the error appears insignificant, in financial systems processing millions of transactions daily, these tiny discrepancies accumulate into material differences that violate regulatory requirements and destroy audit trails.

COBOL's Fixed-Point Decimal Advantage

COBOL stores numbers exactly as decimal values. When you define a field as PIC 9(5)V99, COBOL stores exactly five integer digits and two decimal digits. The value 123.45 is stored as precisely 123.45, not as a binary approximation. This design decision, made in 1959, remains the single most important reason COBOL dominates financial computing.

The advantages extend beyond simple accuracy:

  • Predictable rounding: COBOL's ROUNDED phrase applies deterministic rounding rules that auditors can verify.
  • Exact intermediate results: Arithmetic operations on packed-decimal fields produce exact intermediate results up to 18 or 31 digits depending on the compiler.
  • No representation surprises: The value stored is the value you specified, period.
  • Regulatory compliance: Financial regulators require exact decimal arithmetic for interest calculations, tax computations, and fee assessments.

Real-World Impact

In 2012, a major brokerage firm discovered that its Java-based pricing engine accumulated rounding errors of $0.003 per trade. Across 50 million daily trades, this produced a daily discrepancy of $150,000. The firm's COBOL-based back-office settlement system, processing the same trades, balanced to the penny every day. This is not an isolated case; it reflects a fundamental architectural difference between floating-point and fixed-point decimal arithmetic.


33.2 COBOL Decimal Arithmetic Fundamentals

As introduced in Chapter 3 (Data Types and PICTURE Clauses) and Chapter 6 (Arithmetic Operations), COBOL provides several numeric storage formats. In financial work, the choice of storage format directly affects both performance and precision.

PACKED-DECIMAL (COMP-3)

Packed-decimal storage, specified with USAGE COMP-3 or USAGE PACKED-DECIMAL, stores two decimal digits per byte with a trailing sign nibble. This is the preferred format for financial calculations on IBM mainframes because the hardware provides native packed-decimal arithmetic instructions.

       01  WS-TRANSACTION-AMOUNT    PIC S9(13)V99 COMP-3.
       01  WS-INTEREST-RATE         PIC S9(3)V9(6) COMP-3.
       01  WS-BALANCE               PIC S9(15)V99 COMP-3.

Storage characteristics: - PIC S9(13)V99 COMP-3 occupies 8 bytes (15 digits = 8 bytes) - PIC S9(3)V9(6) COMP-3 occupies 5 bytes (9 digits = 5 bytes) - Formula: CEIL((number-of-digits + 1) / 2) bytes

BINARY (COMP / COMP-4 / COMP-5)

Binary storage is efficient for counters, subscripts, and fields used primarily in comparisons rather than decimal arithmetic. However, binary fields require conversion for decimal operations, which can introduce performance overhead in calculation-intensive programs.

       01  WS-RECORD-COUNT          PIC S9(8) COMP.
       01  WS-LOOP-INDEX            PIC S9(4) COMP.
       01  WS-TABLE-SIZE            PIC S9(4) COMP VALUE 360.

DISPLAY Numeric

DISPLAY numeric stores one digit per byte in character format. While least space-efficient, DISPLAY numeric is valuable for fields that appear directly in reports or flat file output without conversion.

       01  WS-PRINT-AMOUNT          PIC Z(10)9.99.
       01  WS-REPORT-RATE           PIC Z9.999999.

Choosing the Right Format for Financial Work

The following table summarizes the recommended usage:

Scenario Recommended Format Reason
Monetary amounts COMP-3 Native decimal arithmetic, exact precision
Interest rates COMP-3 Requires decimal precision beyond 2 places
Loop counters COMP Binary arithmetic is faster for iteration
Record counts COMP No decimal arithmetic needed
Report fields DISPLAY Direct output without conversion
File output amounts COMP-3 or DISPLAY Depends on file format requirements

Precision Planning

Financial calculations frequently require intermediate results with more decimal places than the final result. A sound practice is to define working fields with extra precision:

       01  WS-CALC-FIELDS.
           05  WS-DAILY-RATE        PIC S9(3)V9(10) COMP-3.
           05  WS-ACCUM-INTEREST    PIC S9(15)V9(6) COMP-3.
           05  WS-ROUND-RESULT      PIC S9(13)V99   COMP-3.

By carrying ten decimal places in WS-DAILY-RATE and six in WS-ACCUM-INTEREST, you ensure that rounding occurs only at the final step when the result is stored in the two-decimal-place output field.


33.3 Interest Calculations

Interest calculations are the most fundamental financial computations in banking and lending systems. COBOL programs calculate interest for savings accounts, certificates of deposit, mortgages, personal loans, credit cards, and commercial lending facilities.

Simple Interest

Simple interest is calculated on the original principal only. The formula is:

Interest = Principal x Rate x Time

The following complete program calculates simple interest for a batch of loan records:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SIMPLE-INTEREST.
       AUTHOR. FINANCIAL-SYSTEMS-TEAM.
      *================================================================*
      * PROGRAM: SIMPLE-INTEREST                                       *
      * PURPOSE: Calculate simple interest for a batch of loan records  *
      * DATE:    2024-01-15                                            *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT LOAN-FILE    ASSIGN TO 'LOANIN'
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-LOAN-STATUS.
           SELECT REPORT-FILE  ASSIGN TO 'RPTOUT'
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-RPT-STATUS.

       DATA DIVISION.
       FILE SECTION.

       FD  LOAN-FILE
           RECORDING MODE IS F
           RECORD CONTAINS 80 CHARACTERS.
       01  LOAN-RECORD.
           05  LR-ACCOUNT-NUM       PIC X(10).
           05  LR-PRINCIPAL          PIC S9(11)V99 COMP-3.
           05  LR-ANNUAL-RATE        PIC S9(3)V9(6) COMP-3.
           05  LR-TERM-DAYS          PIC S9(5) COMP-3.
           05  LR-START-DATE         PIC 9(8).
           05  FILLER                PIC X(41).

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

       WORKING-STORAGE SECTION.

       01  WS-FILE-STATUS.
           05  WS-LOAN-STATUS        PIC XX.
           05  WS-RPT-STATUS         PIC XX.

       01  WS-FLAGS.
           05  WS-EOF-FLAG           PIC X VALUE 'N'.
               88  END-OF-FILE       VALUE 'Y'.

       01  WS-COUNTERS.
           05  WS-RECORDS-READ       PIC S9(8) COMP VALUE 0.
           05  WS-RECORDS-WRITTEN    PIC S9(8) COMP VALUE 0.

       01  WS-CALC-FIELDS.
           05  WS-DAILY-RATE         PIC S9(3)V9(10) COMP-3.
           05  WS-INTEREST-AMT       PIC S9(13)V9(4) COMP-3.
           05  WS-ROUNDED-INT        PIC S9(13)V99   COMP-3.
           05  WS-TOTAL-INTEREST     PIC S9(15)V99   COMP-3
                                     VALUE 0.

       01  WS-REPORT-HEADER.
           05  FILLER                PIC X(10) VALUE 'ACCOUNT   '.
           05  FILLER                PIC X(20) VALUE 'PRINCIPAL           '.
           05  FILLER                PIC X(12) VALUE 'RATE        '.
           05  FILLER                PIC X(10) VALUE 'DAYS      '.
           05  FILLER                PIC X(20) VALUE 'INTEREST            '.
           05  FILLER                PIC X(60) VALUE SPACES.

       01  WS-REPORT-DETAIL.
           05  WS-RPT-ACCOUNT       PIC X(10).
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-RPT-PRINCIPAL     PIC Z(10)9.99.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-RPT-RATE          PIC Z9.999999.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-RPT-DAYS          PIC Z(4)9.
           05  FILLER                PIC X(2) VALUE SPACES.
           05  WS-RPT-INTEREST      PIC Z(10)9.99.
           05  FILLER                PIC X(55) VALUE SPACES.

       01  WS-REPORT-TOTAL.
           05  FILLER                PIC X(46) VALUE SPACES.
           05  FILLER                PIC X(16)
                                     VALUE 'TOTAL INTEREST: '.
           05  WS-RPT-TOTAL-INT     PIC Z(12)9.99.
           05  FILLER                PIC X(53) VALUE SPACES.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-LOANS
               UNTIL END-OF-FILE
           PERFORM 3000-FINALIZE
           STOP RUN
           .

       1000-INITIALIZE.
           OPEN INPUT  LOAN-FILE
                OUTPUT REPORT-FILE
           IF WS-LOAN-STATUS NOT = '00'
               DISPLAY 'ERROR OPENING LOAN FILE: ' WS-LOAN-STATUS
               STOP RUN
           END-IF
           WRITE REPORT-RECORD FROM WS-REPORT-HEADER
           PERFORM 9000-READ-LOAN
           .

       2000-PROCESS-LOANS.
           ADD 1 TO WS-RECORDS-READ

      *    Calculate daily rate: annual rate / 365
           DIVIDE LR-ANNUAL-RATE BY 365
               GIVING WS-DAILY-RATE ROUNDED

      *    Simple interest = principal * daily rate * number of days
           MULTIPLY LR-PRINCIPAL BY WS-DAILY-RATE
               GIVING WS-INTEREST-AMT ROUNDED
           MULTIPLY WS-INTEREST-AMT BY LR-TERM-DAYS
               GIVING WS-INTEREST-AMT ROUNDED

      *    Round to cents for final result
           MOVE WS-INTEREST-AMT TO WS-ROUNDED-INT
           ADD WS-ROUNDED-INT TO WS-TOTAL-INTEREST

      *    Format report line
           MOVE LR-ACCOUNT-NUM      TO WS-RPT-ACCOUNT
           MOVE LR-PRINCIPAL        TO WS-RPT-PRINCIPAL
           MOVE LR-ANNUAL-RATE      TO WS-RPT-RATE
           MOVE LR-TERM-DAYS        TO WS-RPT-DAYS
           MOVE WS-ROUNDED-INT      TO WS-RPT-INTEREST

           WRITE REPORT-RECORD FROM WS-REPORT-DETAIL
           ADD 1 TO WS-RECORDS-WRITTEN

           PERFORM 9000-READ-LOAN
           .

       3000-FINALIZE.
           MOVE WS-TOTAL-INTEREST TO WS-RPT-TOTAL-INT
           WRITE REPORT-RECORD FROM WS-REPORT-TOTAL
           CLOSE LOAN-FILE
                 REPORT-FILE
           DISPLAY 'SIMPLE INTEREST CALCULATION COMPLETE'
           DISPLAY 'RECORDS READ:    ' WS-RECORDS-READ
           DISPLAY 'RECORDS WRITTEN: ' WS-RECORDS-WRITTEN
           DISPLAY 'TOTAL INTEREST:  ' WS-TOTAL-INTEREST
           .

       9000-READ-LOAN.
           READ LOAN-FILE
               AT END SET END-OF-FILE TO TRUE
           END-READ
           .

Compound Interest

Compound interest is calculated on both the original principal and accumulated interest. The formula is:

A = P x (1 + r/n)^(n*t)

Where A is the final amount, P is the principal, r is the annual rate, n is the number of compounding periods per year, and t is the time in years.

COBOL does not have a built-in exponentiation operator for non-integer exponents when using packed-decimal fields. The standard approach is to use iterative multiplication:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COMPOUND-INTEREST.
      *================================================================*
      * PROGRAM: COMPOUND-INTEREST                                     *
      * PURPOSE: Calculate compound interest with various compounding   *
      *          frequencies (monthly, quarterly, daily)                *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-INPUT-FIELDS.
           05  WS-PRINCIPAL          PIC S9(13)V99 COMP-3.
           05  WS-ANNUAL-RATE        PIC S9(3)V9(6) COMP-3.
           05  WS-YEARS              PIC S9(3) COMP-3.
           05  WS-COMPOUND-FREQ      PIC S9(3) COMP-3.
               88  FREQ-DAILY        VALUE 365.
               88  FREQ-MONTHLY      VALUE 12.
               88  FREQ-QUARTERLY    VALUE 4.
               88  FREQ-SEMI-ANNUAL  VALUE 2.
               88  FREQ-ANNUAL       VALUE 1.

       01  WS-CALC-FIELDS.
           05  WS-PERIOD-RATE        PIC S9(3)V9(10) COMP-3.
           05  WS-GROWTH-FACTOR      PIC S9(5)V9(10) COMP-3.
           05  WS-TOTAL-PERIODS      PIC S9(7) COMP-3.
           05  WS-PERIOD-COUNTER     PIC S9(7) COMP-3.
           05  WS-ACCUMULATOR        PIC S9(15)V9(6) COMP-3.
           05  WS-COMPOUND-AMOUNT    PIC S9(15)V99 COMP-3.
           05  WS-INTEREST-EARNED    PIC S9(15)V99 COMP-3.

       01  WS-DISPLAY-FIELDS.
           05  WS-DISP-PRINCIPAL     PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-AMOUNT        PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-INTEREST      PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-RATE          PIC Z9.999999.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: $100,000 at 5.25% compounded monthly for 10 years
           MOVE 100000.00            TO WS-PRINCIPAL
           MOVE 5.250000             TO WS-ANNUAL-RATE
           MOVE 10                   TO WS-YEARS
           SET  FREQ-MONTHLY         TO TRUE

           PERFORM 1000-CALCULATE-COMPOUND-INTEREST

           MOVE WS-PRINCIPAL         TO WS-DISP-PRINCIPAL
           MOVE WS-COMPOUND-AMOUNT   TO WS-DISP-AMOUNT
           MOVE WS-INTEREST-EARNED   TO WS-DISP-INTEREST
           MOVE WS-ANNUAL-RATE       TO WS-DISP-RATE

           DISPLAY '=================================='
           DISPLAY 'COMPOUND INTEREST CALCULATION'
           DISPLAY '=================================='
           DISPLAY 'PRINCIPAL:        ' WS-DISP-PRINCIPAL
           DISPLAY 'ANNUAL RATE:      ' WS-DISP-RATE '%'
           DISPLAY 'COMPOUNDING:      MONTHLY'
           DISPLAY 'YEARS:            ' WS-YEARS
           DISPLAY 'FINAL AMOUNT:     ' WS-DISP-AMOUNT
           DISPLAY 'INTEREST EARNED:  ' WS-DISP-INTEREST

           STOP RUN
           .

       1000-CALCULATE-COMPOUND-INTEREST.
      *    Calculate period rate = annual rate / compounding frequency
           DIVIDE WS-ANNUAL-RATE BY 100
               GIVING WS-PERIOD-RATE
           DIVIDE WS-PERIOD-RATE BY WS-COMPOUND-FREQ
               GIVING WS-PERIOD-RATE ROUNDED

      *    Growth factor per period = 1 + period rate
           ADD 1 TO WS-PERIOD-RATE
               GIVING WS-GROWTH-FACTOR

      *    Total periods = frequency * years
           MULTIPLY WS-COMPOUND-FREQ BY WS-YEARS
               GIVING WS-TOTAL-PERIODS

      *    Iterative compounding: multiply growth factor
      *    for each period
           MOVE 1.0 TO WS-ACCUMULATOR
           PERFORM VARYING WS-PERIOD-COUNTER FROM 1 BY 1
               UNTIL WS-PERIOD-COUNTER > WS-TOTAL-PERIODS
               MULTIPLY WS-ACCUMULATOR BY WS-GROWTH-FACTOR
                   GIVING WS-ACCUMULATOR ROUNDED
           END-PERFORM

      *    Final amount = principal * accumulated growth factor
           MULTIPLY WS-PRINCIPAL BY WS-ACCUMULATOR
               GIVING WS-COMPOUND-AMOUNT ROUNDED

      *    Interest earned = final amount - principal
           SUBTRACT WS-PRINCIPAL FROM WS-COMPOUND-AMOUNT
               GIVING WS-INTEREST-EARNED
           .

Daily Interest Accrual

Many financial institutions calculate interest on a daily basis. This is particularly important for savings accounts, money market accounts, and commercial loans where balances change frequently:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DAILY-ACCRUAL.
      *================================================================*
      * PROGRAM: DAILY-ACCRUAL                                         *
      * PURPOSE: Calculate daily interest accrual for accounts with     *
      *          varying balances throughout the month                  *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-ACCRUAL-TABLE.
           05  WS-DAILY-ENTRY OCCURS 31 TIMES.
               10  WS-DAY-BALANCE    PIC S9(13)V99 COMP-3.
               10  WS-DAY-ACCRUAL    PIC S9(9)V9(6) COMP-3.

       01  WS-CALC-FIELDS.
           05  WS-ANNUAL-RATE        PIC S9(3)V9(6) COMP-3.
           05  WS-DAILY-RATE         PIC S9(3)V9(10) COMP-3.
           05  WS-DAYS-IN-MONTH      PIC S9(3) COMP-3.
           05  WS-DAYS-IN-YEAR       PIC S9(3) COMP-3 VALUE 365.
           05  WS-DAY-INDEX          PIC S9(3) COMP-3.
           05  WS-MONTH-ACCRUAL      PIC S9(13)V99 COMP-3.
           05  WS-ACCUM-ACCRUAL      PIC S9(13)V9(6) COMP-3.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: 4.50% annual rate, 30-day month
           MOVE 4.500000            TO WS-ANNUAL-RATE
           MOVE 30                  TO WS-DAYS-IN-MONTH

      *    Set up sample daily balances (varying throughout month)
           MOVE 50000.00 TO WS-DAY-BALANCE(1)
           MOVE 50000.00 TO WS-DAY-BALANCE(2)
           MOVE 50000.00 TO WS-DAY-BALANCE(3)
           MOVE 50000.00 TO WS-DAY-BALANCE(4)
           MOVE 50000.00 TO WS-DAY-BALANCE(5)
           MOVE 48500.00 TO WS-DAY-BALANCE(6)
           MOVE 48500.00 TO WS-DAY-BALANCE(7)
           MOVE 48500.00 TO WS-DAY-BALANCE(8)
           MOVE 48500.00 TO WS-DAY-BALANCE(9)
           MOVE 48500.00 TO WS-DAY-BALANCE(10)
           MOVE 48500.00 TO WS-DAY-BALANCE(11)
           MOVE 48500.00 TO WS-DAY-BALANCE(12)
           MOVE 48500.00 TO WS-DAY-BALANCE(13)
           MOVE 48500.00 TO WS-DAY-BALANCE(14)
           MOVE 51200.00 TO WS-DAY-BALANCE(15)
           MOVE 51200.00 TO WS-DAY-BALANCE(16)
           MOVE 51200.00 TO WS-DAY-BALANCE(17)
           MOVE 51200.00 TO WS-DAY-BALANCE(18)
           MOVE 51200.00 TO WS-DAY-BALANCE(19)
           MOVE 51200.00 TO WS-DAY-BALANCE(20)
           MOVE 51200.00 TO WS-DAY-BALANCE(21)
           MOVE 51200.00 TO WS-DAY-BALANCE(22)
           MOVE 51200.00 TO WS-DAY-BALANCE(23)
           MOVE 51200.00 TO WS-DAY-BALANCE(24)
           MOVE 51200.00 TO WS-DAY-BALANCE(25)
           MOVE 53750.00 TO WS-DAY-BALANCE(26)
           MOVE 53750.00 TO WS-DAY-BALANCE(27)
           MOVE 53750.00 TO WS-DAY-BALANCE(28)
           MOVE 53750.00 TO WS-DAY-BALANCE(29)
           MOVE 53750.00 TO WS-DAY-BALANCE(30)

           PERFORM 1000-CALCULATE-DAILY-ACCRUAL

           DISPLAY 'MONTHLY ACCRUED INTEREST: ' WS-MONTH-ACCRUAL
           STOP RUN
           .

       1000-CALCULATE-DAILY-ACCRUAL.
      *    Daily rate = annual rate / 100 / days in year
           DIVIDE WS-ANNUAL-RATE BY 100
               GIVING WS-DAILY-RATE
           DIVIDE WS-DAILY-RATE BY WS-DAYS-IN-YEAR
               GIVING WS-DAILY-RATE ROUNDED

           MOVE ZERO TO WS-ACCUM-ACCRUAL

           PERFORM VARYING WS-DAY-INDEX FROM 1 BY 1
               UNTIL WS-DAY-INDEX > WS-DAYS-IN-MONTH

      *        Daily accrual = balance * daily rate
               MULTIPLY WS-DAY-BALANCE(WS-DAY-INDEX)
                   BY WS-DAILY-RATE
                   GIVING WS-DAY-ACCRUAL(WS-DAY-INDEX) ROUNDED

               ADD WS-DAY-ACCRUAL(WS-DAY-INDEX)
                   TO WS-ACCUM-ACCRUAL

           END-PERFORM

      *    Round accumulated accrual to cents
           MOVE WS-ACCUM-ACCRUAL TO WS-MONTH-ACCRUAL
           .

APR to APY Conversion

The Annual Percentage Rate (APR) and Annual Percentage Yield (APY) are related but distinct measures. APY accounts for the effect of compounding:

APY = (1 + APR/n)^n - 1

Where n is the number of compounding periods per year.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. APR-APY-CONVERT.
      *================================================================*
      * PROGRAM: APR-APY-CONVERT                                       *
      * PURPOSE: Convert between APR and APY for Truth-in-Lending and  *
      *          Truth-in-Savings disclosures                          *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-INPUT-FIELDS.
           05  WS-APR               PIC S9(3)V9(6) COMP-3.
           05  WS-COMPOUND-PERIODS  PIC S9(3) COMP-3.

       01  WS-CALC-FIELDS.
           05  WS-PERIOD-RATE       PIC S9(3)V9(10) COMP-3.
           05  WS-GROWTH-FACTOR     PIC S9(5)V9(10) COMP-3.
           05  WS-ACCUMULATED       PIC S9(5)V9(10) COMP-3.
           05  WS-PERIOD-CTR        PIC S9(3) COMP-3.
           05  WS-APY               PIC S9(3)V9(6) COMP-3.

       01  WS-DISPLAY-FIELDS.
           05  WS-DISP-APR          PIC Z9.999999.
           05  WS-DISP-APY          PIC Z9.999999.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: 5.00% APR compounded monthly
           MOVE 5.000000            TO WS-APR
           MOVE 12                  TO WS-COMPOUND-PERIODS

           PERFORM 1000-APR-TO-APY

           MOVE WS-APR TO WS-DISP-APR
           MOVE WS-APY TO WS-DISP-APY
           DISPLAY 'APR: ' WS-DISP-APR '% -> APY: ' WS-DISP-APY '%'

      *    Example: 5.00% APR compounded daily
           MOVE 5.000000            TO WS-APR
           MOVE 365                 TO WS-COMPOUND-PERIODS

           PERFORM 1000-APR-TO-APY

           MOVE WS-APR TO WS-DISP-APR
           MOVE WS-APY TO WS-DISP-APY
           DISPLAY 'APR: ' WS-DISP-APR '% -> APY: ' WS-DISP-APY '%'

           STOP RUN
           .

       1000-APR-TO-APY.
      *    Period rate = APR / 100 / n
           DIVIDE WS-APR BY 100
               GIVING WS-PERIOD-RATE
           DIVIDE WS-PERIOD-RATE BY WS-COMPOUND-PERIODS
               GIVING WS-PERIOD-RATE ROUNDED

      *    Growth factor = 1 + period rate
           ADD 1 TO WS-PERIOD-RATE
               GIVING WS-GROWTH-FACTOR

      *    Accumulated = growth factor ^ n (iterative)
           MOVE 1.0 TO WS-ACCUMULATED
           PERFORM VARYING WS-PERIOD-CTR FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-COMPOUND-PERIODS
               MULTIPLY WS-ACCUMULATED BY WS-GROWTH-FACTOR
                   GIVING WS-ACCUMULATED ROUNDED
           END-PERFORM

      *    APY = (accumulated - 1) * 100
           SUBTRACT 1 FROM WS-ACCUMULATED
           MULTIPLY WS-ACCUMULATED BY 100
               GIVING WS-APY ROUNDED
           .

33.4 Amortization Schedules

Amortization schedules are among the most common financial calculations in banking. Every mortgage, auto loan, and personal loan requires a payment schedule showing how each payment is split between principal and interest.

The Amortization Formula

The standard monthly payment formula is:

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

Where M is the monthly payment, P is the loan principal, r is the monthly interest rate, and n is the total number of payments.

Complete Amortization Schedule Program

The following program generates a full amortization schedule for a fixed-rate loan:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. AMORT-SCHEDULE.
      *================================================================*
      * PROGRAM: AMORT-SCHEDULE                                        *
      * PURPOSE: Generate complete amortization schedule for fixed-rate *
      *          loans (mortgage, auto, personal)                      *
      * NOTES:   Handles standard US mortgage conventions with final   *
      *          payment adjustment for rounding                       *
      *================================================================*

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT AMORT-REPORT ASSIGN TO 'AMORTOUT'
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-RPT-STATUS.

       DATA DIVISION.
       FILE SECTION.

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

       WORKING-STORAGE SECTION.

       01  WS-RPT-STATUS            PIC XX.

       01  WS-LOAN-PARAMS.
           05  WS-LOAN-AMOUNT       PIC S9(13)V99 COMP-3.
           05  WS-ANNUAL-RATE       PIC S9(3)V9(6) COMP-3.
           05  WS-TERM-MONTHS       PIC S9(5) COMP-3.
           05  WS-LOAN-TYPE         PIC X(10).

       01  WS-CALC-FIELDS.
           05  WS-MONTHLY-RATE      PIC S9(3)V9(10) COMP-3.
           05  WS-GROWTH-FACTOR     PIC S9(5)V9(10) COMP-3.
           05  WS-POWER-RESULT      PIC S9(7)V9(10) COMP-3.
           05  WS-NUMERATOR         PIC S9(15)V9(10) COMP-3.
           05  WS-DENOMINATOR       PIC S9(15)V9(10) COMP-3.
           05  WS-MONTHLY-PAYMENT   PIC S9(9)V99 COMP-3.
           05  WS-PERIOD-CTR        PIC S9(5) COMP-3.
           05  WS-REMAINING-BAL     PIC S9(13)V99 COMP-3.
           05  WS-INTEREST-PORTION  PIC S9(9)V99 COMP-3.
           05  WS-PRINCIPAL-PORTION PIC S9(9)V99 COMP-3.
           05  WS-TOTAL-INTEREST    PIC S9(15)V99 COMP-3.
           05  WS-TOTAL-PRINCIPAL   PIC S9(15)V99 COMP-3.
           05  WS-TOTAL-PAID        PIC S9(15)V99 COMP-3.
           05  WS-INTEREST-WORK     PIC S9(13)V9(6) COMP-3.

       01  WS-REPORT-HEADER-1.
           05  FILLER PIC X(50) VALUE
               '=================================================='.
           05  FILLER PIC X(50) VALUE
               '=================================================='.
           05  FILLER PIC X(32) VALUE SPACES.

       01  WS-REPORT-HEADER-2.
           05  FILLER PIC X(30) VALUE '  LOAN AMORTIZATION SCHEDULE  '.
           05  FILLER PIC X(102) VALUE SPACES.

       01  WS-LOAN-INFO-1.
           05  FILLER PIC X(16) VALUE '  LOAN AMOUNT:  '.
           05  WS-INFO-AMOUNT   PIC $ZZZ,ZZZ,ZZ9.99.
           05  FILLER PIC X(10) VALUE '   TYPE:  '.
           05  WS-INFO-TYPE     PIC X(10).
           05  FILLER PIC X(80) VALUE SPACES.

       01  WS-LOAN-INFO-2.
           05  FILLER PIC X(16) VALUE '  ANNUAL RATE:  '.
           05  WS-INFO-RATE     PIC Z9.999999.
           05  FILLER PIC X(12) VALUE '%  TERM:    '.
           05  WS-INFO-TERM     PIC ZZ9.
           05  FILLER PIC X(7) VALUE ' MONTHS'.
           05  FILLER PIC X(80) VALUE SPACES.

       01  WS-LOAN-INFO-3.
           05  FILLER PIC X(20) VALUE '  MONTHLY PAYMENT:  '.
           05  WS-INFO-PAYMENT  PIC $ZZZ,ZZ9.99.
           05  FILLER PIC X(100) VALUE SPACES.

       01  WS-COLUMN-HEADER.
           05  FILLER PIC X(8)  VALUE '  MONTH '.
           05  FILLER PIC X(16) VALUE '  PAYMENT       '.
           05  FILLER PIC X(16) VALUE '  INTEREST      '.
           05  FILLER PIC X(16) VALUE '  PRINCIPAL     '.
           05  FILLER PIC X(20) VALUE '  REMAINING BALANCE '.
           05  FILLER PIC X(56) VALUE SPACES.

       01  WS-DETAIL-LINE.
           05  FILLER            PIC X(2) VALUE SPACES.
           05  WS-DET-MONTH     PIC ZZ9.
           05  FILLER            PIC X(3) VALUE SPACES.
           05  WS-DET-PAYMENT   PIC $ZZZ,ZZ9.99.
           05  FILLER            PIC X(4) VALUE SPACES.
           05  WS-DET-INTEREST  PIC $ZZZ,ZZ9.99.
           05  FILLER            PIC X(4) VALUE SPACES.
           05  WS-DET-PRINCIPAL PIC $ZZZ,ZZ9.99.
           05  FILLER            PIC X(4) VALUE SPACES.
           05  WS-DET-BALANCE   PIC $ZZZ,ZZZ,ZZ9.99.
           05  FILLER            PIC X(54) VALUE SPACES.

       01  WS-TOTAL-LINE.
           05  FILLER            PIC X(5) VALUE '  TOT'.
           05  FILLER            PIC X(3) VALUE SPACES.
           05  WS-TOT-PAID      PIC $ZZZ,ZZZ,ZZ9.99.
           05  FILLER            PIC X(1) VALUE SPACES.
           05  WS-TOT-INTEREST  PIC $ZZZ,ZZZ,ZZ9.99.
           05  FILLER            PIC X(1) VALUE SPACES.
           05  WS-TOT-PRINCIPAL PIC $ZZZ,ZZZ,ZZ9.99.
           05  FILLER            PIC X(69) VALUE SPACES.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
           OPEN OUTPUT AMORT-REPORT

      *    Example: $250,000 mortgage at 6.50% for 30 years
           MOVE 250000.00           TO WS-LOAN-AMOUNT
           MOVE 6.500000            TO WS-ANNUAL-RATE
           MOVE 360                 TO WS-TERM-MONTHS
           MOVE 'MORTGAGE'          TO WS-LOAN-TYPE

           PERFORM 1000-CALCULATE-PAYMENT
           PERFORM 2000-GENERATE-SCHEDULE
           PERFORM 3000-WRITE-TOTALS

           CLOSE AMORT-REPORT
           DISPLAY 'AMORTIZATION SCHEDULE GENERATED SUCCESSFULLY'
           STOP RUN
           .

       1000-CALCULATE-PAYMENT.
      *    Monthly rate = annual rate / 100 / 12
           DIVIDE WS-ANNUAL-RATE BY 100
               GIVING WS-MONTHLY-RATE
           DIVIDE WS-MONTHLY-RATE BY 12
               GIVING WS-MONTHLY-RATE ROUNDED

      *    Growth factor = 1 + monthly rate
           ADD 1 TO WS-MONTHLY-RATE
               GIVING WS-GROWTH-FACTOR

      *    Calculate (1 + r)^n iteratively
           MOVE 1.0 TO WS-POWER-RESULT
           PERFORM VARYING WS-PERIOD-CTR FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-TERM-MONTHS
               MULTIPLY WS-POWER-RESULT BY WS-GROWTH-FACTOR
                   GIVING WS-POWER-RESULT ROUNDED
           END-PERFORM

      *    Numerator = monthly rate * (1 + r)^n
           MULTIPLY WS-MONTHLY-RATE BY WS-POWER-RESULT
               GIVING WS-NUMERATOR

      *    Denominator = (1 + r)^n - 1
           SUBTRACT 1 FROM WS-POWER-RESULT
               GIVING WS-DENOMINATOR

      *    Monthly payment = principal * numerator / denominator
           MULTIPLY WS-LOAN-AMOUNT BY WS-NUMERATOR
               GIVING WS-MONTHLY-PAYMENT
           DIVIDE WS-MONTHLY-PAYMENT BY WS-DENOMINATOR
               GIVING WS-MONTHLY-PAYMENT ROUNDED

      *    Write header information
           WRITE REPORT-LINE FROM WS-REPORT-HEADER-1
           WRITE REPORT-LINE FROM WS-REPORT-HEADER-2
           WRITE REPORT-LINE FROM WS-REPORT-HEADER-1

           MOVE WS-LOAN-AMOUNT     TO WS-INFO-AMOUNT
           MOVE WS-LOAN-TYPE       TO WS-INFO-TYPE
           WRITE REPORT-LINE FROM WS-LOAN-INFO-1

           MOVE WS-ANNUAL-RATE     TO WS-INFO-RATE
           MOVE WS-TERM-MONTHS     TO WS-INFO-TERM
           WRITE REPORT-LINE FROM WS-LOAN-INFO-2

           MOVE WS-MONTHLY-PAYMENT TO WS-INFO-PAYMENT
           WRITE REPORT-LINE FROM WS-LOAN-INFO-3

           WRITE REPORT-LINE FROM WS-REPORT-HEADER-1
           WRITE REPORT-LINE FROM WS-COLUMN-HEADER
           .

       2000-GENERATE-SCHEDULE.
           MOVE WS-LOAN-AMOUNT TO WS-REMAINING-BAL
           MOVE ZERO TO WS-TOTAL-INTEREST
           MOVE ZERO TO WS-TOTAL-PRINCIPAL
           MOVE ZERO TO WS-TOTAL-PAID

           PERFORM VARYING WS-PERIOD-CTR FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-TERM-MONTHS

      *        Interest for this period = balance * monthly rate
               MULTIPLY WS-REMAINING-BAL BY WS-MONTHLY-RATE
                   GIVING WS-INTEREST-WORK ROUNDED
               MOVE WS-INTEREST-WORK TO WS-INTEREST-PORTION

      *        Principal for this period = payment - interest
               SUBTRACT WS-INTEREST-PORTION FROM WS-MONTHLY-PAYMENT
                   GIVING WS-PRINCIPAL-PORTION

      *        Handle final payment adjustment for rounding
               IF WS-PERIOD-CTR = WS-TERM-MONTHS
                   MOVE WS-REMAINING-BAL TO WS-PRINCIPAL-PORTION
                   ADD WS-INTEREST-PORTION TO WS-PRINCIPAL-PORTION
                       GIVING WS-MONTHLY-PAYMENT
               END-IF

      *        Update remaining balance
               SUBTRACT WS-PRINCIPAL-PORTION FROM WS-REMAINING-BAL

      *        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-PAID

      *        Format and write detail line
               MOVE WS-PERIOD-CTR        TO WS-DET-MONTH
               MOVE WS-MONTHLY-PAYMENT   TO WS-DET-PAYMENT
               MOVE WS-INTEREST-PORTION  TO WS-DET-INTEREST
               MOVE WS-PRINCIPAL-PORTION TO WS-DET-PRINCIPAL
               MOVE WS-REMAINING-BAL     TO WS-DET-BALANCE
               WRITE REPORT-LINE FROM WS-DETAIL-LINE

           END-PERFORM
           .

       3000-WRITE-TOTALS.
           WRITE REPORT-LINE FROM WS-REPORT-HEADER-1
           MOVE WS-TOTAL-PAID      TO WS-TOT-PAID
           MOVE WS-TOTAL-INTEREST  TO WS-TOT-INTEREST
           MOVE WS-TOTAL-PRINCIPAL TO WS-TOT-PRINCIPAL
           WRITE REPORT-LINE FROM WS-TOTAL-LINE

           DISPLAY 'TOTAL PAID:      ' WS-TOTAL-PAID
           DISPLAY 'TOTAL INTEREST:  ' WS-TOTAL-INTEREST
           DISPLAY 'TOTAL PRINCIPAL: ' WS-TOTAL-PRINCIPAL
           .

The JCL to execute this amortization program on an IBM mainframe would be:

//AMORTJOB JOB (ACCT),'AMORT SCHEDULE',CLASS=A,
//         MSGCLASS=X,NOTIFY=&SYSUID
//*
//STEP01   EXEC PGM=AMORTSCH
//STEPLIB  DD DSN=PROD.FINANCE.LOADLIB,DISP=SHR
//AMORTOUT DD DSN=PROD.FINANCE.AMORT.REPORT,
//         DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(5,2),RLSE),
//         DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//SYSPRINT DD SYSOUT=*

Auto Loan vs. Mortgage Differences

While both use the same fundamental amortization formula, auto loans and mortgages differ in several practical aspects that affect COBOL programs:

  • Term length: Mortgages typically span 180-360 months; auto loans span 36-84 months.
  • Prepayment handling: Mortgage systems must track prepayment penalties; auto loans rarely have them.
  • Escrow: Mortgage payments often include escrow for taxes and insurance, requiring additional calculation fields.
  • Day count: Some auto loans use Actual/365 day count while most mortgages use 30/360.

33.5 Currency Handling

The ROUNDED Phrase

COBOL's ROUNDED phrase is the primary mechanism for controlling how arithmetic results are truncated to fit the receiving field. Without ROUNDED, COBOL truncates (chops off extra digits). With ROUNDED, COBOL applies rounding rules.

As discussed in Chapter 6 (Arithmetic Operations), the basic ROUNDED phrase performs standard arithmetic rounding (round half up):

      *    Without ROUNDED - truncation
           DIVIDE 10.00 BY 3 GIVING WS-RESULT
      *    WS-RESULT = 3.33 (truncated from 3.333...)

      *    With ROUNDED - standard rounding
           DIVIDE 10.00 BY 3 GIVING WS-RESULT ROUNDED
      *    WS-RESULT = 3.33 (rounded from 3.333...)

      *    Key difference example:
           DIVIDE 2.00 BY 3 GIVING WS-RESULT
      *    WS-RESULT = 0.66 (truncated from 0.666...)

           DIVIDE 2.00 BY 3 GIVING WS-RESULT ROUNDED
      *    WS-RESULT = 0.67 (rounded from 0.666...)

COBOL 2002 Rounding Modes

The COBOL 2002 and later standards introduced explicit rounding mode specification. Financial systems often require specific rounding modes for regulatory compliance:

      *    COBOL 2002+ rounding modes
           DIVIDE WS-A BY WS-B GIVING WS-C
               ROUNDED MODE IS TRUNCATION
      *    Always truncate toward zero

           DIVIDE WS-A BY WS-B GIVING WS-C
               ROUNDED MODE IS AWAY-FROM-ZERO
      *    Always round away from zero

           DIVIDE WS-A BY WS-B GIVING WS-C
               ROUNDED MODE IS NEAREST-EVEN
      *    Banker's rounding - round half to nearest even digit

           DIVIDE WS-A BY WS-B GIVING WS-C
               ROUNDED MODE IS NEAREST-AWAY-FROM-ZERO
      *    Traditional rounding - round half away from zero

Banker's Rounding Implementation

Banker's rounding (round half to even) eliminates the slight upward bias of standard rounding. When the value is exactly halfway between two representable values, it rounds to the nearest even digit. This is critical for high-volume financial processing where systematic bias would accumulate.

For compilers that do not support the COBOL 2002 rounding modes, you can implement banker's rounding manually:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BANKERS-ROUND.
      *================================================================*
      * PROGRAM: BANKERS-ROUND                                         *
      * PURPOSE: Implement banker's rounding (round half to even) for  *
      *          compilers without COBOL 2002 rounding modes           *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-INPUT-VALUE        PIC S9(13)V9(4) COMP-3.
       01  WS-OUTPUT-VALUE       PIC S9(13)V99   COMP-3.
       01  WS-WORK-FIELDS.
           05  WS-TRUNCATED      PIC S9(13)V99   COMP-3.
           05  WS-REMAINDER      PIC S9(3)V9(4)  COMP-3.
           05  WS-HALF-CHECK     PIC S9(3)V9(4)  COMP-3.
           05  WS-LAST-DIGIT     PIC S9(1)        COMP-3.
           05  WS-ROUNDED-UP     PIC S9(13)V99   COMP-3.
           05  WS-SIGN-SAVE      PIC S9(1)        COMP-3.
           05  WS-ABS-VALUE      PIC S9(13)V9(4) COMP-3.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Test cases for banker's rounding
           MOVE  2.505  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.505   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.50 (5 rounds to even, 0 is even)

           MOVE  2.515  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.515   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.52 (5 rounds to even, 2 is even)

           MOVE  2.525  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.525   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.52 (5 rounds to even, 2 is even)

           MOVE  2.535  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.535   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.54 (5 rounds to even, 4 is even)

           MOVE  2.545  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.545   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.54 (5 rounds to even, 4 is even)

           MOVE  2.546  TO WS-INPUT-VALUE
           PERFORM 1000-BANKERS-ROUND
           DISPLAY 'Input: 2.546   -> ' WS-OUTPUT-VALUE
      *    Expected: 2.55 (above halfway, round up)

           STOP RUN
           .

       1000-BANKERS-ROUND.
      *    Handle sign
           IF WS-INPUT-VALUE < 0
               MULTIPLY WS-INPUT-VALUE BY -1
                   GIVING WS-ABS-VALUE
               MOVE -1 TO WS-SIGN-SAVE
           ELSE
               MOVE WS-INPUT-VALUE TO WS-ABS-VALUE
               MOVE 1 TO WS-SIGN-SAVE
           END-IF

      *    Truncate to target precision (2 decimal places)
           MOVE WS-ABS-VALUE TO WS-TRUNCATED

      *    Get the remainder beyond 2 decimal places
           SUBTRACT WS-TRUNCATED FROM WS-ABS-VALUE
               GIVING WS-REMAINDER

      *    Check if remainder is exactly 0.005 (halfway)
           MOVE 0.005 TO WS-HALF-CHECK

           EVALUATE TRUE
      *        Below halfway - truncate (already done)
               WHEN WS-REMAINDER < WS-HALF-CHECK
                   MOVE WS-TRUNCATED TO WS-OUTPUT-VALUE

      *        Above halfway - round up
               WHEN WS-REMAINDER > WS-HALF-CHECK
                   ADD 0.01 TO WS-TRUNCATED
                       GIVING WS-OUTPUT-VALUE

      *        Exactly halfway - round to even
               WHEN OTHER
                   DIVIDE WS-TRUNCATED BY 0.02
                       GIVING WS-LAST-DIGIT
                       REMAINDER WS-REMAINDER
                   IF WS-REMAINDER = ZERO
      *                Last cent digit is even - truncate
                       MOVE WS-TRUNCATED TO WS-OUTPUT-VALUE
                   ELSE
      *                Last cent digit is odd - round up
                       ADD 0.01 TO WS-TRUNCATED
                           GIVING WS-OUTPUT-VALUE
                   END-IF
           END-EVALUATE

      *    Restore sign
           IF WS-SIGN-SAVE = -1
               MULTIPLY WS-OUTPUT-VALUE BY -1
                   GIVING WS-OUTPUT-VALUE
           END-IF
           .

Multi-Currency Handling

International financial systems must handle multiple currencies with different decimal precision (USD uses 2 decimals, JPY uses 0, BHD uses 3) and exchange rate conversions:

       01  WS-CURRENCY-TABLE.
           05  WS-CURRENCY-ENTRY OCCURS 10 TIMES.
               10  WS-CURR-CODE      PIC X(3).
               10  WS-CURR-DECIMALS  PIC 9(1).
               10  WS-CURR-RATE      PIC S9(5)V9(8) COMP-3.
               10  WS-CURR-NAME      PIC X(20).

       01  WS-CONVERSION-FIELDS.
           05  WS-SOURCE-AMOUNT      PIC S9(15)V9(4) COMP-3.
           05  WS-TARGET-AMOUNT      PIC S9(15)V9(4) COMP-3.
           05  WS-BASE-AMOUNT        PIC S9(15)V9(8) COMP-3.
           05  WS-SOURCE-IDX         PIC S9(3) COMP.
           05  WS-TARGET-IDX         PIC S9(3) COMP.

      *    Convert between any two currencies via USD base
       1000-CONVERT-CURRENCY.
      *    Step 1: Convert source to USD base
           DIVIDE WS-SOURCE-AMOUNT
               BY WS-CURR-RATE(WS-SOURCE-IDX)
               GIVING WS-BASE-AMOUNT ROUNDED

      *    Step 2: Convert USD base to target currency
           MULTIPLY WS-BASE-AMOUNT
               BY WS-CURR-RATE(WS-TARGET-IDX)
               GIVING WS-TARGET-AMOUNT ROUNDED
           .

33.6 Tax Calculations

Tax calculations are among the most complex financial computations because tax law creates intricate rules with brackets, phase-outs, credits, and special cases. COBOL's structured approach to data and its precise arithmetic make it well-suited for these calculations.

Progressive Tax Bracket Calculation

The US federal income tax system uses progressive brackets where different portions of income are taxed at different rates. The following program implements a complete progressive tax calculation:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TAX-CALC.
      *================================================================*
      * PROGRAM: TAX-CALC                                              *
      * PURPOSE: Calculate progressive federal income tax using         *
      *          bracket-based rates with table-driven approach        *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-TAX-BRACKETS.
      *    2024 Single Filer Tax Brackets
      *    Format: Lower-Limit(11), Upper-Limit(11), Rate(7)
           05  FILLER PIC X(29) VALUE '0000000000001150000000010.000'.
           05  FILLER PIC X(29) VALUE '0000001150100468750000012.000'.
           05  FILLER PIC X(29) VALUE '0000004687501005250000022.000'.
           05  FILLER PIC X(29) VALUE '0000010052501911750000024.000'.
           05  FILLER PIC X(29) VALUE '0000019117502437500000032.000'.
           05  FILLER PIC X(29) VALUE '0000024375005781250000035.000'.
           05  FILLER PIC X(29) VALUE '0000057812509999999999937.000'.

       01  WS-BRACKET-TABLE REDEFINES WS-TAX-BRACKETS.
           05  WS-BRACKET OCCURS 7 TIMES.
               10  WS-BRACKET-LOWER  PIC 9(11).
               10  WS-BRACKET-UPPER  PIC 9(11).
               10  WS-BRACKET-RATE   PIC 99V999.

       01  WS-TAX-INPUT.
           05  WS-GROSS-INCOME       PIC S9(11)V99 COMP-3.
           05  WS-STANDARD-DEDUCTION PIC S9(11)V99 COMP-3.
           05  WS-TAXABLE-INCOME     PIC S9(11)V99 COMP-3.

       01  WS-TAX-CALC.
           05  WS-BRACKET-IDX        PIC S9(3) COMP.
           05  WS-NUM-BRACKETS       PIC S9(3) COMP VALUE 7.
           05  WS-BRACKET-AMOUNT     PIC S9(11)V99 COMP-3.
           05  WS-BRACKET-TAX        PIC S9(11)V9(4) COMP-3.
           05  WS-TOTAL-TAX          PIC S9(11)V99 COMP-3.
           05  WS-EFFECTIVE-RATE     PIC S9(3)V9(4) COMP-3.
           05  WS-REMAINING-INCOME   PIC S9(11)V99 COMP-3.
           05  WS-BRACKET-WIDTH      PIC S9(11)V99 COMP-3.

       01  WS-DISPLAY-FIELDS.
           05  WS-DISP-INCOME       PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-DEDUCTION    PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-TAXABLE      PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-TAX          PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-RATE         PIC Z9.9999.
           05  WS-DISP-BRKT-AMT     PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-BRKT-TAX     PIC $ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-BRKT-RATE    PIC Z9.999.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: $95,000 gross income, single filer
           MOVE 95000.00            TO WS-GROSS-INCOME
           MOVE 14600.00            TO WS-STANDARD-DEDUCTION

      *    Calculate taxable income
           SUBTRACT WS-STANDARD-DEDUCTION FROM WS-GROSS-INCOME
               GIVING WS-TAXABLE-INCOME
           IF WS-TAXABLE-INCOME < 0
               MOVE ZERO TO WS-TAXABLE-INCOME
           END-IF

           PERFORM 1000-CALCULATE-TAX

           MOVE WS-GROSS-INCOME     TO WS-DISP-INCOME
           MOVE WS-STANDARD-DEDUCTION TO WS-DISP-DEDUCTION
           MOVE WS-TAXABLE-INCOME   TO WS-DISP-TAXABLE
           MOVE WS-TOTAL-TAX        TO WS-DISP-TAX

      *    Effective rate = total tax / gross income * 100
           IF WS-GROSS-INCOME > ZERO
               DIVIDE WS-TOTAL-TAX BY WS-GROSS-INCOME
                   GIVING WS-EFFECTIVE-RATE
               MULTIPLY WS-EFFECTIVE-RATE BY 100
                   GIVING WS-EFFECTIVE-RATE
           END-IF
           MOVE WS-EFFECTIVE-RATE    TO WS-DISP-RATE

           DISPLAY '=================================='
           DISPLAY 'FEDERAL INCOME TAX CALCULATION'
           DISPLAY '=================================='
           DISPLAY 'GROSS INCOME:       ' WS-DISP-INCOME
           DISPLAY 'STANDARD DEDUCTION: ' WS-DISP-DEDUCTION
           DISPLAY 'TAXABLE INCOME:     ' WS-DISP-TAXABLE
           DISPLAY 'TOTAL TAX:          ' WS-DISP-TAX
           DISPLAY 'EFFECTIVE RATE:     ' WS-DISP-RATE '%'

           STOP RUN
           .

       1000-CALCULATE-TAX.
           MOVE ZERO TO WS-TOTAL-TAX
           MOVE WS-TAXABLE-INCOME TO WS-REMAINING-INCOME

           PERFORM VARYING WS-BRACKET-IDX FROM 1 BY 1
               UNTIL WS-BRACKET-IDX > WS-NUM-BRACKETS
               OR WS-REMAINING-INCOME <= ZERO

      *        Calculate bracket width
               SUBTRACT WS-BRACKET-LOWER(WS-BRACKET-IDX)
                   FROM WS-BRACKET-UPPER(WS-BRACKET-IDX)
                   GIVING WS-BRACKET-WIDTH

      *        Amount taxed in this bracket is the lesser of
      *        remaining income or bracket width
               IF WS-REMAINING-INCOME <= WS-BRACKET-WIDTH
                   MOVE WS-REMAINING-INCOME TO WS-BRACKET-AMOUNT
               ELSE
                   MOVE WS-BRACKET-WIDTH TO WS-BRACKET-AMOUNT
               END-IF

      *        Calculate tax for this bracket
               MULTIPLY WS-BRACKET-AMOUNT
                   BY WS-BRACKET-RATE(WS-BRACKET-IDX)
                   GIVING WS-BRACKET-TAX
               DIVIDE WS-BRACKET-TAX BY 100
                   GIVING WS-BRACKET-TAX ROUNDED

      *        Display bracket detail
               MOVE WS-BRACKET-AMOUNT   TO WS-DISP-BRKT-AMT
               MOVE WS-BRACKET-TAX      TO WS-DISP-BRKT-TAX
               MOVE WS-BRACKET-RATE(WS-BRACKET-IDX)
                                         TO WS-DISP-BRKT-RATE
               DISPLAY '  BRACKET ' WS-BRACKET-IDX ': '
                   WS-DISP-BRKT-AMT ' @ '
                   WS-DISP-BRKT-RATE '% = '
                   WS-DISP-BRKT-TAX

      *        Accumulate and reduce remaining
               ADD WS-BRACKET-TAX TO WS-TOTAL-TAX
               SUBTRACT WS-BRACKET-AMOUNT FROM WS-REMAINING-INCOME

           END-PERFORM
           .

Payroll Withholding Calculation

Payroll systems must calculate federal and state income tax withholding, Social Security tax (FICA), and Medicare tax for each pay period:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PAYROLL-TAX.
      *================================================================*
      * PROGRAM: PAYROLL-TAX                                           *
      * PURPOSE: Calculate payroll tax withholdings including federal,  *
      *          FICA, and Medicare taxes                               *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-EMPLOYEE-DATA.
           05  WS-EMP-ID            PIC X(10).
           05  WS-GROSS-PAY         PIC S9(9)V99 COMP-3.
           05  WS-PAY-FREQUENCY     PIC X(1).
               88  PAY-WEEKLY       VALUE 'W'.
               88  PAY-BIWEEKLY     VALUE 'B'.
               88  PAY-SEMIMONTHLY  VALUE 'S'.
               88  PAY-MONTHLY      VALUE 'M'.
           05  WS-FILING-STATUS     PIC X(1).
               88  STATUS-SINGLE    VALUE 'S'.
               88  STATUS-MARRIED   VALUE 'M'.
           05  WS-YTD-GROSS         PIC S9(11)V99 COMP-3.
           05  WS-YTD-FICA-WAGES   PIC S9(11)V99 COMP-3.

       01  WS-TAX-CONSTANTS.
           05  WS-FICA-RATE         PIC S9(3)V9(4) COMP-3
                                    VALUE 6.2000.
           05  WS-FICA-WAGE-LIMIT   PIC S9(11)V99 COMP-3
                                    VALUE 168600.00.
           05  WS-MEDICARE-RATE     PIC S9(3)V9(4) COMP-3
                                    VALUE 1.4500.
           05  WS-ADDL-MEDICARE-RATE PIC S9(3)V9(4) COMP-3
                                    VALUE 0.9000.
           05  WS-ADDL-MEDICARE-THRESH PIC S9(11)V99 COMP-3
                                    VALUE 200000.00.

       01  WS-CALC-FIELDS.
           05  WS-ANNUALIZED-PAY    PIC S9(11)V99 COMP-3.
           05  WS-FICA-TAXABLE      PIC S9(11)V99 COMP-3.
           05  WS-FICA-TAX          PIC S9(9)V99 COMP-3.
           05  WS-MEDICARE-TAX      PIC S9(9)V99 COMP-3.
           05  WS-ADDL-MEDICARE-TAX PIC S9(9)V99 COMP-3.
           05  WS-FEDERAL-TAX       PIC S9(9)V99 COMP-3.
           05  WS-TOTAL-TAX         PIC S9(9)V99 COMP-3.
           05  WS-NET-PAY           PIC S9(9)V99 COMP-3.
           05  WS-NEW-YTD-FICA      PIC S9(11)V99 COMP-3.
           05  WS-EXCESS-FICA       PIC S9(11)V99 COMP-3.
           05  WS-WORK-AMT          PIC S9(11)V9(4) COMP-3.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: Biweekly pay, $3,846.15 gross
           MOVE 'E001234567'         TO WS-EMP-ID
           MOVE 3846.15              TO WS-GROSS-PAY
           SET  PAY-BIWEEKLY         TO TRUE
           SET  STATUS-SINGLE        TO TRUE
           MOVE 88461.45             TO WS-YTD-GROSS
           MOVE 88461.45             TO WS-YTD-FICA-WAGES

           PERFORM 1000-CALC-FICA
           PERFORM 2000-CALC-MEDICARE
           PERFORM 3000-CALC-FEDERAL

           ADD WS-FICA-TAX WS-MEDICARE-TAX WS-ADDL-MEDICARE-TAX
               WS-FEDERAL-TAX
               GIVING WS-TOTAL-TAX
           SUBTRACT WS-TOTAL-TAX FROM WS-GROSS-PAY
               GIVING WS-NET-PAY

           DISPLAY 'PAYROLL TAX SUMMARY FOR: ' WS-EMP-ID
           DISPLAY 'GROSS PAY:       ' WS-GROSS-PAY
           DISPLAY 'FICA TAX:        ' WS-FICA-TAX
           DISPLAY 'MEDICARE TAX:    ' WS-MEDICARE-TAX
           DISPLAY 'ADDL MEDICARE:   ' WS-ADDL-MEDICARE-TAX
           DISPLAY 'FEDERAL TAX:     ' WS-FEDERAL-TAX
           DISPLAY 'TOTAL TAX:       ' WS-TOTAL-TAX
           DISPLAY 'NET PAY:         ' WS-NET-PAY

           STOP RUN
           .

       1000-CALC-FICA.
      *    Check if employee has reached FICA wage limit
           ADD WS-YTD-FICA-WAGES WS-GROSS-PAY
               GIVING WS-NEW-YTD-FICA

           IF WS-YTD-FICA-WAGES >= WS-FICA-WAGE-LIMIT
      *        Already exceeded limit - no FICA tax
               MOVE ZERO TO WS-FICA-TAX
           ELSE IF WS-NEW-YTD-FICA > WS-FICA-WAGE-LIMIT
      *        This paycheck crosses the limit
               SUBTRACT WS-YTD-FICA-WAGES FROM WS-FICA-WAGE-LIMIT
                   GIVING WS-FICA-TAXABLE
               MULTIPLY WS-FICA-TAXABLE BY WS-FICA-RATE
                   GIVING WS-WORK-AMT
               DIVIDE WS-WORK-AMT BY 100
                   GIVING WS-FICA-TAX ROUNDED
           ELSE
      *        Full paycheck is taxable
               MULTIPLY WS-GROSS-PAY BY WS-FICA-RATE
                   GIVING WS-WORK-AMT
               DIVIDE WS-WORK-AMT BY 100
                   GIVING WS-FICA-TAX ROUNDED
           END-IF
           .

       2000-CALC-MEDICARE.
      *    Standard Medicare tax on all wages
           MULTIPLY WS-GROSS-PAY BY WS-MEDICARE-RATE
               GIVING WS-WORK-AMT
           DIVIDE WS-WORK-AMT BY 100
               GIVING WS-MEDICARE-TAX ROUNDED

      *    Additional Medicare tax for high earners
           ADD WS-YTD-GROSS WS-GROSS-PAY
               GIVING WS-WORK-AMT
           IF WS-WORK-AMT > WS-ADDL-MEDICARE-THRESH
               IF WS-YTD-GROSS >= WS-ADDL-MEDICARE-THRESH
      *            All of this paycheck is subject
                   MULTIPLY WS-GROSS-PAY BY WS-ADDL-MEDICARE-RATE
                       GIVING WS-WORK-AMT
                   DIVIDE WS-WORK-AMT BY 100
                       GIVING WS-ADDL-MEDICARE-TAX ROUNDED
               ELSE
      *            Only the amount over threshold is subject
                   SUBTRACT WS-ADDL-MEDICARE-THRESH
                       FROM WS-WORK-AMT
                       GIVING WS-WORK-AMT
                   MULTIPLY WS-WORK-AMT BY WS-ADDL-MEDICARE-RATE
                       GIVING WS-WORK-AMT
                   DIVIDE WS-WORK-AMT BY 100
                       GIVING WS-ADDL-MEDICARE-TAX ROUNDED
               END-IF
           ELSE
               MOVE ZERO TO WS-ADDL-MEDICARE-TAX
           END-IF
           .

       3000-CALC-FEDERAL.
      *    Simplified federal withholding calculation
      *    (In production, this would use IRS Publication 15-T tables)
           MOVE 500.00 TO WS-FEDERAL-TAX
           .

Sales Tax Calculation

Sales tax calculations handle varying tax rates by jurisdiction, tax-exempt items, and combined state/county/city rates:

       01  WS-SALES-TAX-TABLE.
           05  WS-JURISDICTION OCCURS 50 TIMES.
               10  WS-JURIS-CODE     PIC X(5).
               10  WS-STATE-RATE     PIC S9(3)V9(4) COMP-3.
               10  WS-COUNTY-RATE    PIC S9(3)V9(4) COMP-3.
               10  WS-CITY-RATE      PIC S9(3)V9(4) COMP-3.
               10  WS-SPECIAL-RATE   PIC S9(3)V9(4) COMP-3.

       01  WS-SALES-TAX-CALC.
           05  WS-ITEM-AMOUNT       PIC S9(9)V99 COMP-3.
           05  WS-COMBINED-RATE     PIC S9(3)V9(4) COMP-3.
           05  WS-TAX-AMOUNT        PIC S9(9)V99 COMP-3.
           05  WS-TOTAL-AMOUNT      PIC S9(9)V99 COMP-3.

       1000-CALC-SALES-TAX.
      *    Combine all applicable rates
           ADD WS-STATE-RATE(WS-JURIS-IDX)
               WS-COUNTY-RATE(WS-JURIS-IDX)
               WS-CITY-RATE(WS-JURIS-IDX)
               WS-SPECIAL-RATE(WS-JURIS-IDX)
               GIVING WS-COMBINED-RATE

      *    Calculate tax amount
           MULTIPLY WS-ITEM-AMOUNT BY WS-COMBINED-RATE
               GIVING WS-TAX-AMOUNT
           DIVIDE WS-TAX-AMOUNT BY 100
               GIVING WS-TAX-AMOUNT ROUNDED

      *    Total with tax
           ADD WS-ITEM-AMOUNT WS-TAX-AMOUNT
               GIVING WS-TOTAL-AMOUNT
           .

33.7 Financial Date Calculations

Financial systems use specific conventions for counting days between dates. These conventions, called day count conventions, directly affect interest calculations. Different financial instruments use different conventions, and using the wrong one produces incorrect interest amounts.

Day Count Conventions

The four primary day count conventions are:

  1. 30/360 (Bond Basis): Assumes every month has 30 days and the year has 360 days. Used for most US corporate and municipal bonds.

  2. Actual/360 (Money Market): Uses the actual number of days between dates but divides by 360. Used for US money market instruments and commercial loans.

  3. Actual/365 (Fixed): Uses actual days between dates divided by 365. Used for many government bonds and some commercial loans.

  4. Actual/Actual (ISDA): Uses actual days in the actual year (365 or 366 for leap years). Used for US Treasury bonds.

Day Count Convention Program

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DAY-COUNT.
      *================================================================*
      * PROGRAM: DAY-COUNT                                             *
      * PURPOSE: Implement financial day count conventions for          *
      *          interest accrual calculations                         *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-DATE-FIELDS.
           05  WS-START-DATE.
               10  WS-START-YEAR    PIC 9(4).
               10  WS-START-MONTH   PIC 9(2).
               10  WS-START-DAY     PIC 9(2).
           05  WS-END-DATE.
               10  WS-END-YEAR      PIC 9(4).
               10  WS-END-MONTH     PIC 9(2).
               10  WS-END-DAY       PIC 9(2).

       01  WS-CALC-FIELDS.
           05  WS-ACTUAL-DAYS       PIC S9(7) COMP-3.
           05  WS-30-360-DAYS       PIC S9(7) COMP-3.
           05  WS-YEAR-BASIS        PIC S9(3) COMP-3.
           05  WS-DAY-FRACTION      PIC S9(3)V9(10) COMP-3.
           05  WS-D1                PIC S9(3) COMP-3.
           05  WS-D2                PIC S9(3) COMP-3.
           05  WS-M1                PIC S9(3) COMP-3.
           05  WS-M2                PIC S9(3) COMP-3.
           05  WS-Y1                PIC S9(5) COMP-3.
           05  WS-Y2                PIC S9(5) COMP-3.

       01  WS-INTEGER-DATE-1        PIC S9(7) COMP-3.
       01  WS-INTEGER-DATE-2        PIC S9(7) COMP-3.
       01  WS-LEAP-FLAG             PIC X VALUE 'N'.
           88  IS-LEAP-YEAR         VALUE 'Y'.

       01  WS-DAYS-PER-MONTH.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 28.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 30.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 30.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 30.
           05  FILLER PIC 9(2) VALUE 31.
           05  FILLER PIC 9(2) VALUE 30.
           05  FILLER PIC 9(2) VALUE 31.

       01  WS-DAYS-TABLE REDEFINES WS-DAYS-PER-MONTH.
           05  WS-MONTH-DAYS PIC 9(2) OCCURS 12 TIMES.

       01  WS-WORK-FIELDS.
           05  WS-MONTH-IDX         PIC S9(3) COMP-3.
           05  WS-YEAR-IDX          PIC S9(5) COMP-3.
           05  WS-WORK-DAYS         PIC S9(7) COMP-3.
           05  WS-REMAIN-4          PIC S9(5) COMP-3.
           05  WS-REMAIN-100        PIC S9(5) COMP-3.
           05  WS-REMAIN-400        PIC S9(5) COMP-3.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    Example: Calculate day fractions from Jan 15, 2024
      *    to Jul 15, 2024
           MOVE '20240115' TO WS-START-DATE
           MOVE '20240715' TO WS-END-DATE

           PERFORM 1000-CALC-30-360
           DISPLAY '30/360 DAYS:     ' WS-30-360-DAYS
           MOVE 360 TO WS-YEAR-BASIS
           DIVIDE WS-30-360-DAYS BY WS-YEAR-BASIS
               GIVING WS-DAY-FRACTION
           DISPLAY '30/360 FRACTION: ' WS-DAY-FRACTION

           PERFORM 2000-CALC-ACTUAL-DAYS
           DISPLAY 'ACTUAL DAYS:     ' WS-ACTUAL-DAYS

           MOVE 360 TO WS-YEAR-BASIS
           DIVIDE WS-ACTUAL-DAYS BY WS-YEAR-BASIS
               GIVING WS-DAY-FRACTION
           DISPLAY 'ACT/360 FRACTION:' WS-DAY-FRACTION

           MOVE 365 TO WS-YEAR-BASIS
           DIVIDE WS-ACTUAL-DAYS BY WS-YEAR-BASIS
               GIVING WS-DAY-FRACTION
           DISPLAY 'ACT/365 FRACTION:' WS-DAY-FRACTION

           STOP RUN
           .

       1000-CALC-30-360.
      *    US 30/360 (NASD method)
           MOVE WS-START-DAY   TO WS-D1
           MOVE WS-END-DAY     TO WS-D2
           MOVE WS-START-MONTH TO WS-M1
           MOVE WS-END-MONTH   TO WS-M2
           MOVE WS-START-YEAR  TO WS-Y1
           MOVE WS-END-YEAR    TO WS-Y2

      *    If D1 is 31, change to 30
           IF WS-D1 = 31
               MOVE 30 TO WS-D1
           END-IF

      *    If D2 is 31 and D1 is 30 or 31, change D2 to 30
           IF WS-D2 = 31 AND WS-D1 = 30
               MOVE 30 TO WS-D2
           END-IF

      *    30/360 days = (Y2-Y1)*360 + (M2-M1)*30 + (D2-D1)
           SUBTRACT WS-Y1 FROM WS-Y2 GIVING WS-30-360-DAYS
           MULTIPLY WS-30-360-DAYS BY 360
           SUBTRACT WS-M1 FROM WS-M2 GIVING WS-WORK-DAYS
           MULTIPLY WS-WORK-DAYS BY 30
           ADD WS-WORK-DAYS TO WS-30-360-DAYS
           SUBTRACT WS-D1 FROM WS-D2 GIVING WS-WORK-DAYS
           ADD WS-WORK-DAYS TO WS-30-360-DAYS
           .

       2000-CALC-ACTUAL-DAYS.
      *    Convert both dates to integer day numbers and subtract
           PERFORM 2100-DATE-TO-INTEGER-1
           PERFORM 2200-DATE-TO-INTEGER-2
           SUBTRACT WS-INTEGER-DATE-1 FROM WS-INTEGER-DATE-2
               GIVING WS-ACTUAL-DAYS
           .

       2100-DATE-TO-INTEGER-1.
      *    Convert start date to integer (days from reference)
           MOVE ZERO TO WS-INTEGER-DATE-1
           COMPUTE WS-INTEGER-DATE-1 =
               FUNCTION INTEGER-OF-DATE(WS-START-DATE)
           .

       2200-DATE-TO-INTEGER-2.
      *    Convert end date to integer (days from reference)
           MOVE ZERO TO WS-INTEGER-DATE-2
           COMPUTE WS-INTEGER-DATE-2 =
               FUNCTION INTEGER-OF-DATE(WS-END-DATE)
           .

       3000-CHECK-LEAP-YEAR.
      *    Leap year check for Actual/Actual calculations
           MOVE 'N' TO WS-LEAP-FLAG
           DIVIDE WS-YEAR-IDX BY 4
               GIVING WS-WORK-DAYS REMAINDER WS-REMAIN-4
           IF WS-REMAIN-4 = ZERO
               DIVIDE WS-YEAR-IDX BY 100
                   GIVING WS-WORK-DAYS REMAINDER WS-REMAIN-100
               IF WS-REMAIN-100 NOT = ZERO
                   SET IS-LEAP-YEAR TO TRUE
               ELSE
                   DIVIDE WS-YEAR-IDX BY 400
                       GIVING WS-WORK-DAYS REMAINDER WS-REMAIN-400
                   IF WS-REMAIN-400 = ZERO
                       SET IS-LEAP-YEAR TO TRUE
                   END-IF
               END-IF
           END-IF
           .

33.8 Present Value, Future Value, NPV, and IRR

Time value of money calculations are fundamental to financial analysis, investment evaluation, and accounting. COBOL programs implement these calculations for pension fund valuations, lease accounting, investment portfolio analysis, and capital budgeting.

Present Value and Future Value

Present value (PV) answers the question: what is a future amount worth today? Future value (FV) answers the reverse: what will today's amount be worth in the future?

PV = FV / (1 + r)^n FV = PV x (1 + r)^n

Net Present Value (NPV)

NPV is the sum of present values of a series of cash flows:

NPV = Sum of [CF(t) / (1 + r)^t] for t = 0 to n

Complete Time Value of Money Program

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TIME-VALUE-MONEY.
      *================================================================*
      * PROGRAM: TIME-VALUE-MONEY                                      *
      * PURPOSE: Calculate PV, FV, NPV, and IRR for financial          *
      *          analysis and investment evaluation                    *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-MAX-PERIODS          PIC S9(3) COMP VALUE 60.

       01  WS-CASH-FLOW-TABLE.
           05  WS-CASH-FLOW        PIC S9(13)V99 COMP-3
                                   OCCURS 60 TIMES.

       01  WS-TVM-FIELDS.
           05  WS-PRESENT-VALUE    PIC S9(15)V99 COMP-3.
           05  WS-FUTURE-VALUE     PIC S9(15)V99 COMP-3.
           05  WS-DISCOUNT-RATE    PIC S9(3)V9(8) COMP-3.
           05  WS-NUM-PERIODS      PIC S9(3) COMP-3.
           05  WS-NPV-RESULT       PIC S9(15)V99 COMP-3.
           05  WS-IRR-RESULT       PIC S9(3)V9(6) COMP-3.

       01  WS-CALC-WORK.
           05  WS-PERIOD-CTR       PIC S9(3) COMP-3.
           05  WS-DISCOUNT-FACTOR  PIC S9(5)V9(10) COMP-3.
           05  WS-GROWTH-FACTOR    PIC S9(5)V9(10) COMP-3.
           05  WS-ACCUMULATOR      PIC S9(15)V9(6) COMP-3.
           05  WS-PV-CASHFLOW      PIC S9(15)V9(6) COMP-3.
           05  WS-POWER-CTR        PIC S9(3) COMP-3.
           05  WS-POWER-RESULT     PIC S9(7)V9(10) COMP-3.

       01  WS-IRR-WORK.
           05  WS-IRR-LOW          PIC S9(3)V9(8) COMP-3.
           05  WS-IRR-HIGH         PIC S9(3)V9(8) COMP-3.
           05  WS-IRR-MID          PIC S9(3)V9(8) COMP-3.
           05  WS-NPV-LOW          PIC S9(15)V99 COMP-3.
           05  WS-NPV-HIGH         PIC S9(15)V99 COMP-3.
           05  WS-NPV-MID          PIC S9(15)V99 COMP-3.
           05  WS-IRR-TOLERANCE    PIC S9(3)V9(8) COMP-3
                                   VALUE 0.00000100.
           05  WS-IRR-MAX-ITER     PIC S9(5) COMP VALUE 1000.
           05  WS-IRR-ITERATION    PIC S9(5) COMP.

       01  WS-DISPLAY-FIELDS.
           05  WS-DISP-PV          PIC $ZZZ,ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-FV          PIC $ZZZ,ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-NPV         PIC -ZZZ,ZZZ,ZZZ,ZZ9.99.
           05  WS-DISP-IRR         PIC Z9.999999.
           05  WS-DISP-CF          PIC -ZZZ,ZZZ,ZZ9.99.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
      *    ============================================
      *    Example 1: Present Value Calculation
      *    What is $100,000 in 5 years worth today
      *    at a 7% discount rate?
      *    ============================================
           MOVE 100000.00 TO WS-FUTURE-VALUE
           MOVE 0.07       TO WS-DISCOUNT-RATE
           MOVE 5          TO WS-NUM-PERIODS

           PERFORM 1000-CALC-PRESENT-VALUE

           MOVE WS-PRESENT-VALUE TO WS-DISP-PV
           MOVE WS-FUTURE-VALUE  TO WS-DISP-FV
           DISPLAY '==============================='
           DISPLAY 'PRESENT VALUE CALCULATION'
           DISPLAY 'FUTURE VALUE:  ' WS-DISP-FV
           DISPLAY 'DISCOUNT RATE: 7.00%'
           DISPLAY 'PERIODS:       5'
           DISPLAY 'PRESENT VALUE: ' WS-DISP-PV

      *    ============================================
      *    Example 2: NPV of an Investment
      *    Initial investment: -$500,000
      *    Year 1 cash flow:   $125,000
      *    Year 2 cash flow:   $150,000
      *    Year 3 cash flow:   $175,000
      *    Year 4 cash flow:   $150,000
      *    Year 5 cash flow:   $100,000
      *    Discount rate:      10%
      *    ============================================
           MOVE -500000.00 TO WS-CASH-FLOW(1)
           MOVE  125000.00 TO WS-CASH-FLOW(2)
           MOVE  150000.00 TO WS-CASH-FLOW(3)
           MOVE  175000.00 TO WS-CASH-FLOW(4)
           MOVE  150000.00 TO WS-CASH-FLOW(5)
           MOVE  100000.00 TO WS-CASH-FLOW(6)
           MOVE 6          TO WS-NUM-PERIODS
           MOVE 0.10       TO WS-DISCOUNT-RATE

           PERFORM 2000-CALC-NPV

           MOVE WS-NPV-RESULT TO WS-DISP-NPV
           DISPLAY '==============================='
           DISPLAY 'NET PRESENT VALUE CALCULATION'
           DISPLAY 'DISCOUNT RATE: 10.00%'
           PERFORM VARYING WS-PERIOD-CTR FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-NUM-PERIODS
               MOVE WS-CASH-FLOW(WS-PERIOD-CTR) TO WS-DISP-CF
               DISPLAY '  PERIOD ' WS-PERIOD-CTR ': ' WS-DISP-CF
           END-PERFORM
           DISPLAY 'NPV:           ' WS-DISP-NPV

      *    ============================================
      *    Example 3: Internal Rate of Return (IRR)
      *    Using same cash flows as NPV example
      *    ============================================
           PERFORM 3000-CALC-IRR

           MULTIPLY WS-IRR-RESULT BY 100
               GIVING WS-DISP-IRR
           DISPLAY '==============================='
           DISPLAY 'INTERNAL RATE OF RETURN'
           DISPLAY 'IRR:           ' WS-DISP-IRR '%'

           STOP RUN
           .

       1000-CALC-PRESENT-VALUE.
      *    PV = FV / (1 + r)^n
      *    Calculate (1 + r)^n iteratively
           ADD 1 TO WS-DISCOUNT-RATE
               GIVING WS-GROWTH-FACTOR
           MOVE 1.0 TO WS-POWER-RESULT

           PERFORM VARYING WS-POWER-CTR FROM 1 BY 1
               UNTIL WS-POWER-CTR > WS-NUM-PERIODS
               MULTIPLY WS-POWER-RESULT BY WS-GROWTH-FACTOR
                   GIVING WS-POWER-RESULT ROUNDED
           END-PERFORM

           DIVIDE WS-FUTURE-VALUE BY WS-POWER-RESULT
               GIVING WS-PRESENT-VALUE ROUNDED
           .

       2000-CALC-NPV.
      *    NPV = sum of CF(t) / (1 + r)^t for t = 0..n-1
           MOVE ZERO TO WS-NPV-RESULT

           PERFORM VARYING WS-PERIOD-CTR FROM 1 BY 1
               UNTIL WS-PERIOD-CTR > WS-NUM-PERIODS

      *        Calculate discount factor (1 + r)^(t-1)
               ADD 1 TO WS-DISCOUNT-RATE
                   GIVING WS-GROWTH-FACTOR
               MOVE 1.0 TO WS-DISCOUNT-FACTOR

               SUBTRACT 1 FROM WS-PERIOD-CTR
                   GIVING WS-POWER-CTR
               PERFORM VARYING WS-POWER-CTR
                   FROM WS-POWER-CTR BY -1
                   UNTIL WS-POWER-CTR < 1
                   MULTIPLY WS-DISCOUNT-FACTOR
                       BY WS-GROWTH-FACTOR
                       GIVING WS-DISCOUNT-FACTOR ROUNDED
               END-PERFORM

      *        PV of this cash flow
               DIVIDE WS-CASH-FLOW(WS-PERIOD-CTR)
                   BY WS-DISCOUNT-FACTOR
                   GIVING WS-PV-CASHFLOW ROUNDED

               ADD WS-PV-CASHFLOW TO WS-NPV-RESULT

           END-PERFORM
           .

       3000-CALC-IRR.
      *    IRR is the discount rate that makes NPV = 0
      *    Use bisection method to find it
           MOVE 0.00 TO WS-IRR-LOW
           MOVE 1.00 TO WS-IRR-HIGH
           MOVE ZERO TO WS-IRR-ITERATION

           PERFORM UNTIL WS-IRR-ITERATION >= WS-IRR-MAX-ITER

               ADD 1 TO WS-IRR-ITERATION

      *        Calculate midpoint
               ADD WS-IRR-LOW TO WS-IRR-HIGH
                   GIVING WS-IRR-MID
               DIVIDE WS-IRR-MID BY 2
                   GIVING WS-IRR-MID

      *        Calculate NPV at midpoint rate
               MOVE WS-IRR-MID TO WS-DISCOUNT-RATE
               PERFORM 2000-CALC-NPV
               MOVE WS-NPV-RESULT TO WS-NPV-MID

      *        Check convergence
               IF WS-NPV-MID > -0.01 AND WS-NPV-MID < 0.01
                   MOVE WS-IRR-MID TO WS-IRR-RESULT
                   EXIT PERFORM
               END-IF

      *        Narrow the search range
               IF WS-NPV-MID > 0
                   MOVE WS-IRR-MID TO WS-IRR-LOW
               ELSE
                   MOVE WS-IRR-MID TO WS-IRR-HIGH
               END-IF

      *        Check if range is narrow enough
               SUBTRACT WS-IRR-LOW FROM WS-IRR-HIGH
                   GIVING WS-ACCUMULATOR
               IF WS-ACCUMULATOR < WS-IRR-TOLERANCE
                   MOVE WS-IRR-MID TO WS-IRR-RESULT
                   EXIT PERFORM
               END-IF

           END-PERFORM
           .

33.9 Regulatory Compliance

Financial calculations must meet strict regulatory requirements. COBOL programs in banking, insurance, and securities must implement specific precision requirements, maintain complete audit trails, and follow mandated calculation methods.

Precision Requirements

Different regulatory frameworks mandate different levels of precision:

  • Truth in Lending Act (TILA): APR must be accurate to 1/8 of 1 percentage point (0.125%).
  • Truth in Savings Act (TISA): APY must be accurate to 1/100 of 1 percentage point (0.01%).
  • IRS Tax Calculations: Must be accurate to the penny for amounts, to 4 decimal places for tax rates.
  • Securities Pricing: Bond prices must be accurate to 1/32 or 1/64 of a point depending on instrument type.

Audit Trail Implementation

Every financial calculation should produce a traceable audit record. The following structure captures calculation details for regulatory examination:

       01  WS-AUDIT-RECORD.
           05  WS-AUDIT-TIMESTAMP.
               10  WS-AUDIT-DATE    PIC 9(8).
               10  WS-AUDIT-TIME    PIC 9(8).
           05  WS-AUDIT-PROGRAM     PIC X(8).
           05  WS-AUDIT-USER        PIC X(8).
           05  WS-AUDIT-ACCOUNT     PIC X(15).
           05  WS-AUDIT-CALC-TYPE   PIC X(4).
               88  CALC-SIMPLE-INT  VALUE 'SINT'.
               88  CALC-COMPOUND    VALUE 'CINT'.
               88  CALC-AMORT       VALUE 'AMRT'.
               88  CALC-TAX         VALUE 'TAX '.
               88  CALC-NPV         VALUE 'NPV '.
           05  WS-AUDIT-INPUTS.
               10  WS-AUDIT-INPUT-1 PIC S9(15)V9(6) COMP-3.
               10  WS-AUDIT-INPUT-2 PIC S9(15)V9(6) COMP-3.
               10  WS-AUDIT-INPUT-3 PIC S9(15)V9(6) COMP-3.
               10  WS-AUDIT-INPUT-4 PIC S9(15)V9(6) COMP-3.
           05  WS-AUDIT-RESULT      PIC S9(15)V9(6) COMP-3.
           05  WS-AUDIT-PRECISION   PIC 9(2).
           05  WS-AUDIT-ROUND-MODE  PIC X(4).
           05  WS-AUDIT-STATUS      PIC X(2).
               88  AUDIT-SUCCESS    VALUE '00'.
               88  AUDIT-OVERFLOW   VALUE '01'.
               88  AUDIT-UNDERFLOW  VALUE '02'.
               88  AUDIT-ERROR      VALUE '99'.

       1000-WRITE-AUDIT-TRAIL.
      *    Populate audit timestamp
           MOVE FUNCTION CURRENT-DATE(1:8) TO WS-AUDIT-DATE
           MOVE FUNCTION CURRENT-DATE(9:8) TO WS-AUDIT-TIME
      *    Write audit record to sequential audit file
           WRITE AUDIT-RECORD FROM WS-AUDIT-RECORD
           IF WS-AUDIT-FILE-STATUS NOT = '00'
               DISPLAY 'AUDIT WRITE ERROR: ' WS-AUDIT-FILE-STATUS
               PERFORM 9999-ABEND-PROCESSING
           END-IF
           .

Regulatory Calculation Validation

Production financial programs should include self-validation routines that verify calculations against known test cases. This practice satisfies regulatory examination requirements:

       1000-VALIDATE-INTEREST-CALC.
      *    Known test case: $10,000 at 5% simple interest
      *    for 90 days (Actual/365) = $123.29
           MOVE 10000.00 TO WS-PRINCIPAL
           MOVE 5.000000 TO WS-ANNUAL-RATE
           MOVE 90       TO WS-TERM-DAYS
           MOVE 365      TO WS-DAYS-IN-YEAR

           PERFORM 2000-CALC-SIMPLE-INTEREST

           IF WS-INTEREST-RESULT NOT = 123.29
               DISPLAY 'VALIDATION FAILURE: SIMPLE INTEREST'
               DISPLAY 'EXPECTED: 123.29'
               DISPLAY 'ACTUAL:   ' WS-INTEREST-RESULT
               MOVE 'VF' TO WS-RETURN-CODE
           ELSE
               DISPLAY 'VALIDATION PASSED: SIMPLE INTEREST'
           END-IF
           .

33.10 Handling Very Large Amounts

As financial systems process increasingly large transactions, COBOL programs must handle amounts that exceed traditional PICTURE clause limits. National debt calculations, derivatives notional amounts, and sovereign wealth fund transactions can involve amounts in the trillions.

Extending PICTURE Clauses

Standard COBOL supports up to 18 digits in a numeric field. For amounts exceeding this, you can use COBOL's extended precision capabilities or implement multi-field arithmetic:

      *    Standard maximum: 18 digits (quadrillions)
       01  WS-LARGE-AMOUNT      PIC S9(16)V99 COMP-3.
      *    Maximum value: 9,999,999,999,999,999.99

      *    IBM Enterprise COBOL supports 31-digit fields
      *    with ARITH(EXTEND) compiler option
       01  WS-EXTENDED-AMOUNT   PIC S9(29)V99 COMP-3.
      *    Maximum: 29-digit integer + 2 decimal places

Overflow Detection

Financial programs must detect arithmetic overflow before it corrupts data. COBOL provides the ON SIZE ERROR phrase for this purpose:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. OVERFLOW-DETECT.
      *================================================================*
      * PROGRAM: OVERFLOW-DETECT                                       *
      * PURPOSE: Demonstrate overflow detection in financial            *
      *          calculations using ON SIZE ERROR                      *
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-AMOUNT-1          PIC S9(13)V99 COMP-3.
       01  WS-AMOUNT-2          PIC S9(13)V99 COMP-3.
       01  WS-RESULT            PIC S9(13)V99 COMP-3.
       01  WS-LARGE-RESULT      PIC S9(17)V99 COMP-3.
       01  WS-OVERFLOW-FLAG     PIC X VALUE 'N'.
           88  OVERFLOW-OCCURRED VALUE 'Y'.

       PROCEDURE DIVISION.

       0000-MAIN-PROCESS.
           MOVE 9999999999999.99 TO WS-AMOUNT-1
           MOVE 5000000000000.00 TO WS-AMOUNT-2

      *    This addition will overflow the result field
           ADD WS-AMOUNT-1 TO WS-AMOUNT-2
               GIVING WS-RESULT
               ON SIZE ERROR
                   SET OVERFLOW-OCCURRED TO TRUE
                   DISPLAY 'OVERFLOW DETECTED IN ADDITION'
                   DISPLAY 'ATTEMPTING WITH LARGER FIELD...'
      *            Retry with larger result field
                   ADD WS-AMOUNT-1 TO WS-AMOUNT-2
                       GIVING WS-LARGE-RESULT
                       ON SIZE ERROR
                           DISPLAY 'CRITICAL: OVERFLOW IN LARGE '
                               'FIELD - ABORTING'
                           STOP RUN
                       NOT ON SIZE ERROR
                           DISPLAY 'RECOVERED: RESULT = '
                               WS-LARGE-RESULT
                   END-ADD
               NOT ON SIZE ERROR
                   DISPLAY 'RESULT: ' WS-RESULT
           END-ADD

           STOP RUN
           .

Multi-Precision Arithmetic

For amounts that exceed even 31 digits (certain government bond market aggregate calculations), you can implement multi-field arithmetic:

       01  WS-MEGA-AMOUNT.
           05  WS-MEGA-HIGH     PIC S9(18) COMP-3.
           05  WS-MEGA-LOW      PIC S9(18) COMP-3.
      *    Represents up to 36-digit amounts
      *    Total = HIGH * 10^18 + LOW

       01  WS-MEGA-AMOUNT-2.
           05  WS-MEGA-HIGH-2   PIC S9(18) COMP-3.
           05  WS-MEGA-LOW-2    PIC S9(18) COMP-3.

       01  WS-MEGA-RESULT.
           05  WS-MEGA-RES-HIGH PIC S9(18) COMP-3.
           05  WS-MEGA-RES-LOW  PIC S9(18) COMP-3.

       01  WS-CARRY             PIC S9(18) COMP-3.
       01  WS-OVERFLOW-LIMIT    PIC S9(18) COMP-3
                                VALUE 999999999999999999.

       1000-MEGA-ADD.
      *    Add two multi-precision amounts
           ADD WS-MEGA-LOW TO WS-MEGA-LOW-2
               GIVING WS-MEGA-RES-LOW

      *    Check for carry
           IF WS-MEGA-RES-LOW > WS-OVERFLOW-LIMIT
               SUBTRACT WS-OVERFLOW-LIMIT FROM WS-MEGA-RES-LOW
               SUBTRACT 1 FROM WS-MEGA-RES-LOW
               MOVE 1 TO WS-CARRY
           ELSE
               MOVE 0 TO WS-CARRY
           END-IF

      *    Add high-order portions plus carry
           ADD WS-MEGA-HIGH WS-MEGA-HIGH-2 WS-CARRY
               GIVING WS-MEGA-RES-HIGH
               ON SIZE ERROR
                   DISPLAY 'OVERFLOW: AMOUNT EXCEEDS 36 DIGITS'
           END-ADD
           .

JCL for a Complete Financial Batch Job

A typical financial calculation batch job on an IBM mainframe combines multiple programs in a single job stream:

//FINCALC  JOB (ACCT),'FINANCIAL CALCS',CLASS=A,
//         MSGCLASS=X,NOTIFY=&SYSUID,
//         MSGLEVEL=(1,1)
//*
//*================================================================*
//* STEP 1: EXTRACT LOAN RECORDS FOR INTEREST CALCULATION          *
//*================================================================*
//EXTRACT  EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=PROD.LOAN.MASTER,DISP=SHR
//SORTOUT  DD DSN=&&LOANEXT,
//         DISP=(NEW,PASS),
//         SPACE=(CYL,(10,5),RLSE),
//         DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//SYSIN    DD *
  SORT FIELDS=(1,10,CH,A)
  INCLUDE COND=(45,1,CH,EQ,C'A')
/*
//*
//*================================================================*
//* STEP 2: CALCULATE SIMPLE INTEREST                              *
//*================================================================*
//CALCINT  EXEC PGM=SIMPINT
//STEPLIB  DD DSN=PROD.FINANCE.LOADLIB,DISP=SHR
//LOANIN   DD DSN=&&LOANEXT,DISP=(OLD,PASS)
//RPTOUT   DD DSN=PROD.FINANCE.INTEREST.REPORT,
//         DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(5,2),RLSE),
//         DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//AUDITOUT DD DSN=PROD.FINANCE.AUDIT.D&LYYMMDD,
//         DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(2,1),RLSE),
//         DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//*
//*================================================================*
//* STEP 3: GENERATE AMORTIZATION SCHEDULES                        *
//*================================================================*
//AMORT    EXEC PGM=AMORTSCH,COND=(0,NE,CALCINT)
//STEPLIB  DD DSN=PROD.FINANCE.LOADLIB,DISP=SHR
//LOANIN   DD DSN=&&LOANEXT,DISP=(OLD,DELETE)
//AMORTOUT DD DSN=PROD.FINANCE.AMORT.REPORT,
//         DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(20,10),RLSE),
//         DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*
//*
//*================================================================*
//* STEP 4: REGULATORY COMPLIANCE VALIDATION                       *
//*================================================================*
//VALIDATE EXEC PGM=REGVALID,COND=(0,NE,AMORT)
//STEPLIB  DD DSN=PROD.FINANCE.LOADLIB,DISP=SHR
//RPTIN    DD DSN=PROD.FINANCE.INTEREST.REPORT,DISP=SHR
//         DD DSN=PROD.FINANCE.AMORT.REPORT,DISP=SHR
//VALRPT   DD DSN=PROD.FINANCE.VALID.REPORT,
//         DISP=(NEW,CATLG,DELETE),
//         SPACE=(CYL,(1,1),RLSE),
//         DCB=(RECFM=FB,LRECL=132,BLKSIZE=0)
//SYSOUT   DD SYSOUT=*

33.11 Summary

Financial calculations represent COBOL's greatest strength and its primary reason for enduring relevance in the 21st century. The key principles covered in this chapter include:

  1. Decimal arithmetic precision: COBOL's packed-decimal format stores monetary amounts exactly, eliminating the floating-point errors that plague other languages in financial applications.

  2. Interest calculation methods: Simple interest, compound interest, and daily accrual all follow well-defined mathematical formulas that translate directly into COBOL arithmetic statements.

  3. Amortization schedules: The standard amortization formula, implemented through iterative calculation, produces payment schedules for mortgages, auto loans, and personal loans. Always handle the final payment adjustment to account for accumulated rounding.

  4. Currency handling: The ROUNDED phrase, banker's rounding, and multi-currency conversion require careful attention to precision and rounding mode selection.

  5. Tax calculations: Progressive tax brackets, payroll withholding, and sales tax all use table-driven approaches that separate business rules from program logic.

  6. Day count conventions: Different financial instruments require different day counting methods (30/360, Actual/360, Actual/365, Actual/Actual), and using the wrong convention produces incorrect interest amounts.

  7. Time value of money: Present value, future value, NPV, and IRR calculations support investment analysis and accounting valuations.

  8. Regulatory compliance: Financial programs must maintain audit trails, meet precision requirements, and include self-validation routines.

  9. Large amount handling: Extended PICTURE clauses, overflow detection with ON SIZE ERROR, and multi-precision arithmetic techniques handle amounts that exceed standard field sizes.

These concepts build on the arithmetic operations covered in Chapter 6 and the data type fundamentals from Chapter 3. In the next chapter, we will examine banking and transaction processing systems, where these financial calculations operate within the context of real-time and batch processing environments.


Review Questions

  1. Why does COBOL's packed-decimal arithmetic produce exact results while floating-point arithmetic does not?

  2. What is the difference between truncation and rounding in COBOL, and why does it matter for financial calculations?

  3. Explain why banker's rounding is preferred over standard rounding for high-volume financial processing.

  4. A mortgage of $300,000 at 5.75% annual interest for 30 years uses monthly compounding. How would you structure the COBOL data fields for this calculation?

  5. What are the four primary day count conventions, and when is each one used?

  6. Explain the bisection method for calculating Internal Rate of Return (IRR) in COBOL.

  7. How does the ON SIZE ERROR phrase help prevent data corruption in financial programs?

  8. Why should financial calculations use intermediate fields with more decimal places than the final result?


Programming Exercises

  1. Certificate of Deposit Calculator: Write a COBOL program that calculates the maturity value of a CD given principal, APY, term in months, and compounding frequency. Include early withdrawal penalty calculations (typically 90 or 180 days of interest).

  2. Credit Card Interest Calculator: Implement the average daily balance method for credit card interest calculation. Process a file of daily transactions and compute the monthly finance charge.

  3. Bond Pricing Program: Write a program that calculates the price of a fixed-rate bond given face value, coupon rate, yield to maturity, and years to maturity. Use the 30/360 day count convention.

  4. Multi-Bracket Tax Engine: Extend the tax calculation program to handle both federal and state taxes, including state-specific bracket tables loaded from a configuration file.

  5. Loan Comparison Tool: Create a program that compares different loan options by calculating total interest paid, monthly payment, and effective interest rate for each option, producing a side-by-side comparison report.