canadian-mortgage-math-engine
v1.0.1
Published
Open-source reference and TypeScript engine for Canadian mortgage qualification, OSFI B-20 stress-test, and semi-annual compounding math.
Maintainers
Readme
Canadian Mortgage & Financial Math Engine 🇨🇦
An open-source reference, mathematical specification, and TypeScript implementation of official Canadian lending, mortgage qualification, and taxation formulas. Includes Bank of Canada / OSFI B-20 stress-test rules, semi-annual compounding equations, GDS/TDS limits, and provincial tax brackets.
📌 Why This Exists
Most mortgage and finance calculators on the internet default to US lending standards (monthly compounding). In Canada, federal lending laws mandate semi-annual compounding for fixed-rate mortgages, and OSFI B-20 enforces strict qualifying stress tests.
Using US formulas for Canadian mortgages causes errors in amortization schedules, payment amounts, and debt-service ratios. This repository serves as the transparent, open-source mathematical reference for developers, financial analysts, and mortgage professionals.
🚨 The Hidden $4,479 Calculation Error
Here is what happens when software calculates a typical Canadian home loan using standard US monthly compounding instead of statutory Canadian compounding:
| Scenario: $500,000 Loan @ 5.00% (25-Yr Amortization) | US Monthly Compounding (Default Web Code) | Canadian Semi-Annual Compounding (Interest Act Legal) | Discrepancy / Phantom Error | | :--- | :--- | :--- | :--- | | Monthly Payment | $2,922.95 | $2,908.02 | +$14.93 / month | | Total Paid (25 Years) | $876,885.00 | $872,406.00 | +$4,479.00 CAD | | Total Interest Paid | $376,885.00 | $372,406.00 | +$4,479.00 CAD |
A difference of $14.93/month might seem negligible, but over a standard 25-year amortization it miscalculates mortgage debt by nearly $4,500 CAD. Using American mortgage math on Canadian loans distorts debt-service calculations, misleads borrowers comparing bank offers, and miscalculates qualification thresholds.
Need an interactive browser tool? Test these formulas with visual charts and amortization schedules on UseCOS Canadian Mortgage Calculator and the Mortgage Renewal Offer Analyzer.
📐 1. Canadian Mortgage Compounding Formula
Under Canadian federal law (Interest Act, Section 6), interest on mortgages must be quoted on a semi-annual or annual compounding basis. Fixed-rate Canadian mortgages compound semi-annually, not in advance.
Mathematical Derivation
To calculate the monthly payment from an annual nominal rate $r$:
Semi-Annual Compounding Factor: $$f = 1 + \frac{r}{2}$$
Effective Monthly Interest Rate ($i$): $$i = \left(1 + \frac{r}{2}\right)^{\frac{2}{12}} - 1 = \left(1 + \frac{r}{2}\right)^{\frac{1}{6}} - 1$$
Monthly Payment ($P$): Given loan principal $L$ and total amortization months $n$: $$P = L \times \frac{i \times (1 + i)^n}{(1 + i)^n - 1}$$
TypeScript Implementation
/**
* Calculates monthly mortgage payment using Canadian semi-annual compounding.
* @param principal Loan amount in CAD ($)
* @param annualRate Nominal annual interest rate as percentage (e.g. 4.79 for 4.79%)
* @param amortizationYears Total amortization period in years (e.g. 25)
*/
export function calculateCanadianMonthlyPayment(
principal: number,
annualRate: number,
amortizationYears: number
): number {
if (principal <= 0 || amortizationYears <= 0) return 0;
if (annualRate === 0) return principal / (amortizationYears * 12);
const r = annualRate / 100;
// Canadian Semi-Annual Compounding to Monthly Equivalent
const monthlyRate = Math.pow(1 + r / 2, 2 / 12) - 1;
const totalMonths = amortizationYears * 12;
const payment =
(principal * (monthlyRate * Math.pow(1 + monthlyRate, totalMonths))) /
(Math.pow(1 + monthlyRate, totalMonths) - 1);
return Math.round(payment * 100) / 100;
}
// Example Test Vector:
// $500,000 principal at 5.00% for 25 years
// Result: $2,908.02 CAD / month🛡️ 2. OSFI Guideline B-20 Stress Test (Qualification Rate)
Federal regulation requires federally regulated financial institutions (FRFIs) to qualify uninsured and insured borrowers at the higher of:
- The contract mortgage interest rate $+ 2.00%$
- The minimum qualifying rate (floor set by OSFI / BoC, currently $5.25%$)
$$r_{\text{qualify}} = \max(r_{\text{contract}} + 2.00%, 5.25%)$$
Debt Service Caps
| Ratio | Cap (Standard) | Cap (Exceptional / Strong Credit) | Formula | | :--- | :--- | :--- | :--- | | GDS (Gross Debt Service) | 39.0% | 39.0% | $\frac{\text{Principal} + \text{Interest} + \text{Property Tax} + \text{Heating} + 50%\text{Condo Fees}}{\text{Gross Annual Income}} \times 100$ | | TDS (Total Debt Service) | 44.0% | 44.0% | $\frac{\text{Housing Costs} + \text{Credit Cards} + \text{Auto Loans} + \text{Other Debts}}{\text{Gross Annual Income}} \times 100$ |
TypeScript Implementation
export function getQualifyingStressRate(contractRate: number): number {
const OSFI_FLOOR = 5.25;
return Math.max(contractRate + 2.0, OSFI_FLOOR);
}
export function checkDebtServiceQualification(params: {
grossAnnualIncome: number;
monthlyPITI: number; // Stress-tested Principal + Interest + Property Tax + Heating
monthlyOtherDebts: number; // Car payments, student loans, credit lines
}): { gds: number; tds: number; qualifies: boolean } {
const grossMonthly = params.grossAnnualIncome / 12;
const gds = (params.monthlyPITI / grossMonthly) * 100;
const tds = ((params.monthlyPITI + params.monthlyOtherDebts) / grossMonthly) * 100;
return {
gds: Math.round(gds * 10) / 10,
tds: Math.round(tds * 10) / 10,
qualifies: gds <= 39.0 && tds <= 44.0
};
}🔄 3. Mortgage Renewal "Break-Even" & Net Advantage
When a borrower renews or switches lenders, comparing headline interest rates is misleading because lenders charge transfer fees, appraisal costs, or offer cashback bonuses.
True Term Net Cost Equation
$$\text{Net Term Advantage} = (\text{Cost}{\text{Current}} - \text{Cost}{\text{Offer}}) + \text{Cashback} - (\text{Discharge Fee} + \text{Appraisal Fee} + \text{Title Fee})$$
For an interactive comparison of total cost across 3-year and 5-year terms, run your renewal statement through the UseCOS Renewal Offer Grader.
📊 4. Canadian 2026 Federal Tax Brackets
Federal tax brackets indexed for inflation for the 2026 tax year:
| Federal Tax Bracket | Tax Rate | Cumulative Tax at Base | | :--- | :--- | :--- | | $0 to $57,375 | 15.0% | $0.00 | | $57,376 to $114,750 | 20.5% | $8,606.25 | | $114,751 to $177,882 | 26.0% | $20,368.13 | | $177,883 to $253,414 | 29.0% | $36,782.45 | | Over $253,414 | 33.0% | $58,686.73 |
Basic Personal Amount (BPA): Federal maximum $16,129 with phase-out above $177,882.
🛠️ Interactive Tools & Reference Suite
All algorithms in this repository are actively maintained and powering the open browser utilities on UseCOS:
- 🏠 Canadian Mortgage Calculator — Semi-annual compounding, CMHC insurance tiers, and amortization schedules.
- 🔄 Mortgage Renewal Offer Analyzer — Term cost, switching fees, and penalty calculations.
- 💼 Paycheck & Tax Calculator Canada — Federal & provincial brackets, CPP/CPP2, and EI limits.
- 🏷️ GST/HST Canada Table & Calculator — Provincial rates and input tax credit breakdowns.
- 🏢 DSCR & Rental Property Calculator — Debt service coverage ratios and cap rates.
📄 License & Contributing
Licensed under the MIT License. Contributions, bug reports, and updates for annual federal/provincial rate indexation are welcome via GitHub Pull Requests.
