Skip to main content

Methodology · Open Math

APY ↔ APR Conversion

Annual Percentage Yield accounts for intra-year compounding. We convert between nominal APR and effective APY using the standard compounding identities, the same way the Truth in Savings Act defines APY.

Authoritative reference

Our implementation is validated against U.S. Truth in Savings Act (Regulation DD) — APY definition.

Reference test cases

These exact cases are asserted in our unit test suite, so a regression would fail the build.

InputExpected
7% APR compounded monthly 7.23% APY
5% APR compounded daily 5.13% APY

Source code

This is the exact, unmodified source that powers the APY vs APR Calculator.

APY ↔ APR conversion (apy.ts)

// src/lib/finance/apy.ts
//
// APY ↔ APR conversions and rate-period equivalence helpers.
//
// Definitions:
//   APR (Annual Percentage Rate)     = nominal annual rate, ignoring compounding.
//   APY (Annual Percentage Yield)    = effective annual rate after compounding.
//   APY = (1 + APR/n)^n − 1          where n is compounding periods per year.
//
// Reference: Truth in Savings Act, 12 CFR 1030, Appendix A.

/**
 * Convert APR to APY given the compounding frequency.
 * @param apr nominal annual rate as a decimal (0.05 for 5%)
 * @param n   compounding periods per year (e.g. 365, 12, 4, 1)
 */
export function aprToApy(apr: number, n: number): number {
  if (n < 1) throw new Error(`aprToApy: n must be >= 1, got ${n}`);
  if (apr <= 0) return apr;
  return Math.pow(1 + apr / n, n) - 1;
}

/**
 * Convert APY to APR given the compounding frequency.
 * @param apy effective annual yield as a decimal (0.05127 for 5.127%)
 * @param n   compounding periods per year
 */
export function apyToApr(apy: number, n: number): number {
  if (n < 1) throw new Error(`apyToApr: n must be >= 1, got ${n}`);
  if (apy <= 0) return apy;
  return n * (Math.pow(1 + apy, 1 / n) - 1);
}

/**
 * APY for continuous compounding: APY = e^r − 1.
 * The theoretical maximum yield for a given nominal rate.
 */
export function aprToApyContinuous(apr: number): number {
  return Math.exp(apr) - 1;
}

/**
 * Convert an APY to its exact per-period rate so that applying the per-period rate
 * `n` times reproduces the APY.
 *
 * Example: APY 4.5%, applied 12× per year, gives a monthly rate of
 *   (1.045)^(1/12) − 1 ≈ 0.367486%.
 *
 * This is the correct conversion for an account that quotes APY but pays interest
 * monthly. Using `apy / n` (the APR convention) is a common bug — see audit F4.
 */
export function periodRateFromApy(apy: number, n: number): number {
  if (n < 1) throw new Error(`periodRateFromApy: n must be >= 1, got ${n}`);
  if (apy <= 0) return 0;
  return Math.pow(1 + apy, 1 / n) - 1;
}