# Interest/APY rounded consistently

- **Pattern:** `ab-001450` (`finserv-form-validation.calculation-accuracy.interest-rounding-consistency`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001450
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001450)

## Why it matters

Two calculation sites that both compute interest but use different rounding functions — one `Math.round()`, one `Math.floor()` — will produce different balances for identical inputs. The discrepancy shows up as a mismatch between the displayed APY and the actual accrued interest, leading to customer disputes and reconciliation failures. CWE-682 covers incorrect financial calculation; ISO 25010 maintainability requires that the same logical operation produce the same result regardless of call site.

## Severity rationale

Low because inconsistent rounding accumulates slowly and typically surfaces only during reconciliation or customer disputes, not as immediate transaction failures.

## Remediation

Centralize interest rounding into a single exported function in `src/lib/finance.ts` and replace every direct `Math.round` / `Math.floor` / `.toFixed` call in financial logic with it.

```ts
// src/lib/finance.ts
import Decimal from 'decimal.js';

/** Canonical rounding for all interest / APY calculations. */
export function roundInterestCents(value: number): number {
  return new Decimal(value)
    .toDecimalPlaces(0, Decimal.ROUND_HALF_EVEN)
    .toNumber();
}
```

Search `src/` for `Math.round`, `Math.floor`, and `.toFixed` within files that reference `interest`, `apy`, or `rate`, and replace each with `roundInterestCents()`.

## Detection

- **ID:** `interest-rounding-consistency`
- **Severity:** `low`
- **What to look for:** Enumerate all interest/APY calculation sites in the codebase. For each, quote the rounding function used (e.g., `Math.round`, `Math.floor`, `toDecimalPlaces(2, ROUND_HALF_EVEN)`). Count the distinct rounding strategies observed. Report: "X interest calculation sites found using Y distinct rounding strategies."
- **Pass criteria:** At least 100% of interest and APY calculation sites use the same rounding strategy. The strategy is documented (a constant, comment, or named function like `bankersRound`). No more than 1 distinct rounding strategy across all interest sites.
- **Fail criteria:** At least 2 distinct rounding strategies are used across interest calculations, or no rounding strategy is documented.
- **Skip (N/A) when:** The project has no interest or APY calculations (0 interest calculation sites found).
- **Detail on fail:** Example: `"APY calculation in dashboard uses Math.round() (round-half-up). Interest accrual in API uses Math.floor() (round-down). Inconsistency between display and actual accrual."` or `"No documented rounding strategy for interest"`
- **Cross-reference:** The bankers-rounding check verifies the correct algorithm is used; this check verifies it is used consistently everywhere.
- **Remediation:** Document and centralize the rounding strategy in `src/lib/finance.ts`:

  **In `src/lib/finance.ts`:**
  ```ts
  export const ROUNDING_STRATEGY = 'banker'; // round-half-to-even

  export function roundInterest(value) {
    // Implement banker's rounding
    const cents = Math.round(value * 100);
    return cents / 100;
  }

  // Usage everywhere:
  const interest = roundInterest(principal * rate);
  ```

## External references

- cwe CWE-682
- iso-25010 maintainability

Taxons: data-integrity, code-quality

HTML version: https://auditbuffet.com/patterns/ab-001450
