How the Hong Kong salaries tax calculator is verified

Inputs

InputFieldType
Gross annual salary (HKD)grossAnnualnumber
Allowancehouseholdstring
Children you claim forchildrennumber

The formula

Each derived value below is computed by the Valem engine from the expression shown, in this order. Nothing else runs — there is no hidden code path.

Derived valueFieldFormula (JSONata)
Employee MPF (5%, capped)mpfEmployee($m := $n(grossAnnual) / 12; $round(($m < $const.mpfMinRelevantMonthly ? 0 : ($m < $const.mpfMaxRelevantMonthly ? $m : $const.mpfMaxRelevantMonthly) * $const.mpfRate) * 12, 2))
Mpf DeductionmpfDeductionmpfEmployee < $const.mpfDeductionCap ? mpfEmployee : $const.mpfDeductionCap
Net total income (after the MPF deduction)incomeAfterDeductions($v := $n(grossAnnual) - mpfDeduction; $v < 0 ? 0 : $v)
Allowancesallowances(household = 'marriedSoleEarner' ? $const.marriedAllowance : $const.basicAllowance) + $floor($n(children)) * $const.childAllowance
Net chargeable incomechargeableIncome($v := incomeAfterDeductions - allowances; $v < 0 ? 0 : $v)
Tax at progressive ratestaxProgressive$round($bandedCharge(chargeableIncome, [{"upTo": $const.bandWidth, "rate": $const.rateBand1}, {"upTo": $const.bandWidth * 2, "rate": $const.rateBand2}, {"upTo": $const.bandWidth * 3, "rate": $const.rateBand3}, {"upTo": $const.bandWidth * 4, "rate": $const.rateBand4}, {"upTo": null, "rate": $const.rateTop}]), 2)
Tax at standard ratetaxStandard$round($bandedCharge(incomeAfterDeductions, [{"upTo": $const.standardTierThreshold, "rate": $const.standardRate}, {"upTo": null, "rate": $const.standardRateUpper}]), 2)
Salaries tax (the lower)salariesTaxtaxProgressive < taxStandard ? taxProgressive : taxStandard
Net / monthnetMonthly$round(($n(grossAnnual) - mpfEmployee - salariesTax) / 12, 2)
Net / year (after MPF & salaries tax)netAnnual$round($n(grossAnnual) - mpfEmployee - salariesTax, 2)

Rates and thresholds

The expressions above read these as $const. They are the figures the annual refresh replaces, so they live as data rather than being written into the formulas — which is what makes a year-on-year change a one-line diff instead of an edit to arithmetic.

ConstantValue
basicAllowance145000
marriedAllowance290000
childAllowance140000
bandWidth50000
rateBand10.02
rateBand20.06
rateBand30.1
rateBand40.14
rateTop0.17
standardRate0.15
standardRateUpper0.16
standardTierThreshold5000000
mpfRate0.05
mpfMinRelevantMonthly7100
mpfMaxRelevantMonthly30000
mpfDeductionCap18000

Test vectors (6)

Every case is executed against this model on each build, by the Java engine and independently by a JavaScript one. A mismatch of a single cent fails the build, so the page cannot ship advertising a figure the model no longer produces.

Case 1 — H1 IRD pam39e Q2: $480,000 with the basic allowance — $35,890 at progressive rates

given grossAnnual480000
given householdsingle
given children0
expect mpfEmployee18000
expect incomeAfterDeductions462000
expect chargeableIncome317000
expect taxProgressive35890
expect taxStandard69300
expect salariesTax35890
expect netAnnual426110

Case 2 — H2 IRD pam39e Q5: same income, married person’s allowance — $12,080

given grossAnnual480000
given householdmarriedSoleEarner
given children0
expect chargeableIncome172000
expect salariesTax12080

Case 3 — H3 one child allowance

given grossAnnual480000
given householdsingle
given children1
expect chargeableIncome177000
expect salariesTax12780

Case 4 — H4 $20,000 a month: MPF $1,000 a month, tax $2,980

given grossAnnual240000
given householdsingle
given children0
expect mpfEmployee12000
expect chargeableIncome83000
expect salariesTax2980
expect netAnnual225020

Case 5 — H5 below the $7,100 minimum relevant income: no employee MPF and no tax

given grossAnnual84000
given householdsingle
given children0
expect mpfEmployee0
expect salariesTax0
expect netAnnual84000

Case 6 — H6 $6m: the two-tiered standard rate is lower than progressive

given grossAnnual6000000
given householdsingle
given children0
expect taxProgressive974290
expect taxStandard907120
expect salariesTax907120
expect netAnnual5074880

Sources

Check it yourself — and reuse it

The whole model as JSON — inputs, outputs, formulas, vectors and sources in one re-runnable document.

This model is published under CC BY 4.0: use it, adapt it, build on it, with attribution. The licence covers the model — the expression of each rule and the arrangement of the catalog. The rates and thresholds are law, and nobody licenses those; the sources above are where they come from.

← Back to the Hong Kong salaries tax calculator

Built with Valem — a live, verifiable calculator you can fork and embed.