學習目標 · Learning Objectives
- Produce a complete FNA document with the standard sections — client situation, goals, cash-flow projection, return assumptions, retirement modelling, recommendation and disclosures — and know why each section exists.
- Build a household cash-flow projection in real dollars that survives the retirement cliff, and separate guaranteed figures from projected figures at every step.
- Run sensitivity analysis on the assumptions that actually move the answer (retirement age, real return, replacement ratio, planning horizon), and present the result as a range the client chooses within, rather than a single false-precision number.
Financial Needs Analysis (財務策劃 FNA)
Need analysis (Lesson 04) tells you what the client is short of. The FNA tells you what to do about it, with numbers the client can check and a compliance reviewer can audit.
An FNA is a document, not a conversation. In Hong Kong it is frequently the single most important file in a client's relationship with an agency: it is what the client keeps, what a complaint is judged against, and what an inspector asks to see. A good FNA is short enough to read in fifteen minutes and complete enough that nobody needs to ask where a number came from.
The FNA has one job: to make the client's future legible. Legible means every figure traces to an input, every input traces to a fact the client confirmed, and every assumption is visible enough to be disagreed with.
Learning Objectives
- Produce a complete FNA document with the standard sections — client situation, goals, cash-flow projection, return assumptions, retirement modelling, recommendation and disclosures — and know why each section exists.
- Build a household cash-flow projection in real dollars that survives the retirement cliff, and separate guaranteed figures from projected figures at every step.
- Run sensitivity analysis on the assumptions that actually move the answer (retirement age, real return, replacement ratio, planning horizon), and present the result as a range the client chooses within, rather than a single false-precision number.
What an FNA Document Contains
In practice: you fill a fixed section structure every time, because a consistent structure is what lets a reviewer compare two FNAs side by side and find what you missed.
The section list below is the working standard for this course. Real agency templates vary, and some carriers supply their own, but every competent Hong Kong FNA contains these elements in roughly this order.
| # | Section | Purpose | If omitted, what goes wrong |
|---|---|---|---|
| 1 | Client and household profile | Establishes the facts the whole document rests on | No traceability; every number is an assertion |
| 2 | Objectives, with dates and amounts | Converts vague intentions into testable goals | The plan has no success criterion |
| 3 | Current financial position | Assets, liabilities, existing cover, cashflow | Cannot measure the gap |
| 4 | Cash-flow projection | Shows the trajectory under current behaviour | The client cannot see the cliff they are heading towards |
| 5 | Assumptions and their basis | Inflation, returns, horizon, replacement ratio | Numbers appear arbitrary; a dispute becomes unwinnable |
| 6 | Gap analysis | The distance between position and objective | No rationale for the recommendation |
| 7 | Retirement modelling | Portfolio needed, income it buys, drawdown plan | Retirement is sold as a product, not modelled as a system |
| 8 | Sensitivity analysis | Shows which assumptions matter | The client believes the plan to a false degree of confidence |
| 9 | Recommendation and staging | What to do, in what order, at what cost | A shopping list instead of a plan |
| 10 | Disclosures and acknowledgements | Guarantee status, risks, warnings | Compliance failure — see Lesson 12 |
| 11 | Product illustration, if any | Guaranteed vs non-guaranteed side by side | A projection is mistaken for a promise |
Sections 10 and 11 are not optional and are frequently the ones rushed. A signed acknowledgement that distinguishes guaranteed from non-guaranteed values is the document that protects both the client and the advisor.
The distinction between sections 4 and 7 is the one most often collapsed. The cash-flow projection shows what happens under current behaviour. Retirement modelling shows what is needed to meet the target. They are different questions, they use different returns, and a plan that conflates them produces a number that means nothing.
Here is the document as a structured object, which is how your system should hold it before rendering.
export type GuaranteeStatus = 'guaranteed' | 'projected' | 'illustrative';
export interface FnaAssumptions {
inflationRate: number; // e.g. 0.03
accumulationReturnReal: number; // e.g. 0.045
drawdownReturnReal: number; // e.g. 0.035
retirementAge: number;
planningHorizonAge: number; // e.g. 85
replacementRatio: number; // e.g. 0.70
taxRate: number; // profits tax rate applied, 0 if N/A
salaryGrowth: number; // e.g. 0.02, or 0 for conservative plans
basisNote: string; // the sentence shown to the client
}
export interface FnaObjective {
id: string;
label: string;
type: 'retirement' | 'education' | 'health' | 'housing' | 'estate' | 'debt';
targetAmount: number;
targetDate: string;
monthlyContribution: number;
fundingInstrument: string;
priority: 1 | 2 | 3;
}
export interface ProjectionYear {
year: number;
age: number;
income: number;
mpfContribution: number;
livingExpense: number;
debtService: number;
surplus: number;
portfolioOpening: number;
investmentReturn: number;
extraContribution: number;
portfolioClosing: number;
phase: 'accumulation' | 'transition' | 'drawdown';
}
export interface Fna {
fnaRef: string; // FNA-2026-03-1147
clientId: string;
advisorId: string;
agencyCode: string;
preparedAt: string; // ISO-8601 with +08:00
currency: 'HKD';
basis: 'real' | 'nominal'; // never mix the two
profileHash: string; // ties to the Lesson 04 profile
household: string[];
objectives: FnaObjective[];
currentPosition: {
liquidAssets: number;
mpfAccrued: number;
propertyEquityExcluded: number; // deliberately 0 while mortgaged
mortgageOutstanding: number;
otherLiabilities: number;
annualIncome: number;
annualOutgoings: number;
netAnnualPosition: number;
};
assumptions: FnaAssumptions;
projections: ProjectionYear[];
retirementModel: RetirementModel;
sensitivity: SensitivityRun[];
recommendation: RecommendationSection;
disclosures: Disclosure[];
clientAcknowledgements: Acknowledgement[];
advisorAttestation: string;
}
Note basis: 'real' | 'nominal'.
That single field should be validated, because mixing a real expense stream with a
nominal discount rate is the arithmetic error that quietly produces a 40% error — the
same family of mistake as the mortgage double-count in Lesson 04.
Make it impossible to express a document whose basis is unknown.
Cash-Flow Projection
In practice: you project the household year by year to a horizon that extends past retirement — typically age 85 — so the client can see not just when they run out of money but what happens if they are wrong about the date.
A cash-flow projection has three phases, and the middle one is where most HK plans fail.
| Phase | Years | What happens | What the client must see |
|---|---|---|---|
| Accumulation | now → retirement | Income exceeds spending; surplus is saved | The growing portfolio and the growing surplus |
| Transition | retirement → +5 years | One income ends, spending is partially funded, the other may still work | The drawdown starting, and the second income still running |
| Drawdown | +5 years → horizon | No earned income; the portfolio funds spending | The date the portfolio is exhausted under the base case |
The Chen household again, with the facts locked in Lesson 04. Chan's net income is HK$948,000 (HK$79,000/month); Li's is HK$480,000 (HK$40,000/month); combined MPF contributions are HK$120,000; living expenses excluding mortgage are HK$1,152,000; mortgage service is HK$494,400. Their net position today is −HK$98,400 a year.
That deficit is the first fact of the projection, and every subsequent number follows from it.
Under current behaviour — no new saving, expenses continue at 3% inflation — the projection runs like this (all figures HK$'000, real terms):
| Year | Age | Surplus | Portfolio | Phase |
|---|---|---|---|---|
| 2026 | 42 | −126 | 1,724 | accumulation |
| 2030 | 46 | −126 | 1,697 | accumulation |
| 2036 | 52 | −174 | 1,189 | accumulation |
| 2040 | 56 | −210 | 581 | accumulation |
| 2043 | 59 | −241 | −60 | portfolio exhausted 2043 |
| 2044 | 60 | −1,473 | −1,535 | Chan retires; Li still working |
| 2046 | 62 | −1,961 | −5,180 | Li retires; no income at all |
| 2055 | 71 | −2,595 | −30,728 | drawdown |
| 2069 | 85 | −3,986 | −106,956 | drawdown |
The projected outcome under current behaviour is that the portfolio is exhausted in 2043, one year before Chan retires, and the household is then entirely dependent on state support and family from that point.
Two observations an experienced agent makes immediately:
- The deficit is structural, not a one-off. The household spends more than it earns by design — high fixed costs, two children in private school fees — so the portfolio is being consumed from day one. No investment return fixes a negative cashflow.
- Li's income does not disappear when Chan retires. Many plans assume both incomes end together. Modelling a two-year window where one income continues is materially more accurate and materially more generous to the client.
Under the recommended plan — living expenses reduced by 10% (HK$1,036,800) and HK$250,000 a year of additional saving — the same horizon produces:
| Year | Age | Surplus | Portfolio | Phase |
|---|---|---|---|---|
| 2026 | 42 | −126 | 2,974 | accumulation |
| 2030 | 46 | −126 | 3,064 | accumulation |
| 2036 | 52 | −174 | 4,649 | accumulation |
| 2040 | 56 | −210 | 5,777 | accumulation |
| 2043 | 59 | −241 | 6,654 | accumulation |
| 2044 | 60 | −1,277 | 5,610 | transition |
| 2046 | 62 | −1,753 | 2,891 | transition |
| 2048 | 64 | −2,107 | −0 | portfolio exhausted 2048 |
| 2055 | 71 | −2,323 | −17,237 | drawdown |
| 2069 | 85 | −3,576 | −79,184 | drawdown |
The plan buys five more years than doing nothing, which is real but not enough — and that is the honest finding. The plan as modelled still fails before the planning horizon. Reporting that plainly is more valuable than tuning the assumptions until the model passes, because a client who is shown a failing base case will act on a lever, whereas a client shown a passing case that depends on 4.5% real returns for 18 years will simply rely on it.
The full recommended-plan projection, in HK$'000:
| Year | Age | Chan | Li | MPF | Household spend | Surplus | Portfolio | Return |
|---|---|---|---|---|---|---|---|---|
| 2026 | 42 | 948 | 480 | 36 | 1,531 | −126 | 550 | 4.5% |
| 2030 | 46 | 1,067 | 540 | 36 | 1,723 | −126 | 1,105 | 4.5% |
| 2035 | 51 | 1,237 | 626 | 36 | 1,998 | −174 | 1,865 | 4.5% |
| 2040 | 56 | 1,434 | 726 | 36 | 2,316 | −210 | 2,702 | 4.5% |
| 2043 | 59 | 1,567 | 793 | 36 | 2,531 | −241 | 3,242 | 4.5% |
| 2044 | 60 | 0 | 368 | 36 | 1,971 | −1,277 | 1,788 | 3.5% |
| 2046 | 62 | 0 | 0 | 36 | 2,091 | −1,753 | 2,891 | 3.5% |
| 2055 | 71 | 0 | 0 | 36 | 1,564 | −1,528 | −21,257 | 3.5% |
| 2069 | 85 | 0 | 0 | 36 | 2,365 | −2,329 | −67,954 | 3.5% |
(The two tables differ in the starting balance because the recommended-plan table opens at HK$418,000 of investable savings with the rest ring-fenced in emergency and MPF reserve; that split must be stated in the FNA, not buried.)
Here is the projection engine. It is worth reading closely because the order of
operations in simulate is the difference between a correct projection and a plausible
one.
export interface ProjectionParams {
startYear: number;
horizonYear: number;
members: Array<{
birthYear: number;
retirementAge: number;
netIncome: number;
incomeGrowth: number;
}>;
mpfContribution: number;
livingExpense: number;
expenseInflation: number;
mortgagePayment: number;
mortgageEndsYear: number;
openingBalance: number;
extraContribution: number;
accumulationReturn: number;
drawdownReturn: number;
horizonSpendingRatio: number; // retirement spend as a fraction of living expense
/** For Li-style secondary earners: work fraction between two retirements. */
bridgeWorkFraction?: number;
}
export interface ProjectionResult {
rows: ProjectionYear[];
firstDeficitYear: number | null;
firstNegativeBalanceYear: number | null;
finalBalance: number;
minBalance: number;
peakBalance: number;
}
/**
* Order of operations matters and is the most common source of subtle error:
* 1. income = base income grown by incomeGrowth to that year
* 2. retirement = zero the income AT the retirement year, not after it
* 3. expense = base expense inflated to that year
* 4. mortgage = service only while the mortgage runs
* 5. surplus = income + mpf - expense - mortgage service
* 6. return = applied to the OPENING balance, then add surplus and contributions
* (applying it to the closing balance silently compounds one year late)
*/
export function simulate(p: ProjectionParams): ProjectionResult {
if (p.horizonYear <= p.startYear) {
throw new Error('horizon must be after start year');
}
const rows: ProjectionYear[] = [];
let balance = p.openingBalance;
for (let year = p.startYear; yLimit(year, p.horizonYear); year++) {
const t = year - p.startYear; // years since start
const age = year - p.members[0].birthYear;
let income = 0;
let anyoneWorking = false;
for (const m of p.members) {
const retiresAt = m.birthYear + m.retirementAge;
if (year < retiresAt) {
// Before the earliest retirement, everyone works full time.
income += m.netIncome * Math.pow(1 + m.incomeGrowth, t);
anyoneWorking = true;
} else {
// Between retirements a surviving earner may work part time.
const othersRetiring = p.members.some(
(o) => o.birthYear + o.retirementAge > year
);
if (othersRetiring && p.bridgeWorkFraction) {
const lastRetirement = Math.max(
...p.members.map((o) => o.birthYear + o.retirementAge)
);
// Phase down the bridge earner in the final two years before they stop.
const taper =
year >= lastRetirement - 2
? p.bridgeWorkFraction * 0.5
: p.bridgeWorkFraction;
income += m.netIncome * Math.pow(1 + m.incomeGrowth, t) * taper;
}
}
}
const earliestRetirement = Math.min(
...p.members.map((m) => m.birthYear + m.retirementAge)
);
const inRetirement = year >= earliestRetirement;
const expense =
(inRetirement
? p.livingExpense * p.horizonSpendingRatio
: p.livingExpense) * Math.pow(1 + p.expenseInflation, t);
const mortgage = year < p.mortgageEndsYear ? p.mortgagePayment : 0;
const surplus = income + p.mpfContribution - expense - mortgage;
const ret =
inRetirement ? p.drawdownReturn : p.accumulationReturn;
const opening = balance;
const growth = opening * ret;
const contribution =
!inRetirement && year > p.startYear ? p.extraContribution : 0;
balance = opening + growth + surplus + contribution;
rows.push({
year,
age,
income,
mpfContribution: p.mpfContribution,
livingExpense: expense,
debtService: mortgage,
surplus,
portfolioOpening: opening,
investmentReturn: growth,
extraContribution: contribution,
portfolioClosing: balance,
phase: inRetirement ? 'drawdown' : 'accumulation',
});
}
const firstDeficitYear =
rows.find((r) => r.surplus < 0)?.year ?? null;
const firstNegativeBalanceYear =
rows.find((r) => r.portfolioClosing < 0)?.year ?? null;
return {
rows,
firstDeficitYear,
firstNegativeBalanceYear,
finalBalance: balance,
minBalance: Math.min(...rows.map((r) => r.portfolioClosing)),
peakBalance: Math.max(...rows.map((r) => r.portfolioClosing)),
};
}
function yLimit(year: number, horizon: number): boolean {
return year <= horizon;
}
Note bridgeWorkFraction.
Without it the model kills both incomes in the same year and produces a needlessly
alarming projection.
With it, the two-year transition where Li still works at reduced hours is represented
explicitly.
Real Hong Kong households often have exactly this shape: one partner retires at 60, the
other at 62, and for two years they are not fully-income-free.
The extraContribution guard year > p.startYear is deliberate.
It prevents an off-by-one where the plan's first contribution is counted twice or not
counted at all, which shifts every subsequent balance.
Return Assumptions and Why They Matter
In practice: you write down every return you used, whether it is guaranteed, and whether it is net of fees and tax — and you run the plan at a lower return than you think is achievable.
Every FNA number downstream of the projection is a function of the return assumption. This is where FNA disputes are actually won and lost, so the standard is strict.
The three returns you will use.
| Return | Applied | Typical assumption | Basis |
|---|---|---|---|
| Accumulation | While still working and contributing | 4.5% real | Balanced portfolio, long horizon, net of fees |
| Drawdown | Retirement, portfolio funding spending | 3.5% real | Conservative mix as the portfolio is drawn down |
| Guaranteed rate | Inside savings/annuity products | Product-specific, often 1–3% | Contractual, but only on the guaranteed portion |
Three discipline points that separate a defensible FNA from a sales illustration.
Real versus nominal, consistently.
If the projection is in real terms, the return must be real.
The default HK inflation assumption used in this course is 3%.
So a 6.5% nominal expected return is a 3.5% real return — not 6.5%.
An FNA that inflates expenses at 3% and discounts at 6.5% has quietly overstated the
plan by roughly 40%.
The field basis exists to make this explicit and the renderer should show the client
"all figures in 2026 dollars" on every page.
Fees and tax are inside the return, never added later. An ILAS with a 1.5% total expense ratio does not return the fund return. It returns the fund return minus 1.5%. If your assumption is gross, the plan is overstated; if net, it is honest. The difference at HK$3 million over 20 years is hundreds of thousands of dollars.
Guaranteed versus non-guaranteed, in one table. This is the single most important disclosure in the document.
| Guaranteed rate | Non-guaranteed rate | Total illustrated | Value if non-guaranteed is 0% | Value at illustrated |
|---|---|---|---|---|
| 0.0% | 7.0% | 7.0% | 1,360,000 | 4,607,603 |
| 2.0% | 5.0% | 7.0% | 1,923,300 | 4,607,603 |
| 3.5% | 3.5% | 7.0% | 2,498,823 | 4,607,603 |
| 5.0% | 1.5% | 6.5% | 3,248,485 | 4,222,501 |
| 5.9% | 0.1% | 6.0% | 3,802,229 | 3,869,276 |
Basis: HK$1,000,000 single premium, HK$18,000 a year for 20 years.
Read the last two columns together and the product design becomes obvious. The two columns are nearly identical at 5.9% guaranteed and diverge enormously at 0% guaranteed. A client who only ever sees the right-hand column is being shown the best case and told nothing about the left-hand one. The number that matters for a client's planning confidence is the difference between them, not either one alone.
The 0.0% guaranteed row is the honest floor: HK$1,360,000 of the HK$1,000,000 plus premiums means the contract paid back essentially nothing in real terms over 20 years. A plan built on the 7% illustrated total fails completely in that row. A plan built on a 3.5% guaranteed rate survives it.
What to do with this in practice.
- Show both columns, always, and get the client to initial the page.
- Run the recommendation at the guaranteed-only rate if the recommendation depends on it being safe.
- Where a higher return is assumed, justify it with the asset mix and say what happens to the plan if it is not achieved.
- Never mix a product illustration's return into your FNA projection unless the FNA explicitly labels that row as projected.
Gap Analysis: From Projection to Recommendation
In practice: you convert a projected shortfall into a ranked list of levers with a dollar effect on each, because a gap without a bridge is a worry, not a plan.
Sections 4 and 7 tell the client where they are and where they need to be. Section 6 is the one that produces a recommendation, and it is the section most often skipped or reduced to a single number.
A single shortfall figure ("you are HK$4.2m short") creates no action. A bridged gap creates a decision, because the client can see which part of the gap they can close with a behaviour and which part requires money they may not have.
The gap is measured at retirement, in real terms, on the target date.
Not at today's portfolio value, and not as a percentage. The client's question is "will the money be there on the day I stop working", so that is where the number belongs.
| Objective | Required at target date | Projected at target date | Gap | Priority |
|---|---|---|---|---|
| Retirement income (70% replacement to 85) | 10,937,133 | 4,482,551 | −6,454,582 | 1 |
| Emergency reserve (6 months spend) | 691,200 | 418,000 | −273,200 | 2 |
| Education fund (2 children, 8 years) | 1,480,000 | 0 | −1,480,000 | 3 |
| Mortgage payoff at 65 | 0 | 2,880,000 | +2,880,000 | — |
Note the last row. The household is ahead on the mortgage by retirement, and that surplus is a funding source, not a saving. An FNA that shows only negatives is hiding the client's actual flexibility — which is exactly the flexibility that makes the plan work.
The bridge: how the HK$6.45m gap is actually closed.
This is the analytical core of the FNA and it is where the levers get ranked. Each lever is expressed as a change to a modelled input, re-run through the same engine, and recorded with its dollar effect. Ranked by effect per unit of client sacrifice:
| Lever | Change | Effect on gap | Cost / sacrifice | Effort to client |
|---|---|---|---|---|
| Retire at 62 instead of 60 | +2 years accumulation | +1,142,858 | 2 more years of income | High |
| Reduce living expenses 10% | 1,152,000 → 1,036,800 | +845,000 sustained | Lifestyle change | Medium |
| Additional annual saving | +250,000/yr for 17 yrs | +5,361,314 at 4.5% | Discretionary income | Medium |
| Higher real return | 4.5% → 5.5% | +1,502,000 | Market risk, uncontrollable | None |
| Increase sum assured only | No effect on cash-flow gap | 0 | — | — |
Read the last row carefully, because it is the mistake this module exists to prevent. Increasing the sum assured does nothing for a cash-flow gap. It changes the size of the payout on death, not the amount of income the household has at retirement. An agent who responds to a retirement gap by recommending more life cover has diagnosed the wrong problem — and the client will feel the advice failed, because it will have.
Read the fourth row with the same care. A 1-point higher real return closes HK$1.5m of the gap and costs the client nothing today. It is still the wrong row to lead with, because it transfers the entire plan onto a market outcome the client does not control. Rank by effect, but rank the levers the client controls above the lever the market controls, even when the market lever is larger.
The engine persists yearly projections, and the bridge is a query over them rather than a separate model. This is why the numbers above always reconcile:
-- Gap bridge: re-run the projection per lever, rank by dollar effect.
WITH levers(lever_id, label, retirement_age, living_expense, annual_saving, real_return) AS (
VALUES
('BASE', 'Retire 60, as modelled', 60, 1152000, 0, 0.045),
('AGE', 'Retire 62 instead of 60', 62, 1152000, 0, 0.045),
('SPEND', 'Reduce living expenses 10%', 60, 1036800, 0, 0.045),
('SAVE', 'Additional HK$250,000 annual saving', 60, 1152000, 250000, 0.045),
('RET', 'Real return 5.5% instead of 4.5%', 60, 1152000, 0, 0.055)
),
runs AS (
SELECT l.lever_id, l.label, l.retirement_age, l.living_expense,
l.annual_saving, l.real_return,
p.portfolio_closing AS corpus_at_retirement
FROM levers l
JOIN projection_run r ON r.lever_id = l.lever_id
JOIN projection_year p ON p.run_id = r.run_id
AND p.age = l.retirement_age -- gap is measured HERE
)
SELECT
base.corpus_at_retirement AS required_now,
r.corpus_at_retirement AS corpus_with_lever,
r.corpus_at_retirement - base.corpus_at_retirement AS effect_on_gap,
10937133 - r.corpus_at_retirement AS residual_gap,
r.retirement_age AS client_controlled_age,
r.real_return AS client_controlled_return
FROM runs r
CROSS JOIN (SELECT corpus_at_retirement FROM runs WHERE lever_id = 'BASE') base
ORDER BY effect_on_gap DESC;
Three properties of this query are deliberate, and each one has prevented a real category of bad FNA:
p.age = l.retirement_age— the gap is read at the lever's own retirement age, not at the base retirement age. Measuring every lever at age 60 would credit the "retire at 62" lever for two extra years of investment that a client who actually retires at 62 will indeed have, but would measure it against a target that no longer matches their plan. The requirement moves with the lever.- A
CROSS JOINon the base run, not a hard-coded constant — so the base case is read from the same engine that produced the levers. If the engine changes, the bridge changes with it. A bridge with a typed-in baseline is a bridge that will silently disagree with the projection it claims to bridge. residual_gapis projected, not suppressed. A lever that does not close the gap still shows the number that remains. The plan this produces is allowed to be incomplete, visibly.
What to record for each lever. The bridge is only auditable if each row survives to the file:
| Field | Purpose |
|---|---|
lever_id, label | Identifies the lever in later reviews |
| The input change | So the run can be reproduced exactly |
effect_on_gap | The dollar effect, signed |
residual_gap | What is still missing after this lever |
client_controlled | Boolean — false for any lever the market decides |
presented_to_client | Boolean — a lever the client was never shown is not advice |
accepted | Boolean — the client's actual decision, not the recommended one |
The accepted column is the one that matters most and is the one most often omitted.
The client does not have to take your recommendation. If they decline to retire later,
that is a recorded decision with a review date attached — not an absent row, which reads
in six months as an analysis that was never completed.
A plan where every lever is accepted = false is a legitimate FNA outcome. It means the
client declined every available lever, and the file should record that the gap is real,
unfunded, and will be revisited on a named date.
Retirement Modelling
In practice: you convert the target retirement income into a required portfolio, check it against what the client will actually hold, and then work out the monthly contribution that closes the difference.
Retirement modelling is the heart of the FNA and the part most often done badly.
The calculation is:
required corpus = annual target income × AF(horizon, real drawdown return)
For the Chan household, targeting 70% of living expenses to age 85 at 3.5% real:
| 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 60: HK$1,850,000 savings + HK$180,000 MPF = HK$2,030,000. Retirement gap: HK$11,260,693 — which is HK$56,936 a month of real income for 25 years that does not currently exist.
Note the effect of the replacement ratio: moving from 70% to 50% cuts the required corpus by roughly 29%, because the corpus is linear in the target. The replacement ratio is therefore the most powerful single variable in the whole plan, and it is the one most often chosen by default rather than by discussion.
Now solve for the contribution required to close it, at a 3.5% real return from age 42 to retirement:
| Saving rate on gross income | Annual | Monthly | Sustainable retirement income | % 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% |
The client's 70% target requires saving 25% of gross income for eighteen years. Run through the full projection with the retirement targets included, the required annual contribution is HK$1,455,493 (HK$121,291 a month) — against a household that is currently in deficit.
That number is arithmetically correct and practically useless as a recommendation. This is the moment where a competent advisor moves from solving the model to negotiating the plan, and the levers below are the whole content of that conversation.
| Lever | Effect on required contribution | Realistic in HK? |
|---|---|---|
| Retire at 65 instead of 60 | 1,455,493 → 865,033, a fall of HK$590,459/yr | Yes — the statutory retirement age is rising to 65 |
| Retire at 62 | 1,187,669 (−HK$267,824) | Yes |
| Lower replacement ratio to 60% | 1,282,700 (−HK$172,793) | Yes, normal |
| Raise drawdown return to 4.5% real | 1,281,483 (−HK$174,010) | No — this is fitting the answer |
| Keep working part-time 60–65 | Income plus extra saving years | Increasingly common |
| Downsize after the mortgage clears | Releases ~HK$10.9m net | Yes, and often the biggest lever |
The last row of that table is the one to watch. Raising the assumed return to make the plan work is not planning — it is reverse engineering the desired answer. It is also the single most common way an FNA becomes indefensible, because it makes the plan's success conditional on a market outcome nobody controls. The correct response to "the plan does not work at my assumed return" is to change the inputs you actually control: contribution, retirement age, replacement ratio, spending, housing.
The housing lever deserves specific mention because Hong Kong property dominates household balance sheets. If the household owns a flat with net equity released on downsizing, that released equity belongs in the retirement model — but only in the downsizing scenario, never in the base case, or the same square feet is counted twice.
Sensitivity Analysis
In practice: you show the client which two assumptions the answer actually depends on, so they know what to worry about and what not to worry about.
Sensitivity analysis is not a disclaimer. It is the most useful part of the FNA for a client deciding whether to act, because it converts an apparently arbitrary output into a set of trade-offs they can choose within.
Run the required contribution across the four variables that actually move the number.
| Variable | Setting | Required annual contribution | vs base |
|---|---|---|---|
| — | base: retire 60, 70% replacement, 3.5% real | 1,455,493 | — |
| Retirement age | 58 | 1,784,753 | +329,260 |
| Retirement age | 62 | 1,187,669 | −267,824 |
| Retirement age | 65 | 865,033 | −590,459 |
| Real return at retirement | 2.5% | 1,663,702 | +208,210 |
| Real return at retirement | 4.5% | 1,281,483 | −174,010 |
| Replacement ratio | 80% of living | 1,628,286 | +172,793 |
| Replacement ratio | 60% of living | 1,282,700 | −172,793 |
Two things fall out of this table that an advisor should say out loud.
Retirement age dominates the return assumption. Moving from 60 to 65 changes the requirement by −HK$590,459 a year. Moving the real return from 2.5% to 4.5% changes it by −HK$174,010. The behavioural lever is worth more than a heroic return assumption, and it is under- valued by almost every client.
The plan is not fragile to the return assumption, but it is fragile to the retirement age. Across a 2% swing in real return the requirement moves by about 14%. Across a 5-year swing in retirement age it moves by about 40%. So the risk to manage is not market risk — it is the client's retirement decision.
A client who retires at 63 instead of 65 has a materially worse plan than one who retires on time but earns one point less per year.
Corpus sensitivity (the other half of the analysis) for the 70% target to age 85:
| Real return | Spend 60% (691k) | Spend 70% (806k) | Spend 80% (922k) | Spend 100% (1,152k) |
|---|---|---|---|---|
| 2.5% | 12,734,929 | 14,857,417 | 16,979,905 | 21,224,882 |
| 3.0% | 12,035,968 | 14,041,962 | 16,047,957 | 20,059,946 |
| 3.5% | 11,392,023 | 13,290,693 | 15,189,364 | 18,986,705 |
| 4.0% | 10,797,982 | 12,597,645 | 14,397,309 | 17,996,636 |
| 4.5% | 10,249,258 | 11,957,468 | 13,665,677 | 17,082,097 |
And the planning horizon:
| Retire at | Corpus to fund the same income to 85 | vs age 60 |
|---|---|---|
| 57 | 11,723,834 | +786,701 |
| 58 | 11,470,568 | +533,435 |
| 60 | 10,937,133 | — |
| 62 | 10,365,704 | −571,429 |
| 65 | 9,431,351 | −1,505,782 |
Sequence of returns. The final piece of sensitivity, and the one clients most need to understand, is that returns do not arrive evenly. A 25-year drawdown with a flat 11.5% nominal return and a 5,000 a year withdrawal ends at 902,664. The same 25 years with the same average return, but sequenced as three good years, one bad year and one mediocre year, ends at 291,797 — because the withdrawals fall hardest when the balance is smallest.
| Return pattern over 25 years | Ending balance (start 100,000, 5,000/yr drawn) |
|---|---|
| Flat 11.5% | 902,664 |
| 3 good, 1 bad (−2%), 1 mediocre (6%) | 291,797 |
| Flat 6% | 154,865 |
| All bad (−2%) | −38,787 |
Two good early years do most of the work. This is why a retirement portfolio should never be 100% equities with no cash buffer, and why the first three years after retirement deserve separate treatment in the plan — typically a higher cash or fixed-income allocation, then a glide path back.
Here is the sensitivity runner.
export interface SensitivityVariable {
name: 'retirementAge' | 'retReturn' | 'replacementRatio' | 'horizonAge' | 'contribution';
values: number[];
}
export interface SensitivityRun {
variable: string;
value: number;
requiredAnnualContribution: number;
requiredCorpusAtRetirement: number;
sustainableAnnualIncome: number;
firstNegativeBalanceYear: number | null;
}
/**
* Required contribution that keeps the portfolio non-negative to the horizon.
* Monotonic in contribution, so bisection is both safe and fast.
*/
export function requiredContribution(
p: ProjectionParams,
overrides: Partial<ProjectionParams> = {}
): number {
const base = { ...p, ...overrides };
const survives = (contribution: number): boolean => {
const r = simulate({ ...base, extraContribution: contribution });
return r.firstNegativeBalanceYear === null;
};
if (survives(0)) return 0;
let lo = 0;
let hi = Math.max(p.members.reduce((a, m) => a + m.netIncome, 0), 1);
// Double until it survives, so we never cap the search at an arbitrary ceiling.
let guard = 0;
while (!survives(hi) && guard++ < 40) hi *= 2;
for (let i = 0; i < 60; i++) {
const mid = (lo + hi) / 2;
if (survives(mid)) hi = mid;
else lo = mid;
}
return hi;
}
export function runSensitivity(
p: ProjectionParams,
variables: SensitivityVariable[]
): SensitivityRun[] {
const out: SensitivityRun[] = [];
for (const v of variables) {
for (const value of v.values) {
const overrides: Partial<ProjectionParams> = {};
switch (v.name) {
case 'retirementAge':
overrides.members = p.members.map((m) => ({
...m,
retirementAge: value,
}));
break;
case 'retReturn':
overrides.drawdownReturn = value;
break;
case 'replacementRatio':
overrides.horizonSpendingRatio = value;
break;
case 'horizonAge':
break; // handled by shortening the horizon below
case 'contribution':
overrides.extraContribution = value;
break;
}
const base = { ...p, ...overrides };
const horizonYear =
v.name === 'horizonAge'
? p.members[0].birthYear + value
: p.horizonYear;
const required = requiredContribution(base, {
horizonYear,
} as Partial<ProjectionParams>);
// Corpus actually reached at retirement under that contribution.
const run = simulate({ ...base, horizonYear, extraContribution: required });
const retirementYear = Math.min(
...base.members.map((m) => m.birthYear + m.retirementAge)
);
const atRetirement =
run.rows.find((r) => r.year === retirementYear)?.portfolioClosing ?? 0;
out.push({
variable: v.name,
value,
requiredAnnualContribution: required,
requiredCorpusAtRetirement: atRetirement,
sustainableAnnualIncome: sustainableIncome(
atRetirement,
retirementYear,
horizonYear,
base.drawdownReturn
),
firstNegativeBalanceYear: run.firstNegativeBalanceYear,
});
}
}
return out;
}
/** Income a corpus can pay, in today's money, until the horizon. */
export function sustainableIncome(
corpus: number,
fromYear: number,
toYear: number,
realReturn: number
): number {
const years = Math.max(0, toYear - fromYear);
if (years === 0) return 0;
const factor = af(years, realReturn);
return factor === 0 ? 0 : corpus / factor;
}
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;
}
One thing to notice: requiredContribution doubles its search ceiling until the plan
survives, rather than assuming a maximum.
That prevents the silent failure mode where a genuinely unaffordable plan returns the
artificial ceiling and the advisor presents HK$4 million a year as if it were a result.
Presenting the FNA to the Client
In practice: you walk the client through the document in the order it was built — situation, goal, gap, plan, sensitivity — and you get written acknowledgement on the guaranteed-versus-projected pages before you discuss any product.
The presentation is where an accurate document is won or lost.
Present the failing base case first. This feels counterintuitive and it is the correct move. If you show the client their plan succeeding before showing them how close it is to failing, every subsequent number is read as reassurance. If you show them the base case failing first — portfolio exhausted 2043 under current behaviour, 2048 under the recommended plan — then every subsequent number is read as progress. The psychology of the second framing is the difference between a client who acts and a client who agrees politely and does nothing.
Then present the levers in the order of size, not in the order of convenience. Retirement age first, housing second, replacement ratio third, contribution last. If you lead with the contribution number the client will say no to it immediately, because it is the largest ask and the least controllable. If you lead with retirement age — where the model shows a HK$590,459 a year improvement from a decision that costs nothing — the client is already engaged on a topic where the answer is favourable.
Show the sensitivity table and ask the client to choose a point in it. Do not ask "shall we proceed?" which invites a yes/no. Ask "which of these five futures would you like to have?" which invites a decision. This is a small technique with a large effect on close rates, and it is honest: the client genuinely is choosing.
Disclose guaranteed versus projected before discussing products. Not after the client has become emotionally committed to an illustrated number. A client who has already anchored on HK$4,607,603 and is then shown the guaranteed column of HK$1,360,000 will not merely be disappointed — they will suspect the figures were switched to pressure them.
Record the acknowledgement. The acknowledgement must be specific:
| Statement | Captured by | When |
|---|---|---|
| Client understands the retirement gap figure | Signed on the FNA | At issue |
| Client understands retirement age is the largest lever | Initials on the sensitivity page | At issue |
| Client understands non-guaranteed values may be zero | Signed on the illustration page | Before product discussion |
| Client has chosen the retirement age assumption | Signed on the assumption page | At issue |
What to do when the plan cannot be made to work. Say so. Write so. Record the date you will revisit and what would have to change. Some of the best FNA files in an agency are the ones that recommend no product this year, with a documented reason and a review date — because they are the ones that produce a client who is still there in five years' time.
Key Takeaways
-
An FNA is a document, not a conversation. Eleven sections in a fixed order, because a consistent structure is what makes two FNAs comparable and one FNA reviewable.
-
Never mix real and nominal figures. With 3% inflation, a 6.5% nominal return is 3.5% real. Combining 3% expense inflation with a 6.5% discount rate overstates the plan by roughly 40%; carry
basisas a validated field. -
Apply returns to the opening balance, then add surplus. Applying the return to the closing balance compounds one year late, and the error is invisible in a spreadsheet and enormous over 40 years.
-
Model the retirement cliff in phases. Accumulation, a two-year transition where the second earner still works part time, then drawdown. Killing both incomes in the same year produces needlessly alarming and inaccurate projections.
-
Report a base case that fails. The Chan household's portfolio is exhausted in 2043 under current behaviour and 2048 under the recommended plan — before the age-85 horizon. Showing that plainly is what makes the rest of the document credible.
-
A gap is only useful once it is bridged. Each lever is a change to a modelled input, re-run through the same engine, and recorded with its signed dollar effect, its residual gap, and whether the client accepted it. Rank the levers the client controls above the larger lever the market controls — and note that increasing the sum assured closes no part of a cash-flow gap, so a retirement shortfall is never answered with more life cover.
-
Fees and tax are inside the return assumption, never added later. An ILAS earning a 1.5% total expense ratio does not return the fund's return.
-
Always present the guaranteed-only column beside the illustrated column. On the HK$1m/20-year illustration the gap between them ranges from HK$67,047 to HK$3,247,603 depending on how the return is split — and the client must see both before discussing products.
-
Retirement age dominates return assumptions, and the client must choose a point in the sensitivity table. A 5-year delay changes the required contribution by about −40%; a 2-point swing in real return changes it by about −14%, so steer the conversation to the larger lever. Then ask "which of these futures would you like?" rather than "shall we proceed?" — the first produces a decision, the second a yes or no. And never raise the assumed return to make a plan work: that is reverse-engineering the desired answer and makes success dependent on a market outcome nobody controls.