學習目標 · Learning Objectives
- Build a client profile that exposes liabilities, dependants and time horizon, then compute a protection gap (保障缺口) and a retirement gap (退休缺口) from that profile — separating income replacement, debt clearance and final expenses.
- Distinguish need from budget honestly, and negotiate the target when the computed need exceeds the affordable amount, instead of silently shrinking the sum assured to fit the premium the client can pay.
- Produce a defensible recommendation: a staged ladder with recorded assumptions, residual gaps and a written justification that would survive client pushback and a compliance file review.
Need Analysis (需求分析)
Need analysis is the part of the sales conversation where you stop selling and start arithmetic.
Every other stage of the point-of-sale system — the catalogue, the quoting engine, the underwriting referral, the comparison sheet — is mechanical. This stage is not. This is the only stage where you form an opinion about a client's future, defend it in numbers, and get the client to act on it. If you get this stage wrong, every downstream stage produces a correctly-calculated answer to the wrong question.
The discipline this lesson teaches is simple to state and hard to practise: establish the need from the client's own facts, separate it from what they can afford, and write down the reasoning so it survives both a hostile client and a compliance reviewer.
Learning Objectives
- Build a client profile that exposes liabilities, dependants and time horizon, then compute a protection gap (保障缺口) and a retirement gap (退休缺口) from that profile — separating income replacement, debt clearance and final expenses.
- Distinguish need from budget honestly, and negotiate the target when the computed need exceeds the affordable amount, instead of silently shrinking the sum assured to fit the premium the client can pay.
- Produce a defensible recommendation: a staged ladder with recorded assumptions, residual gaps and a written justification that would survive client pushback and a compliance file review.
Client Profiling
In practice: you spend the first 40 minutes of a fact-find writing down who depends on whose income, what they owe, and when that changes — before you open the product catalogue at all.
Profiling is not personality typing. It is building a structured picture of obligations over time, because obligations are what generate need. The question is never "what does this client want to buy". The question is "what will this client owe, to whom, in which years, and what happens to the dependants if that income stops".
For a Hong Kong household you are collecting at least seven categories of fact. Miss one and the gap you compute will be wrong in a way the client cannot easily see.
| Dimension | What you collect | Why it drives the number |
|---|---|---|
| Income | Gross and net, by person; bonus and commission history | Sets the replacement target and the affordability ceiling |
| Expenses | Living costs with mortgage service separated out | Prevents the double-count in the gap formula |
| Liabilities | Mortgage balance, car loan, credit-card balance, personal loans, guarantee obligations | Becomes the debt-clearance block of the need |
| Dependants | Spouse, children and their ages, parents being supported | Sets the income-replacement period |
| Existing cover | Group life from employer, MPF death benefit, individual policies, savings with death benefit | Subtracts from the need; double-counting here is a common error |
| Assets | Cash, savings, investment property, equity in the home, MPF balances | Subtracts from the need |
| Time horizon | Retirement age, education milestones, loan maturity dates | Decides how many years of replacement to buy |
The two rows that catch most agents out are expenses and existing cover.
On expenses, the critical point is that mortgage service must be identifiable as a separate figure. In the Chan family worked example below, living expenses are HK$1,152,000 and the mortgage payment is HK$494,400. If you treat HK$1,646,400 as "the living cost" and also add the HK$6,180,000 mortgage outstanding as a lump sum, you inflate the need by 40%. That single error produces a recommendation the client will fail on affordability, and it is the fastest way to lose credibility in the second meeting.
On existing cover, you have to understand that group life is not free money you can ignore, and it is not the same as individual cover. An employer group scheme typically pays on death only, often for a multiple of salary capped around 12 times, and it usually vanishes the day the client resigns. MPF provides a death benefit equal to the greater of the account balance or the minimum relevant balance. A savings policy with a death benefit is a partial substitute for term life, not a dollar-for-dollar one, because it usually carries surrender charges and, if it is an ILAS, market risk. The honest treatment is to note each one, state whether it is guaranteed and when it stops, and only then deduct it.
Here is the profile object your system should be capturing.
Note that it separates mortgageService from livingExpenses deliberately, because the
gap formula depends on that separation.
export interface ClientProfile {
clientId: string;
asOf: string; // ISO date the facts were taken
advisorId: string;
advisorBranch: string;
household: HouseholdMember[];
mortgage: {
outstandingBalance: number; // HK$, not the original sum
monthlyPayment: number;
monthlyPrincipal: number; // needed to know when it clears
monthlyInterest: number;
maturityDate: string; // when the balance reaches zero unaided
provider: string;
};
otherLiabilities: Liability[];
assets: Asset[];
existingCover: ExistingCover[];
dependants: Dependant[];
objectives: Objective[]; // each with its own target date + amount
assumptions: Record<string, number>; // inflation, discount rate, replacement ratio
}
export interface HouseholdMember {
name: string;
dateOfBirth: string;
maritalStatus: 'single' | 'married' | 'divorced' | 'widowed';
occupation: string;
occupationClass: 1 | 2 | 3 | 4; // drives premium, see Lesson 03
annualGrossIncome: number;
annualNetIncome: number;
bonusHistory5Y: number[]; // volatility matters for affordability
isMainEarner: boolean;
}
export interface Liability {
type: 'car' | 'credit-card' | 'personal-loan' | 'guarantee' | 'other';
outstandingBalance: number;
monthlyPayment: number;
joint: boolean;
guarantorOf?: string[]; // a guarantee is a contingent liability
}
export interface Asset {
type: 'cash' | 'deposit' | 'fund' | 'property' | 'mpf' | 'ilp-value';
description: string;
currentValue: number;
isLiquid: boolean; // can it be realised inside 30 days?
isCommitted: boolean; // ring-fenced for a known goal?
encumbrance?: number; // e.g. not yet paid off
}
export interface ExistingCover {
type: 'group-life' | 'individual-term' | 'mpf-death-benefit' | 'ilp' | 'health';
provider: string;
sumAssured: number;
guaranteed: boolean;
endsOn?: string; // resignation, age, policy term
paymentTermYears?: number;
notes: string;
}
Because the profile is structured, it can be queried. The advisor should never be re-typing a fact-find into a proposal document by hand.
-- The need calculation is derived, never hand-keyed.
-- Every input row keeps the profile id, the as-of date and the advisor who asserted it,
-- so a compliance reviewer can reconstruct the recommendation years later.
CREATE TABLE need_assessment (
id INTEGER PRIMARY KEY,
profile_id INTEGER NOT NULL REFERENCES client_profile(id),
assessed_at TEXT NOT NULL, -- ISO-8601
advisor_id INTEGER NOT NULL REFERENCES advisor(id),
adviser_ref TEXT, -- the FNA report serial
profile_hash TEXT NOT NULL, -- sha256 of the JSON snapshot
superseded_by INTEGER REFERENCES need_assessment(id),
is_current INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE need_block (
assessment_id INTEGER NOT NULL REFERENCES need_assessment(id),
block_code TEXT NOT NULL, -- B1 B2 B3 B4 ...
label TEXT NOT NULL,
subject_member TEXT NOT NULL, -- who the block belongs to
amount INTEGER NOT NULL, -- integer HK$, never REAL
years INTEGER, -- replacement horizon
discount_rate REAL, -- real or nominal, must be flagged
formula TEXT NOT NULL, -- the literal expression used
assumption_ids TEXT, -- JSON array -> assumption table
PRIMARY KEY (assessment_id, block_code, subject_member)
);
CREATE INDEX idx_need_profile ON need_block(assessment_id);
-- A recommendation that closes more gap than the affordable ceiling allows,
-- or that has no residual gap recorded, fails this query.
CREATE VIEW recommendation_integrity AS
SELECT
a.id AS assessment_id,
a.adviser_ref,
SUM(CASE WHEN b.block_code = 'B1' THEN b.amount ELSE 0 END) AS income_replacement,
SUM(CASE WHEN b.block_code = 'B2' THEN b.amount ELSE 0 END) AS debt_clearance,
SUM(CASE WHEN b.block_code = 'B3' THEN b.amount ELSE 0 END) AS final_expenses,
r.premium_annual AS proposed_premium,
r.affordable_ceiling_annual AS budget_ceiling,
r.residual_gap AS residual_gap,
CASE
WHEN r.premium_annual > r.affordable_ceiling_annual
THEN 'EXCEEDS_AFFORDABILITY'
WHEN r.residual_gap = 0 AND r.staged = 0
THEN 'SUSPICIOUS_ZERO_GAP'
ELSE 'OK'
END AS flag
FROM need_assessment a
JOIN need_block b ON b.assessment_id = a.id
JOIN recommendation r ON r.assessment_id = a.id
WHERE a.is_current = 1
GROUP BY a.id, a.adviser_ref, r.premium_annual,
r.affordable_ceiling_annual, r.residual_gap, r.staged;
Two design decisions in that schema matter more than they look.
The first is that money is stored as INTEGER, not REAL.
Floating point loses cents at HK$13,771,556.556, and a recommendation that is wrong by
seven figures because of accumulated rounding is not defensible.
Compute in decimal, round once at the display boundary, store the integer.
The second is the formula column.
A reviewer does not want to trust your output; they want to re-derive it.
Storing the literal expression that produced each block lets a compliance officer
recompute it and compare.
That single column is the difference between "the agent calculated this" and "the
calculation is auditable".
Budget vs Need
In practice: you compute the need, you compute what the client can genuinely afford this month, and when those two numbers do not agree you say so out loud and negotiate — you never quietly cut the sum assured until the premium feels comfortable.
The distinction is the single most important habit in this lesson.
Need is what would leave the family whole if the breadwinner died tomorrow, plus enough to fund retirement as planned. It is derived from obligations. Budget is what the client can pay without damaging their current standard of living, their debt servicing, or their children's education funding. It is derived from cash flow.
Need is a function of the client's obligations. Budget is a function of the client's discretionary surplus. They are different numbers and they frequently disagree.
The Chan household makes this vivid. Their facts:
| Item | Annual | Monthly | Note |
|---|---|---|---|
| Chan's net income | 948,000 | 79,000 | Main earner, age 42 |
| Li's net income | 480,000 | 40,000 | Part-time, age 40 |
| MPF contributions (both) | 120,000 | 10,000 | Both fully contributing |
| Total income | 1,548,000 | 129,000 | |
| Living expenses (ex-mortgage) | 1,152,000 | 96,000 | Housing, food, transport, insurance |
| Mortgage service | 494,400 | 41,200 | Principal + interest |
| Total outflow | 1,646,400 | 137,200 | |
| Net position | −98,400 | −8,200 | The household is already in deficit |
This household is running HK$8,200 a month in the red before paying a single premium. That is the most important fact in the entire analysis, and it must be stated before any product is discussed.
The honest budget conclusion is that this client's affordable new-premium budget today is approximately zero, and the first recommendation is not an insurance policy — it is a plan to create surplus.
An experienced agent says this plainly: "Before I talk to you about protection, you are HK$98,400 a year short. We fix that first, because insurance that lapses in year two is worse than no insurance."
That reordering is what makes a recommendation defensible. A policy funded out of a deficit is a policy that will be surrendered.
Once there is surplus, here is how much is realistically available. The ceiling is not a rule; it is a judgement, and you must record which judgement you made and why.
| Saving rate on gross income | Annual | Monthly | Sustainable retirement income at 60 | As % of current living costs |
|---|---|---|---|---|
| 0% (status quo) | 0 | 0 | 228,784 | 20% |
| 10% | 154,800 | 12,900 | 458,893 | 40% |
| 15% | 232,200 | 19,350 | 573,948 | 50% |
| 20% | 309,600 | 25,800 | 689,003 | 60% |
| 25% | 387,000 | 32,250 | 804,057 | 70% |
| 30% | 464,400 | 38,700 | 919,112 | 80% |
| 40% | 619,200 | 51,600 | 1,149,222 | 100% |
Read that last column carefully. The client's stated goal is to replace 70% of current living costs in retirement. Achieving that requires saving 25% of gross income, sustained for eighteen years, which is a very demanding plan. At a more commonly achieved 15%, the household arrives at 50% replacement — which is still a defensible Hong Kong retirement outcome, and an honest one.
The technique is called target negotiation: you do not reduce the number until it fits, you go back to the client and renegotiate which target you are solving for.
// Separate the two questions: what is needed, and what is payable.
// Never let the second silently overwrite the first.
export interface AffordabilityInput {
grossIncomeAnnual: number;
netIncomeAnnual: number;
committedOutgoingsAnnual: number; // rent/mortgage, school fees, existing premiums
discretionaryOutgoingsAnnual: number;
existingPremiumAnnual: number; // all policies across all carriers
targetRetirementAge: number;
clientAge: number;
hasOutstandingDebt: boolean;
}
export interface AffordabilityVerdict {
netPositionAnnual: number;
netPositionMonthly: number;
affordablePremiumAnnual: number;
ceilingBasis: string;
warnings: string[];
recommendation: 'fix-cashflow-first' | 'protect-only' | 'protect-and-fund';
}
/**
* The ceiling is deliberately conservative.
* An agent who sets the ceiling at the theoretical maximum and then fills it to
* the brim produces clients who lapse.
*/
export function assessAffordability(i: AffordabilityInput): AffordabilityVerdict {
const netPositionAnnual =
i.netIncomeAnnual -
i.committedOutgoingsAnnual -
i.discretionaryOutgoingsAnnual -
i.existingPremiumAnnual;
const warnings: string[] = [];
// A household already in deficit cannot fund new premium. Say so.
if (netPositionAnnual <= 0) {
warnings.push(
'Household runs a deficit before any new premium. ' +
'New protection premium must be near zero until cashflow is repaired.'
);
return {
netPositionAnnual,
netPositionMonthly: netPositionAnnual / 12,
affordablePremiumAnnual: 0,
ceilingBasis: 'deficit-household',
warnings,
recommendation: 'fix-cashflow-first',
};
}
// Protect the retirement first, then let the balance carry protection.
// Contribution ages are clientAge+1 .. targetRetirementAge-1.
const contributionYears = Math.max(0, i.targetRetirementAge - i.clientAge - 1);
const retirementSavingsPct =
contributionYears >= 15 ? 0.25 : contributionYears >= 10 ? 0.20 : 0.15;
const retirementSavings = i.grossIncomeAnnual * retirementSavingsPct;
let affordable = netPositionAnnual - retirementSavings;
if (affordable < 0) {
warnings.push('Retirement saving target alone exceeds surplus. Reduce the target.');
affordable = 0;
}
// Never recommend protection premium above 15% of gross income in year one.
// Above that, lapse risk rises sharply.
const hardCap = i.grossIncomeAnnual * 0.15;
if (affordable > hardCap) {
warnings.push(
`Affordable (${Math.round(affordable)}) exceeds 15% of income (${Math.round(hardCap)}). ` +
'Capping at 15% to protect against early lapse.'
);
affordable = hardCap;
}
if (i.hasOutstandingDebt) {
warnings.push(
'Outstanding debt present: protect debt clearance before retirement funding.'
);
}
return {
netPositionAnnual: netPositionAnnual,
netPositionMonthly: netPositionAnnual / 12,
affordablePremiumAnnual: affordable,
ceilingBasis:
`surplus minus ${(retirementSavingsPct * 100).toFixed(0)}% retirement saving ` +
`(contribution window ${contributionYears}y), capped at 15% of income`,
warnings,
recommendation: affordable === 0 ? 'protect-only' : 'protect-and-fund',
};
}
That last block is illustrative of a real problem you will hit in TypeScript: extending an interface from outside its file is legal but hostile to review. Keep helpers in the same module or export a proper function.
Now the deeper point about budget. Budget is not only about this year's cash. Two adjustments experienced agents make:
- Non-sunk-cost premium loading. Year-one budget should assume the premium escalates if the client is under 40 and has chosen a 20-pay term on a policy they will still own at 62.
- Bonus and commission income is not baseline income. For commission earners, use the three-year average, not the best year. Using a bonus year to justify a premium is how policies are surrendered in the first downturn.
The mortgage timing question is the other subtlety. Ask the bank for the amortisation schedule. Knowing the balance at retirement rather than today can change the entire structure of the recommendation, because debt clearance is a one-off block that disappears once the mortgage clears.
The Protection Gap Formula
In practice: you turn the profile into a small number of labelled blocks, each with its own formula, so the client can see that the number is assembled rather than asserted.
The standard structure is DIME: Death, Disability, Medical, Education. For a Hong Kong protection analysis the Death block is what matters most, and it decomposes into three sub-blocks that are frequently confused with each other.
- B1 — Income replacement. A stream of future living costs that survives the breadwinner, netted against the surviving spouse's income, over the replacement horizon.
- B2 — Debt clearance. The mortgage and loan balances that must be repaid at death, so the property passes to the family free of encumbrance.
- B3 — Final expenses. Funeral, probate, last medical bills, the gap between the date of death and the payment of the death benefit.
Plus two blocks that many agents forget entirely:
- B4 — Education funding. The amount the children will need, which is a separate claim on capital and is not covered by income replacement if you set the replacement horizon to the youngest child's independence.
- B5 — Emergency buffer. Several months of expenses held in cash, which is a liquidity need, not an insurance need.
B1 is the one that needs the most care.
The replacement horizon is the number of years the family needs income.
The correct horizon is not "until the children are independent" as a blanket rule.
It is min(age at which the youngest child is independent, age at which retirement income begins, age at which life insurance becomes uneconomic).
For the Chan family: Chan is 42, Hoi-tin is 10, Yu-yan is 6. Yu-tin's independence at 22 is year 2038, which is 12 years away. But Chan retires at 60 in 2044, which is 18 years away. Insurance that runs to retirement age is priced on mortality, and a policy that renews to age 75 or 85 for a client with a clean health profile can cost more than the mortality math suggests if you use the wrong pricing basis.
The standard, defensible HK practice is:
- Buy the debt and dependency layer as decreasing term to the date the mortgage clears and the youngest child is independent — call it 15 years here.
- Buy the retirement layer separately as a savings or annuity product, because it is a funding problem, not an insurance problem.
That split is what produces an affordable answer instead of a HK$4 million term policy that must be renewed to age 85.
Here is the full worked example.
Step 1 — income replacement (B1).
Annual living costs, mortgage service excluded: HK$1,152,000 Less Li's continuing income: −HK$480,000 Annual shortfall to fund: HK$672,000
Discounted over 15 years at a real rate of 4%:
AF(15, 0.04) = (1 − 1.04⁻¹⁵) / 0.04 = 11.1184
B1 = 672,000 × 11.1184 = HK$7,471,556
Why 15 years and not 18? Because 2044 is retirement, and we handle retirement separately. Why a real 4% and not a nominal 7%? Because the HK$672,000 is a today's dollars figure extrapolated into the future. Mixing a nominal discount rate with a real expense stream is the second most common arithmetic error in need analysis, after the mortgage double-count. If you want to work in nominal terms, escalate expenses at your inflation assumption first — 3% — and then use a nominal rate of about 7%. Both routes give a similar answer; mixing them does not.
Step 2 — debt clearance (B2).
Mortgage outstanding balance: HK$6,180,000 Car loan outstanding: HK$0 Credit card balance (paid in full monthly by agreement): HK$0 B2 = HK$6,180,000
Note that B2 is a lump sum, not an annuity. A term life policy pays a single sum, so a single obligation maps cleanly. If the client had a guaranteed obligation — for example, they guarantee a sibling's mortgage — that balance belongs in B2 too, and you must flag it as contingent.
Step 3 — final expenses (B3).
Funeral and last rites, probate, estate administration, outstanding final medical bills:
B3 = HK$120,000
For Hong Kong, HK$100,000 to HK$150,000 is the normal band. Take the client's actual preference from their religious custom — a Christian funeral and an ancestral Chinese rite differ substantially, and the difference is a real cost the family will incur.
Total need and the gap.
| Block | Component | Amount |
|---|---|---|
| B1 | Income replacement, 15 years at 4% real | 7,471,556 |
| B2 | Mortgage balance cleared | 6,180,000 |
| B3 | Final expenses | 120,000 |
| Total need | 13,771,556 | |
| Less | Savings and deposits | (1,850,000) |
| Less | MPF accrued benefits (death benefit) | (180,000) |
| Less | Equity released from home (not counted — see below) | 0 |
| Total resources | (2,030,000) | |
| Protection gap | 11,741,556 | |
| Less | Existing group life cover | (250,000) |
| Incremental gap | 11,491,556 |
Notice I did not count the equity in the home. The mortgage is secured on it and the outstanding balance is already in B2. If you deduct the equity and add the mortgage to the need, you are double-counting the property from both sides. The equity only becomes a resource when the property is actually sold, and for a family that intends to stay in the home, it is not accessible capital. This is a very common inflation of the apparent need.
The double-count trap, shown explicitly.
Here is the mistake in full, because you need to be able to spot it in your own notes and in a competitor's proposal.
WRONG (mortgage counted twice):
(1,152,000 + 494,400 − 480,000) × 11.1184 + 6,180,000 + 120,000
= 12,968,487 + 6,300,000
= 19,268,487 ← 40% too high
RIGHT (mortgage service stripped out of the stream, balance added once):
(1,152,000 − 480,000) × 11.1184 + 6,180,000 + 120,000
= 7,471,556 + 6,300,000
= 13,771,556 ← correct
The error inflates the gap by HK$5,496,931. At HK$3,200 per HK$1,000,000 of level term, that phantom HK$5.5 million "need" buys HK$2,007 a month of premium for cover the family does not need. The client pays that for years, it reduces the retirement contribution, and the retirement projection — which is correct — then fails anyway.
export interface GapInput {
annualLivingExpenses: number; // EXCLUDES mortgage service
mortgageService: number;
survivingSpouseIncome: number;
replacementYears: number;
realDiscountRate: number;
mortgageOutstanding: number;
carLoanOutstanding: number;
otherGuaranteedDebt: number;
finalExpenses: number;
liquidAssets: number;
committedAssets: number;
mpfDeathBenefit: number;
existingGroupCover: number;
educationFundNotSeparatelyProvided: number;
}
export interface GapBlock {
code: 'B1' | 'B2' | 'B3' | 'B4' | 'B5';
label: string;
amount: number;
formula: string;
}
export interface GapResult {
blocks: GapBlock[];
totalNeed: number;
totalResources: number;
protectionGap: number;
incrementalGap: number;
doubleCountCheck: 'pass' | 'fail';
warnings: string[];
}
/** Annuity factor: the present value of 1 per year for n years at rate r. */
export function af(n: number, r: number): number {
if (n <= 0) return 0;
if (r === 0) return n;
return (1 - Math.pow(1 + r, -n)) / r;
}
export function computeProtectionGap(i: Guarded<GapInput>): GapResult {
assertNoDoubleCount(i);
const shortfall = i.annualLivingExpenses - i.survivingSpouseIncome;
if (shortfall <= 0) {
throw new Error(
'Surviving income covers all living costs. ' +
'B1 is zero; only debt and final expenses remain.'
);
}
const b1 = round2(shortfall * af(i.replacementYears, i.realDiscountRate));
const b2 = round2(
i.mortgageOutstanding + i.carLoanOutstanding + i.otherGuaranteedDebt
);
const b3 = round2(i.finalExpenses);
const b4 = round2(i.educationFundNotSeparatelyProvided);
const blocks: GapBlock[] = [
{
code: 'B1',
label: `Income replacement, ${i.replacementYears}y @ ${pct(i.realDiscountRate)} real`,
amount: b1,
formula: `(${i.annualLivingExpenses} - ${i.survivingSpouseIncome}) x ` +
`AF(${i.replacementYears}, ${i.realDiscountRate}) = ${b1}`,
},
{
code: 'B2',
label: 'Debt clearance at death',
amount: b2,
formula: `${i.mortgageOutstanding} + ${i.carLoanOutstanding} + ` +
`${i.otherGuaranteedDebt} = ${b2}`,
},
{
code: 'B3',
label: 'Final expenses',
amount: b3,
formula: `${i.finalExpenses}`,
},
];
if (b4 > 0) {
blocks.push({
code: 'B4',
label: 'Education funding not covered by B1 horizon',
amount: b4,
formula: `${i.educationFundNotSeparatelyProvided}`,
});
}
const totalNeed = round2(b1 + b2 + b3 + b4);
const totalResources = round2(
i.liquidAssets + i.committedAssets + i.mpfDeathBenefit + i.existingGroupCover
);
return {
blocks,
totalNeed,
totalResources,
protectionGap: round2(Math.max(0, totalNeed - totalResources)),
incrementalGap: round2(Math.max(0, totalNeed - totalResources - i.existingGroupCover)),
doubleCountCheck: 'pass',
warnings: buildWarnings(i, b1, b2),
};
}
/**
* The guardrail that catches the 40% error before the client ever sees it.
* If mortgageService was folded into annualLivingExpenses upstream, this trips.
*/
function assertNoDoubleCount(i: Guarded<GapInput>): void {
// Heuristic: living expenses including full mortgage service for a HK household
// with a 6M mortgage almost always exceed net income. Flag rather than throw,
// because genuinely high-income clients do exist.
const totalOutgoings = i.annualLivingExpenses + i.mortgageService;
const impliedIncome = totalOutgoings + i.survivingSpouseIncome;
if (i.mortgageOutstanding > 0 && i.annualLivingExpenses > impliedIncome) {
throw new Error(
'annualLivingExpenses appears to include mortgage service. ' +
'Pass them separately or the mortgage will be counted twice.'
);
}
}
function buildWarnings(i: Guarded<GapInput>, b1: number, b2: number): string[] {
const w: string[] = [];
const mortgageShare = b2 / (b1 + b2 || 1);
if (mortgageShare > 0.5) {
w.push(
`Debt clearance is ${(mortgageShare * 100).toFixed(0)}% of the B1+B2 total. ` +
'Check the mortgage maturity date: if it clears before the replacement ' +
'horizon ends, a decreasing term to maturity is cheaper and sufficient.'
);
}
if (i.replacementYears < 10) {
w.push(
'Replacement horizon below 10 years: confirm no dependant is still ' +
'dependent beyond the horizon.'
);
}
if (i.committedAssets > 0) {
w.push('Committed assets deducted: confirm they are not needed for a known goal.');
}
if (i.existingGroupCover > 0) {
w.push(
'Group cover deducted: record the cessation event (resignation / age) so ' +
'the replacement date is visible.'
);
}
return w;
}
type Guarded<T> = T & Record<string, unknown>;
const round2 = (n: number) => Math.round(n * 100) / 100;
const pct = (n: number) => `${(n * 100).toFixed(1)}%`;
Dependency Analysis
In practice: you map who depends on whose income and for how long, then choose between joint-life and single-life cover, because that choice changes the premium more than anything else in the proposal.
Dependency analysis answers a narrow question: if this person's income stops, who stops eating?
The raw data is a dependency ratio, but the ratio is only a screening tool.
| Household | Earners | Dependants | Earner-to-dependent ratio | Observation |
|---|---|---|---|---|
| Chan family | 2 (948k + 480k) | 2 children (10, 6) | 1.0 | Dependent on the higher earner for 97% of surplus |
| Dual-income, no children | 2 | 0 | n/a | Life cover is for debt and estate, not income |
| Single income, 3 young children | 1 | 3 + non-earning spouse | 0.25 | Highest-need profile in the market |
| Empty nest, mortgage nearly cleared | 2 | 0 | n/a | Needs retirement funding, not much life cover |
The Chan case exposes the most common analytical error in the market. There are two earners, so the instinct is to split the analysis and buy a smaller policy on each. But Chan's income is HK$948,000 and Li's is HK$480,000. If Chan dies, the household loses 66% of income, not 50%. The dependency analysis says: Li is financially dependent on Chan in the sense that matters, even though Li has an income of her own.
Let me be precise, because this is the crux.
If Chan dies today:
- The surviving household income is HK$480,000 (Li) + HK$120,000 (MPF) = HK$600,000
- Against outflow of HK$1,646,400
- Annual shortfall: HK$1,046,400 — a 64% income loss
If Li dies today:
- Surviving household income is HK$948,000 + HK$120,000 = HK$1,068,000
- Against outflow of HK$1,646,400
- Annual shortfall: HK$578,400 — a 36% income loss
So the two deaths are not symmetric, and cover sized "50/50 across the household" is wrong in both directions: it under-insures the main earner and over-insures the secondary.
The right structure.
| Who | Role | Death impact | Appropriate cover |
|---|---|---|---|
| Chan (42) | Main earner, 66% of income | Household short HK$1,046,400/yr | Full gap: B1 + B2 + B3 |
| Li (40) | Secondary earner, 34% of income | Household short HK$578,400/yr | Debt + final expenses + education, not full income replacement |
This is where the mortgage changes the answer for Li. The mortgage does not disappear when the higher earner dies. So the policy on Li is principally about clearing the debt and funding the children, not about replacing a salary she does not fully earn.
Joint-life versus single-life. A joint-life (joint-to-last-survivor) policy pays on the first death, which is what you want when the aim is clearing debt and funding the children. A last-survivor policy pays only when both have died, which is right when the aim is funding the surviving spouse's income to retirement. Most Hong Kong agents should be able to state both and explain the difference, because clients compare quotes that quote different structures as if they were the same product.
The other dependency dimension is parents. In Hong Kong, supporting parents in old age is a real and growing claim. Treat it as a B1 extension with a horizon to the parent's own care needs ending, or as a stated goal with its own target — but do not ignore it, and do not silently assume the client's parents are financially independent when many Hong Kong families have one or two non-earning parents in their late 70s.
Retirement Gap
In practice: you convert a target retirement income into a capital requirement at retirement age, subtract what the client will actually have, and the difference is the retirement gap you fund with savings or an annuity — not with life insurance.
The retirement gap is the second half of need analysis and it is where most of the client's money will be spent over a lifetime.
The method is:
- Decide the replacement ratio — target retirement spending as a percentage of current spending.
- Convert that annual figure to a capital requirement at retirement, using the real net-of-inflation return the client can expect and the horizon.
- Subtract the assets the client will hold at retirement.
- The remainder is the gap to be funded between now and then.
Replacement ratio. For Hong Kong households, 50–70% of pre-retirement spending is the defensible band. Below 50% you are planning a significant drop in lifestyle; above 80% you are usually assuming clients retire debt-free, downsized, and in good health, which is optimistic unless it is true. Take the client's own words on this: "what would your household spending look like if your income stopped tomorrow?" is a better starting point than any textbook ratio, because it captures their actual spending structure.
The capital requirement.
Corpus = annual target × AF(horizon, real net return)
For the Chan family targeting 70% of living costs to age 85 at a 3.5% real net return:
| Target | Annual income | Monthly | Corpus needed at 60 |
|---|---|---|---|
| 60% of living | 691,200 | 57,600 | 11,392,023 |
| 70% of living | 806,400 | 67,200 | 13,290,693 |
| 80% of living | 921,600 | 76,800 | 15,189,364 |
| 100% of living | 1,152,000 | 96,000 | 18,986,705 |
Assets at retirement: HK$1,850,000 savings + HK$180,000 MPF = HK$2,030,000.
Retirement gap at the 70% target: HK$11,260,693.
In monthly terms of real income, that gap is HK$56,936 per month for 25 years — money that does not exist.
Levers, in the order an experienced agent raises them.
You present the gap, then you work down this list. You do not skip to "buy a bigger policy", because every lever below is worth more per dollar spent than product.
| # | Lever | Effect on the gap | Realistic in HK? |
|---|---|---|---|
| 1 | Delay retirement | Each year of delay cuts the drawdown period and adds a saving year | Yes — statutory retirement age is rising to 65 |
| 2 | Downsize the home after the mortgage clears | Releasing equity is the largest single source for many HK households | Yes, and often already planned |
| 3 | Lower the replacement ratio to 50–60% | Cuts the required corpus by roughly a third | Yes — this is normal, not failure |
| 4 | Part-time work 60–65 | Income plus extra saving years | Increasingly common |
| 5 | Larger savings contribution | Direct, but competes with protection and education | Only after 1–4 |
The delay lever is powerful. Working from 60 to 65 changes the required annual contribution from HK$1,455,493 to HK$865,033 — a saving of HK$590,459 a year, or about HK$49,000 a month. Retirement age is the cheapest variable to move in the entire plan, which is exactly why it should be the first lever discussed and not the last.
The home equity lever is worth pausing on, because Hong Kong property is the dominant household asset and it is frequently ignored. Assume the Chans own a 2,000 sq ft Mid-Levels flat at HK$14,000 per sq ft = HK$28,000,000 today, with HK$6,180,000 outstanding. Downsizing to a 900 sq ft unit in a less expensive district releases about HK$11,400,000, roughly HK$10,860,000 net of stamp duty and agency. At a 3.5% real return that adds HK$658,920 a year of sustainable retirement income. That single decision closes more than half the gap.
The discipline is to do not put the same money in two places. If the plan depends on downsizing, the released equity must not also be counted as an investible asset inside the savings projection, or you have double-counted the same square feet.
Where insurance stops and retirement funding starts.
This boundary is the most common source of an indefensible recommendation. Term life insurance is priced on mortality. A 42-year-old non-smoker in good health will die with high probability, so a large term benefit is cheap and the insurer expects to pay it. A retirement annuity is priced on longevity, and the insurer expects to pay it too. They are different risks with different prices.
If you fund a retirement gap with term life, you have bought a product that will pay only if the client dies, at which point they do not need retirement income. If you fund an income-replacement gap with a retirement annuity, you have bought a product that pays only if the client survives, at which point they may still need income.
The correct mapping:
| Need block | Correct instrument | Wrong instrument and why |
|---|---|---|
| B1 income replacement to 57 | Term life, decreasing or level | Retirement annuity — pays on survival, wrong trigger |
| B2 mortgage clearance | Term life to mortgage maturity | Endowment/savings — expensive, and the death benefit is not guaranteed to cover the balance |
| B3 final expenses | A small term rider or a savings buffer | A whole separate policy for HK$120,000 is uneconomic |
| Retirement gap | Savings, ILAS, or a retirement annuity | Term life — pays on the wrong event |
| Education | Endowment or savings-linked plan | Term life — pays only if you die |
The one legitimate hybrid is a savings plan with an accelerated death benefit, which gives a partial life-insurance function plus a fund. But it is a compromise, not a free lunch: you pay for the fund, and the benefit is usually a percentage of the account rather than a stated sum assured, which cannot be checked against B2.
Building a Defensible Recommendation
In practice: you stage the recommendation in priority order, quote the premium for each stage, and hand over a one-page summary that shows both the gap closed and the gap remaining.
A defensible recommendation has four properties.
- It is traceable to stated facts. Every number descends from a profile field.
- It respects affordability. It does not require a deficit.
- It is staged, so the client can stop after stage one and still be better off.
- It records what it does not solve, so the residual gap is visible.
Staging is what separates a plan from a shopping list.
For the Chan household, with the cash deficit dealt with first, the ladder is:
| Stage | Item | Sum assured | Annual premium | Monthly equivalent | Gap closed |
|---|---|---|---|---|---|
| — | Repair the cash deficit (budget exercise) | — | 0 | 0 | Prerequisite |
| S1 | Decreasing term, Chan, to age 57 | 6,180,000 | 6,798 | 11,330 | B2 cleared |
| S1 | Critical illness, Chan | 2,000,000 | 9,200 | 15,333 | B3 illness |
| S1 | Education fund, Hoi-tin (uni 2036) | 600,000 target | 70,338 | 5,862 | B4 partial |
| S1 | Education fund, Yu-yan (uni 2040) | 600,000 target | 53,116 | 4,426 | B4 partial |
| S1 total | 139,452 | 11,621 | |||
| S2 | Critical illness, Li | 1,000,000 | 2,800 | 4,667 | B3 illness |
| S2 | Term, Li (debt + education) | 2,000,000 | 5,800 | 9,667 | B2 + B4 |
| S2 total | 8,600 | 7,167 | |||
| S3 | Level term, Chan, step-up at 50 | 4,000,000 | 12,800 | 21,333 | B1 partial |
| Full plan | 160,852 | 13,404 |
Rate assumptions: decreasing term HK$1,100 per HK$1,000,000 (F42, 20-pay); level term HK$3,200 per HK$1,000,000 (F42, 20-pay) and HK$2,900 (M40, 20-pay); critical illness HK$4,600 per HK$1,000,000 (F42, 20-pay) and HK$2,800 (M40, 20-pay). These are illustrative figures for analysis — always quote from the live rate table (Lesson 03).
Reading the table honestly.
The full plan is HK$13,404 a month. The household was HK$8,200 a month in deficit before any premium. So the full plan is arithmetically unavailable to this client today, and saying otherwise would be indefensible.
What is defensible:
- The analysis and the gap number are correct and should be kept on file.
- The first action is cashflow repair: reduce HK$8,200/month of outflow, or raise income, or use a small amount of savings to absorb two years while the repair lands.
- Once the deficit is closed, stage S1 becomes affordable at HK$11,621/month, and even then the adviser should check it against the retirement contribution, because S1 contains the education funding, which is a savings product competing with retirement funding.
The point of showing the full plan and its unavailability is not to embarrass the client. It is to establish that you computed the complete answer, and that you are proposing a sequence rather than a sales pitch.
Objections you should expect, and the answer to each.
| Objection | The response that holds up |
|---|---|
| "I have group life, I am covered" | Group life stops at resignation and usually pays only on death. Let me show you the year it ends and the multiple. |
| "I have a savings policy with a death benefit" | Let us compare what it actually pays. Is it a stated sum or a percentage of a fund subject to market movement? |
| "Term life is a waste, I never get the money back" | Correct, and that is the point — it is the cheapest way to protect a mortgage. Your savings policy protects nothing if you die. |
| "My agent at the bank already looked at this" | Good. Let us run it against yours so you can compare like for like, including the double-count check. |
| "The premium is too high" | The gap is HK$11.7m and we can afford about HK$X. Let us show you which part of the gap we can close this year and which part waits. |
| "Is this guaranteed to pay?" | Your term life death benefit is guaranteed if premiums are paid. The education fund's projected value is not. Here is the guaranteed portion only. |
The last one matters more than it looks and connects to Lesson 05 and 06. Distinguishing a guaranteed benefit from an illustrated projection is a compliance obligation, not a courtesy. A client who believes a projected savings value is a guarantee has been misled, and the file will show it.
When the recommendation is "not yet".
A legitimate and often correct outcome is: no product today. The reasons that are genuinely defensible rather than evasive:
- The client is in deficit; any premium will be surrendered.
- The client has a year of major known capital calls and a thin emergency fund.
- The client has an unresolved health question that would change the price by more than the premium saved by waiting (see Lesson 06).
- The client is about to change jobs and the group cover and residual insurance both depend on employment.
If that is your answer, write it down with the reasons and the date you will revisit. An agent who records "no sale, review in Q3 after the bonus, because deficit of HK$98,400/yr" is far better placed than one who fills the gap in the file.
Documenting the Reasoning
In practice: you save the profile, the formulas, the assumptions and the rejected options, because a recommendation that cannot be re-derived two years later is a recommendation that cannot be defended.
Documentation in need analysis serves three audiences, and they need different things.
| Audience | What they need | Where it lives |
|---|---|---|
| The client | The numbers and the reasoning, in plain language | The needs analysis report |
| Compliance / IA review | The facts, the formulas, the assumptions, the rejections | The file, retained per the Insurance Ordinance record-keeping rules |
| The next advisor | Everything, including what you tried and why it failed | The CRM record and case notes |
The single most valuable thing you can record is the rejected option. If the client declined the full plan, record the full plan, the premium, the reason, and the date. Next year, when the situation changes, the next advisor must not re-explain what already failed.
Here is the record you persist at the end of a needs analysis.
{
"recordType": "needs-analysis",
"schemaVersion": "1.2",
"adviserRef": "FNA-2026-03-1147",
"clientId": "CL-88214",
"profileHash": "sha256:9f2c1ab7e5d04c3b8a6e1f27c9d3b4a08",
"assessedAt": "2026-03-11T14:22:07+08:00",
"advisor": {
"agency": "AG-0042",
"agentCode": "PA-1187",
"isAgencyRep": true,
"supervisedBy": "PA-0903"
},
"assumptions": {
"realDiscountRate": 0.04,
"inflationRate": 0.03,
"basis": "real terms throughout; no nominal/real mixing",
"replacementRatioTarget": 0.7,
"retirementAge": 60,
"planHorizonAge": 85,
"investmentReturnAccumulation": 0.045,
"investmentReturnDrawdown": 0.035
},
"blocks": [
{
"code": "B1",
"subject": "CHAN-SIU-MAN",
"label": "Income replacement 15y @4% real",
"amount": 7471556,
"formula": "(1152000 - 480000) * AF(15, 0.04)",
"inputs": {
"annualLivingExpensesExMortgage": 1152000,
"survivingSpouseIncome": 480000,
"replacementYears": 15,
"rate": 0.04
}
},
{
"code": "B2",
"subject": "HOUSEHOLD",
"label": "Mortgage clearance",
"amount": 6180000,
"formula": "6180000 + 0 + 0",
"inputs": {
"mortgageOutstanding": 6180000,
"carLoan": 0,
"guaranteedDebt": 0
}
},
{
"code": "B3",
"subject": "HOUSEHOLD",
"label": "Final expenses",
"amount": 120000,
"formula": "120000",
"inputs": { "basis": "client-stated religious requirement" }
}
],
"result": {
"totalNeed": 13771556,
"totalResources": 2030000,
"protectionGap": 11741556,
"existingGroupCoverDeducted": 250000,
"incrementalGap": 11491556,
"doubleCountCheck": "pass"
},
"affordability": {
"netPositionAnnual": -98400,
"affordablePremiumAnnual": 0,
"ceilingBasis": "deficit-household",
"verdict": "fix-cashflow-first"
},
"recommendation": {
"status": "staged",
"stages": [
{ "stage": 0, "action": "cashflow repair", "premiumAnnual": 0 },
{ "stage": 1, "action": "term + CI + education", "premiumAnnual": 139452 },
{ "stage": 2, "action": "spouse cover", "premiumAnnual": 8600 },
{ "stage": 3, "action": "income replacement step-up", "premiumAnnual": 12800 }
],
"fullPlanPremiumAnnual": 160852,
"affordableNow": false,
"residualGapAfterFullPlan": 272956,
"reviewBy": "2026-09-30"
},
"rejectedOptions": [
{
"option": "HK$10,000,000 level term to age 85",
"premiumAnnual": 28800,
"rejectedReason": "Mispriced for purpose. Term to 85 outlives the need and the " +
"same money as savings dominates the plan's purpose.",
"rejectedBy": "advisor",
"date": "2026-03-11"
},
{
"option": "Mortgage-inclusive expense basis",
"premiumAnnual": 0,
"rejectedReason": "Would double-count the mortgage; computed gap 19,268,467 " +
"vs correct 13,771,556.",
"rejectedBy": "advisor",
"date": "2026-03-11"
}
],
"clientAcknowledgements": [
{
"statement": "Client understands HK$11,491,556 is the computed protection gap.",
"method": "verbal-confirmed-and-signed",
"capturedAt": "2026-03-11T15:05:00+08:00"
},
{
"statement": "Client was advised that education fund values are projected, not guaranteed.",
"method": "written-disclosure-sent",
"documentRef": "DISC-EDU-2026-03-11"
}
]
}
Four features of that record are worth naming.
doubleCountCheck is persisted, not merely performed.
It becomes evidence that you checked.
rejectedOptions keeps the discarded paths.
A reviewer who sees "I considered HK$10m to 85 and rejected it as mispriced" learns that
your recommendation was a choice.
affordability.affordablePremiumAnnual: 0 next to a full plan costing HK$160,852
prevents the single most damaging finding in a compliance review — a recommendation the
client had no capacity to pay.
residualGapAfterFullPlan is HK$272,956, not zero.
The plan does not solve the problem completely, and the file says so.
A recommendation that claims to solve everything invites the question of why it needs to.
-- Before you file: does this record survive a re-derivation?
-- This is the query a reviewer would run, so run it yourself first.
WITH recomputed AS (
SELECT
b.assessment_id,
SUM(CASE WHEN b.block_code = 'B1' THEN b.amount
ELSE 0 END) AS b1,
SUM(CASE WHEN b.block_code = 'B2' THEN b.amount
ELSE 0 END) AS b2,
SUM(CASE WHEN b.block_code = 'B3' THEN b.amount
ELSE 0 END) AS b3,
SUM(CASE WHEN b.block_code = 'B4' THEN b.amount
ELSE 0 END) AS b4
FROM need_block b
GROUP BY b.assessment_id
),
filed AS (
SELECT
r.assessment_id,
CAST(json_extract(r.payload, '$.result.totalNeed') AS INTEGER) AS filed_total_need,
CAST(json_extract(r.payload, '$.result.protectionGap') AS INTEGER) AS filed_gap,
CAST(json_extract(r.payload, '$.affordability.affordablePremiumAnnual') AS INTEGER)
AS affordable_premium,
CAST(json_extract(r.payload, '$.result.totalResources') AS INTEGER) AS filed_resources,
CAST(json_extract(r.payload, '$.recommendation.fullPlanPremiumAnnual') AS INTEGER)
AS full_plan_premium,
CAST(json_extract(r.payload, '$.recommendation.residualGapAfterFullPlan') AS INTEGER)
AS residual_gap
FROM recommendation r
)
SELECT
f.assessment_id,
r.b1 + r.b2 + r.b3 + r.b4 AS recomputed_total_need,
f.filed_total_need,
(r.b1 + r.b2 + r.b3 + r.b4) - f.filed_total_need AS need_delta,
f.filed_resources + f.filed_gap - f.filed_total_need AS gap_consistency_delta,
f.full_plan_premium - f.affordable_premium AS affordability_headroom,
CASE
WHEN (r.b1 + r.b2 + r.b3 + r.b4) <> f.filed_total_need
THEN 'FAIL: filed total does not equal sum of blocks'
WHEN f.filed_resources + f.filed_gap <> f.filed_total_need
THEN 'FAIL: resources + gap <> total need'
WHEN f.full_plan_premium > f.affordable_premium
THEN 'FAIL: full plan exceeds the affordability ceiling'
WHEN f.residual_gap < 0
THEN 'FAIL: negative residual gap'
ELSE 'PASS'
END AS verdict
FROM filed f
JOIN recomputed r ON r.assessment_id = f.assessment_id;
gap_consistency_delta is zero only if resources plus gap equals total need.
If an agent reports a gap of HK$11.7m and resources of HK$2.03m against a total need of
HK$13.8m, that identity holds.
If it does not, one of the three numbers was adjusted to make the presentation look
better, and that is the end of the file.
Key Takeaways
-
Profiling is obligation-mapping, not personality typing. Collect income, expenses with mortgage service separated, liabilities, dependants, existing cover, assets and time horizon before opening the catalogue.
-
Need and budget are different questions. Need comes from obligations; budget comes from surplus. When they conflict, negotiate the target — never shrink the sum assured silently until the premium feels acceptable.
-
A household already in deficit cannot fund new premium. The Chan household is HK$98,400 a year short before any premium. "Repair the cashflow first" is a legitimate, and here correct, recommendation.
-
The mortgage is the most double-counted item in the market. Including mortgage service in the 15-year expense stream and adding the outstanding balance inflates the gap by 40% in this example. Assert it in code and store the check result.
-
Home equity is not a resource while the mortgage is on it. Deducting equity while adding the mortgage to the need counts the same property twice; and if the plan depends on downsizing, the released equity cannot also sit inside the savings projection.
-
Set the replacement horizon as the minimum of independence, retirement and pricing viability. Chan buys the debt-and-dependency layer to year 15 and funds retirement separately, rather than buying a term policy to age 85 that outlives the need.
-
Match the instrument to the trigger. Term life pays on death, so it covers income replacement and debt. Retirement annuities pay on survival, so they fund the retirement gap. Funding either need with the other product is the most common indefensible error.
-
Stage the recommendation and record the residual gap. A plan that claims to close the gap completely invites the question of why anything else is needed; here the honest residual is HK$272,956.
-
Persist assumptions, formulas and rejected options. A recommendation that cannot be re-derived two years later cannot be defended, and
rejectedOptionsis what stops the next advisor re-proposing what already failed.