{
  "schemaVersion": 1,
  "slug": "compound-interest-calculator",
  "category": "finance",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "a7e6028e2917f53136967559a69f772013e2624e676694b413b57e969c10d05e",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/compound-interest-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "A = P·b^(n·t) + Σⱼ₌₁ᴺ C·b^(n·(t−(j−δ)/f)); b = 1+r/n; N = ⌊f·t⌋; I = A−(P+C·N)",
    "vars": [
      [
        "A",
        "Final balance"
      ],
      [
        "P",
        "Initial amount"
      ],
      [
        "r",
        "Annual interest rate / 100"
      ],
      [
        "n",
        "Compounding frequency"
      ],
      [
        "t",
        "Years"
      ],
      [
        "C",
        "Recurring deposit"
      ],
      [
        "f",
        "Deposit frequency"
      ],
      [
        "N",
        "⌊f·t⌋"
      ],
      [
        "δ",
        "0: Period end; 1: Period beginning"
      ],
      [
        "I",
        "Interest earned"
      ]
    ],
    "note": "Growth uses a constant nominal annual rate and fractional compounding periods between deposit dates. Monthly or annual deposits use the selected start/end timing; only complete deposit periods count. Taxes, fees and inflation are excluded; returns are not guaranteed. B−A compares the selected final balances, which may have different time horizons. The annual table and growth chart show A."
  },
  "externalEvidence": {
    "reviewedAt": "2026-10-22",
    "sources": [
      {
        "name": "OpenStax — Compound interest equation",
        "url": "https://openstax.org/books/college-algebra-2e/pages/6-key-equations"
      },
      {
        "name": "SEC Investor.gov — Compound interest inputs",
        "url": "https://www.investor.gov/financial-tools-calculators/calculators/compound-interest-calculator"
      }
    ],
    "implementationHash": "a7e6028e2917f53136967559a69f772013e2624e676694b413b57e969c10d05e",
    "basis": "Apply the nominal annual rate divided by the compounding frequency to the initial balance. Each recurring deposit grows from its scheduled beginning or end of a complete month or year; a partial final deposit period adds growth but no new deposit. At 0% interest, an initial 1000 plus twelve monthly deposits of 100 ends at 2200 after one year. Total deposited includes the initial amount; interest is final balance minus that total. Scenario B uses its own rate, deposit and duration, while the annual table and chart show A. A B − A difference with different durations is a balance comparison, not an equal-time return comparison. Supported limits are 1 billion initially, 10 million per deposit, 0–100 years and −100% to 100% nominal annual interest. Taxes, fees and inflation are excluded; returns are not guaranteed.",
    "limitations": "Growth uses a constant nominal annual rate and fractional compounding periods between deposit dates. Monthly or annual deposits use the selected start/end timing; only complete deposit periods count. Taxes, fees and inflation are excluded; returns are not guaranteed. B−A compares the selected final balances, which may have different time horizons. The annual table and growth chart show A."
  },
  "contract": {
    "compute": "const project=(P,rate,t,C,f,timing)=>{\n const b=1+rate/100/compounds, count=Math.floor(t*f+1e-9), g=Math.pow(b,compounds/f);\n let saved=0;\n for(let j=0;j<count;j++) saved=saved*g+C;\n if(timing==='begin') saved*=g;\n saved*=Math.pow(b,compounds*Math.max(0,t-count/f));\n const amount=P*Math.pow(b,compounds*t)+saved, contribution=P+C*count;\n return {amount,contribution,interest:amount-contribution};\n};\nif(![1,4,12,365].includes(compounds)||![1,12].includes(depositFrequency)||!['begin','end'].includes(depositTiming)||!['off','on'].includes(compare)||[[principal,1e9],[deposit,1e7],[years,100],...(compare==='on'?[[depositB,1e7],[yearsB,100]]:[])].some(([v,max])=>!Number.isFinite(v)||v<0||v>max)||[annualRate,...(compare==='on'?[rateB]:[])].some(v=>!Number.isFinite(v)||v < -100||v>100))return{__error:'inputError'};\nconst a=project(principal,annualRate,years,deposit,depositFrequency,depositTiming);\nconst b=compare==='on'?project(principal,rateB,yearsB,depositB,depositFrequency,depositTiming):null;\nconst growth=[{period:0,contribution:principal,interest:0,balance:principal}];\nfor(let y=1;y<years;y++){const row=project(principal,annualRate,y,deposit,depositFrequency,depositTiming);growth.push({period:y,contribution:row.contribution,interest:row.interest,balance:row.amount});}\nif(years>0)growth.push({period:years,contribution:a.contribution,interest:a.interest,balance:a.amount});\nreturn{amount:a.amount,contribution:a.contribution,interest:a.interest,amountB:b?b.amount:null,contributionB:b?b.contribution:null,interestB:b?b.interest:null,balanceDifference:b?b.amount-a.amount:null,__growth:growth};",
    "inputs": [
      {
        "name": "principal",
        "type": "number",
        "default": 10000,
        "min": 0,
        "step": 100,
        "max": 1000000000,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "annualRate",
        "type": "number",
        "default": 7,
        "min": -100,
        "max": 100,
        "step": 0.01,
        "slider": true,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "years",
        "type": "number",
        "default": 10,
        "min": 0,
        "max": 100,
        "step": 0.1,
        "slider": true,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "compounds",
        "type": "select",
        "default": 12,
        "options": [
          {
            "value": 1
          },
          {
            "value": 4
          },
          {
            "value": 12
          },
          {
            "value": 365
          }
        ],
        "advanced": true
      },
      {
        "name": "deposit",
        "type": "number",
        "default": 0,
        "min": 0,
        "max": 10000000,
        "step": 10,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "depositFrequency",
        "type": "select",
        "default": 12,
        "options": [
          {
            "value": 12
          },
          {
            "value": 1
          }
        ]
      },
      {
        "name": "depositTiming",
        "type": "select",
        "default": "end",
        "options": [
          {
            "value": "end"
          },
          {
            "value": "begin"
          }
        ],
        "advanced": true
      },
      {
        "name": "compare",
        "type": "select",
        "default": "off",
        "options": [
          {
            "value": "off"
          },
          {
            "value": "on"
          }
        ]
      },
      {
        "name": "depositB",
        "type": "number",
        "default": 100,
        "min": 0,
        "max": 10000000,
        "step": 10,
        "showWhen": {
          "compare": [
            "on"
          ]
        },
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "yearsB",
        "type": "number",
        "default": 10,
        "min": 0,
        "max": 100,
        "step": 0.1,
        "showWhen": {
          "compare": [
            "on"
          ]
        },
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "rateB",
        "type": "number",
        "default": 7,
        "min": -100,
        "max": 100,
        "step": 0.01,
        "showWhen": {
          "compare": [
            "on"
          ]
        },
        "requiredValue": true,
        "strictRange": true
      }
    ],
    "outputs": [
      {
        "name": "amount",
        "precision": 2,
        "trimTrailingZeros": true
      },
      {
        "name": "contribution",
        "precision": 2,
        "trimTrailingZeros": true
      },
      {
        "name": "interest",
        "precision": 2,
        "trimTrailingZeros": true
      },
      {
        "name": "amountB",
        "precision": 2,
        "optional": true,
        "trimTrailingZeros": true
      },
      {
        "name": "contributionB",
        "precision": 2,
        "optional": true,
        "trimTrailingZeros": true
      },
      {
        "name": "interestB",
        "precision": 2,
        "optional": true,
        "trimTrailingZeros": true
      },
      {
        "name": "balanceDifference",
        "precision": 2,
        "optional": true,
        "trimTrailingZeros": true
      }
    ],
    "interpret": null
  }
}
