How the Hong Kong sole proprietor calculator is verified

Inputs

InputFieldType
Annual business profit before your own MPF (HKD)profitsAnnualnumber
Allowance under personal assessmenthouseholdstring
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)
Your MPF (5%, capped)mpfContribution($p := $n(profitsAnnual); $round($p < $const.mpfMinRelevantAnnual ? 0 : ($p < $const.mpfMaxRelevantAnnual ? $p : $const.mpfMaxRelevantAnnual) * $const.mpfRate, 2))
Assessable profits (after the MPF deduction)assessableProfits($v := $n(profitsAnnual) - (mpfContribution < $const.mpfDeductionCap ? mpfContribution : $const.mpfDeductionCap); $v < 0 ? 0 : $v)
Profits tax (7.5% / 15%)profitsTax$round($bandedCharge(assessableProfits, [{"upTo": $const.profitsTaxTierThreshold, "rate": $const.profitsTaxLowerRate}, {"upTo": null, "rate": $const.profitsTaxRate}]), 2)
Allowances (personal assessment)allowances(household = 'marriedSoleEarner' ? $const.marriedAllowance : $const.basicAllowance) + $floor($n(children)) * $const.childAllowance
Chargeable under personal assessmentpaChargeable($v := assessableProfits - allowances; $v < 0 ? 0 : $v)
Tax under personal assessmentpersonalAssessmentTax$round($bandedCharge(paChargeable, [{"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 payable (the lower)taxPayablepersonalAssessmentTax < profitsTax ? personalAssessmentTax : profitsTax
Net income / yearnetIncome$round($n(profitsAnnual) - mpfContribution - taxPayable, 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
profitsTaxLowerRate0.075
profitsTaxRate0.15
profitsTaxTierThreshold2000000
basicAllowance145000
marriedAllowance290000
childAllowance140000
bandWidth50000
rateBand10.02
rateBand20.06
rateBand30.1
rateBand40.14
rateTop0.17
mpfRate0.05
mpfMinRelevantAnnual85200
mpfMaxRelevantAnnual360000
mpfDeductionCap18000

Test vectors (5)

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 — P1 $480,000 single: profits tax $34,650 beats personal assessment $35,890

given profitsAnnual480000
given householdsingle
given children0
expect mpfContribution18000
expect assessableProfits462000
expect profitsTax34650
expect paChargeable317000
expect personalAssessmentTax35890
expect taxPayable34650
expect netIncome427350

Case 2 — P2 $300,000 single: personal assessment $8,000 beats profits tax $21,375

given profitsAnnual300000
given householdsingle
given children0
expect mpfContribution15000
expect profitsTax21375
expect personalAssessmentTax8000
expect taxPayable8000
expect netIncome277000

Case 3 — P3 $480,000 with the married person’s allowance: personal assessment $12,080

given profitsAnnual480000
given householdmarriedSoleEarner
given children0
expect paChargeable172000
expect taxPayable12080
expect netIncome449920

Case 4 — P4 below the $85,200 minimum relevant income: no MPF and no tax

given profitsAnnual80000
given householdsingle
given children0
expect mpfContribution0
expect taxPayable0
expect netIncome80000

Case 5 — P5 $3m: the 15% upper tier applies above $2m of assessable profits

given profitsAnnual3000000
given householdsingle
given children0
expect assessableProfits2982000
expect profitsTax297300
expect personalAssessmentTax464290
expect taxPayable297300
expect netIncome2684700

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 sole proprietor calculator

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