Votre vrai taux d’imposition au Québec
Trois taux différents racontent toute l’histoire : le marginal statutaire (les tranches), le marginal effectif (TEMI) qui inclut RRQ/AE et la perte de crédits, et le taux effectif moyen sur l’ensemble de votre revenu. On les calcule, on les compare, on explique tout.
Understanding the Three Tax Rates
The media and politicians talk about “tax rates” in the singular. Tax specialists use at least three. Here is the nuance that changes everything.
Based on tax brackets only
Federal (with Quebec 16.5% abatement) + Quebec provincial, on the next dollar earned. This is the rate displayed in most media. It underestimates your true cost.
All Inclusive, Next Dollar Earned
Statutory marginal + QPP + EI + QPIP + loss of refundable credits (GST, CCB, Family Allowance, OAS). This is your true tax cost on the next $1,000 earned. Often 50-70% for families.
On your entire income
Total taxes and contributions divided by your gross income. Always lower than the other two because the first brackets are taxed low (basic personal amount).
Calculator — Your Three Rates Online
Composition of the METR
Breakdown by category of the charges that apply to your next dollar earned.
METR curve by income
The blue dot marker indicates your current income. Move the cursor to explore the tiers.
⚠️ Scope of the calculator — read before you act
Included:
- Quebec federal + provincial tax (2025 brackets) with basic personal amount
- Quebec Abatement 16.5% on Federal Tax
- QPP premiums (basic + additional), Employment Insurance (QC rate), QPIP
- Phase-outs: GST credit, Quebec solidarity credit, CCB (according to # children × bracket), Qc family allowance, PSV recovery (65+)
Not included (and it sometimes changes a lot):
- RRSP/TFSA/RESP/FHSA contributions — which actually reduce your taxable income
- Non-refundable credits: medical expenses, donations, public transit, education, person with a disability, caregiver
- Pension splitting between spouses, professional allowance, deduction for employees
- Annuitant/Self-Employment HSF, Reduced Child Care Subsidy, Public Drug Premium
As a result, this calculator gives orders of magnitude and illustrates the “bumps” of the METR. It is not a substitute for tax software or the advice of a tax professional for important decisions (RRSP vs. TFSA, retirement, sale of assets, etc.).
Compare with an official calculator
To validate the order of magnitude of your average effective rate or file your return, use one of these tools. The full METR (with phase-outs) is rarely displayed by consumer tools — that’s our angle here.
Disposable Income Calculator
Official. Detailed calculation of taxes + contributions + refundable credits, by profile.
Open →Income Tax Calculator
All levels of government and province in Canada. Indexed annually.
Open →TurboTax Software
To file the return. Calculates the exact tax based on your actual credits.
Open →Why the rate you see on the screen is not your true rate
A $70,000 resident doctor with two young children, seen as the “27.5% bracket,” actually loses about 55% of every additional $1,000 earned in overtime:
- Fed+QC brackets: ~32%
- Additional QPP + EI + QPIP: ~6%
- CCB Recovery (2 children, low bracket): 13.5%
- Reduction in the Quebec family allowance: 4%
= 55%. These phase-outs pile up and create the famous “bumps” that make RRSP optimization particularly profitable around $50-90K for families.
Five strategies to reduce your METR
1. Contribute to an RRSP
Reduces taxable income, thus “shifting” your position on the curve. Double Gain: Less tax + clawback of credits like the CCB and Family Allowance.
2. Contribute to a TFSA
Income is not reduced, but yield and withdrawals do not count in the calculation of the future METR. Ideal if you are in a “bump”.
3. Income splitting
Retired couples: splitting of the pension between spouses. Family in business: salary to spouse at a reasonable rate.
4. Spread out exceptional income
Sale of a property, RRSP withdrawal, retirement bonus: if possible, spread it over several years so as not to hit the “bump” all at once.
5. Plan for RRIF withdrawals
After age 71, the minimum withdrawal from the RRIF increases with age. Combined with the OAS and QPP, you quickly enter the PSV recovery zone (15%).
Frequently asked questions
What is the difference between the three rates displayed?
Why can my METR reach 60-70% at certain levels?
“True tax rate” — is that an official term?
What is included in the calculation?
What is NOT included (compute limits)?
How can I check that the calculation is correct?
How do I reduce my METR?
Can this calculator replace a tax specialist?
Other Assur360 simulators
Beyond the numbers, there’s your real life.
Our broker can help you turn these calculations into concrete decisions: life insurance, disability, mortgage, emergency fund. Free consultation with no obligation.
function fmtPct(n) { return (n*100).toFixed(1) + ‘%’; } function fmtMoney(n) { return new Intl.NumberFormat(‘fr-CA’, {style:’currency’, currency:’CAD’, maximumFractionDigits:0}).format(n); } function fmt(n) { return new Intl.NumberFormat(‘fr-CA’).format(n); }
// Impôt progressif total à un revenu donné function progressiveTax(income, brackets) { let tax = 0, lower = 0; for (const b of brackets) { const upper = b.to === null ? Infinity : b.to; if (income <= lower) break; const taxable = Math.min(income, upper) – lower; tax += taxable * b.rate; if (income <= upper) break; lower = upper; } return tax; }
function bracketRate(income, brackets) { let lower = 0; for (const b of brackets) { const upper = b.to === null ? Infinity : b.to; if (income <= upper) return b.rate; lower = upper; } return brackets[brackets.length-1].rate; }
function rrqContribution(income, p) { const ex = p.rrq.exemption; if (income <= ex) return 0; const tier1 = Math.max(0, Math.min(income, p.rrq.mga_1) – ex); let cot = tier1 * (p.rrq.rate_base + p.rrq.rate_supp_1); if (income > p.rrq.mga_1) { const tier2 = Math.min(income, p.rrq.mga_2) – p.rrq.mga_1; cot += tier2 * p.rrq.rate_supp_2; } return cot; }
function aeContribution(income, p) { return Math.min(income, p.ae.max_insurable) * p.ae.rate_qc; }
function rqapContribution(income, p) { return Math.min(income, p.rqap.max_insurable) * p.rqap.rate_employee; }
// Marginal rates par composante
function rrqMarg(income, p) {
if (income <= p.rrq.exemption) return 0;
if (income <= p.rrq.mga_1) return p.rrq.rate_base + p.rrq.rate_supp_1;
if (income <= p.rrq.mga_2) return p.rrq.rate_supp_2;
return 0;
}
function aeMarg(income, p) { return income <p.ae.max_insurable ? p.ae.rate_qc : 0; } function rqapMarg(income, p) { return income <p.rqap.max_insurable ? p.rqap.rate_employee : 0; } function aceMarg(income, kids, p) { if (kids <= 0) return 0; const k = Math.min(kids, 4); if (income <p.ace.low_threshold) return 0; if (income <p.ace.high_threshold) return p.ace.low_rate[k] || 0; return p.ace.high_rate[k] || 0; } function alloFamilleMarg(income, kids, family, p) { if (kids <= 0) return 0; const seuil = family === ‘monoparental’ ? p.allocation_famille_qc.phaseout_single : p.allocation_famille_qc.phaseout_couple; return income
// ===== TEMI : marginal effectif (somme de toutes les charges sur prochain dollar) ===== function temiAt(income, profile, p) { const fedRaw = bracketRate(income, p.fed_brackets); const fed = fedRaw * (1 – p.fed_qc_abatement); const qc = bracketRate(income, p.qc_brackets); const stat = fed + qc; // taux marginal STATUTAIRE const rrq = rrqMarg(income, p); const ae = aeMarg(income, p); const rqap = rqapMarg(income, p); const tps = tpsMarg(income, p); const sol = solidariteMarg(income, p); const ace = aceMarg(income, profile.kids, p); const allo = alloFamilleMarg(income, profile.kids, profile.family, p); const psv = psvMarg(income, profile.age, p); const total = stat + rrq + ae + rqap + tps + sol + ace + allo + psv; return { total, stat, breakdown: { ‘Impôt fédéral (après abattement Qc)’: fed, ‘Impôt Québec’: qc, ‘RRQ (de base + suppl.)’: rrq, ‘Assurance-emploi (AE)’: ae, ‘RQAP’: rqap, ‘Perte crédit TPS’: tps, ‘Perte crédit solidarité’: sol, ‘Récupération ACE’: ace, ‘Réduction Allocation famille Qc’: allo, ‘Récupération PSV (65+)’: psv, } }; }
// ===== Taux effectif moyen : impôts + cotisations / revenu ===== function averageRate(income, profile, p) { if (income <= 0) return 0; // Federal tax const fedTax = progressiveTax(income, p.fed_brackets); const fedCredit = p.basic_personal_amount.federal * p.fed_brackets[0].rate; const fedNet = Math.max(0, fedTax – fedCredit) * (1 – p.fed_qc_abatement); // Quebec tax const qcTax = progressiveTax(income, p.qc_brackets); const qcCredit = p.basic_personal_amount.quebec * p.qc_brackets[0].rate; const qcNet = Math.max(0, qcTax – qcCredit); // Cotisations const rrq = rrqContribution(income, p); const ae = aeContribution(income, p); const rqap = rqapContribution(income, p); const totalNet = fedNet + qcNet + rrq + ae + rqap; return totalNet / income; }
function buildCurve(profile, p) { const points = []; for (let inc = 5000; inc <= 200000; inc += 1000) { points.push({x: inc, y: temiAt(inc, profile, p).total}); } return points; }
function renderChart(curve, currentIncome, p) {
const W = 720, H = 280, padL = 50, padR = 12, padT = 16, padB = 30;
const xmin = 5000, xmax = 200000, ymin = 0, ymax = 0.85;
const xS = x => padL + (x – xmin) / (xmax – xmin) * (W – padL – padR);
const yS = y => H – padB – (y – ymin) / (ymax – ymin) * (H – padT – padB);
let path = “M ” + xS(curve[0].x) + ” ” + yS(curve[0].y);
for (let i = 1; i < curve.length; i++) path += ” L ” + xS(curve[i].x) + ” ” + yS(curve[i].y);
let xticks = ”, yticks = ”;
for (let v = 25000; v <= xmax; v += 25000) {
xticks += `
function renderBreakdown(comp) { const colors = { ‘Impôt fédéral (après abattement Qc)’: ‘#0d1f2d’, ‘Impôt Québec’: ‘#10c4c7’, ‘RRQ (de base + suppl.)’: ‘#f59e0b’, ‘Assurance-emploi (AE)’: ‘#a78bfa’, ‘RQAP’: ‘#ec4899’, ‘Perte crédit TPS’: ‘#84cc16’, ‘Perte crédit solidarité’: ‘#06b6d4’, ‘Récupération ACE’: ‘#ef4444’, ‘Réduction Allocation famille Qc’: ‘#f97316’, ‘Récupération PSV (65+)’: ‘#7c3aed’, }; const items = Object.entries(comp).filter(([_, v]) => v > 0.0005); const total = items.reduce((s, [_, v]) => s + v, 0); if (total === 0) return ‘
Aucune charge marginale détectée à ce niveau de revenu.
‘; let bar = ”, rows = ”; items.sort((a, b) => b[1] – a[1]); for (const [k, v] of items) { const pct = (v / total) * 100; bar += `
`; rows += `
`; } return `
`; }
function update() { if (!PARAMS) return; const income = parseInt(document.getElementById(‘temi-income’).value) || 0; const family = document.getElementById(‘temi-family’).value; const kids = parseInt(document.getElementById(‘temi-kids’).value) || 0; const age = parseInt(document.getElementById(‘temi-age’).value) || 35; chart.profile = {family, kids, age}; chart.currentIncome = income; document.getElementById(‘temi-income-out’).textContent = fmtMoney(income); const t = temiAt(income, chart.profile, PARAMS); const avgRate = averageRate(income, chart.profile, PARAMS); document.getElementById(‘temi-stat-pct’).textContent = fmtPct(t.stat); document.getElementById(‘temi-temi-pct’).textContent = fmtPct(t.total); document.getElementById(‘temi-avg-pct’).textContent = fmtPct(avgRate); const lose = Math.round(t.total * 1000); document.getElementById(‘temi-keep-1k’).textContent = fmtMoney(1000 – lose); document.getElementById(‘temi-lose-1k’).textContent = fmtMoney(lose); document.getElementById(‘temi-breakdown’).innerHTML = renderBreakdown(t.breakdown); document.getElementById(‘temi-chart’).innerHTML = renderChart(buildCurve(chart.profile, PARAMS), income, PARAMS); }
function init() { fetch(PARAMS_URL).then(r => r.json()).then(p => { PARAMS = p; document.getElementById(‘temi-meta’).textContent = `Paramètres ${p.year} · mis à jour le ${p.last_updated}`; [‘temi-income’, ‘temi-family’, ‘temi-kids’, ‘temi-age’].forEach(id => { const el = document.getElementById(id); if (el) { el.addEventListener(‘input’, update); el.addEventListener(‘change’, update); } }); update(); }).catch(() => { document.getElementById(‘temi-meta’).textContent = ‘Erreur de chargement des paramètres fiscaux.’; }); } if (document.readyState === ‘loading’) document.addEventListener(‘DOMContentLoaded’, init); else init(); })();