Hong Kong Share Award Tax — DIPN 38 Partial Exemption
When a Hong Kong taxpayer transfers from a non-HK employer to a HK employer mid-vesting, share awards granted under the non-HK employment are only partially taxable in HK. This skill produces:
- An IR56B vs Assessment cross-check — detect which tax years have unclaimed exemptions
- A DIPN 38 computation PDF in the IRD-accepted "APPENDIX-EQUITY" format
- An HTML audit report for personal review (CC Pages)
- The eTAX BIR60 Appendix Section 4 filing guidance
- An IR831 / Section 70A retrospective claim package (for years where exemption was missed)
When to trigger
Strong triggers:
- User mentions HK salaries tax + share awards / GSU / RSU / stock options
- User mentions "DIPN 38" / "partial exemption" / "section 8(1A)(a)"
- User mentions transfer between non-HK and HK entities (Google Shanghai → Google HK, etc.)
- User uploads a stock statement / IR56B / IRD assessment and asks about HK tax
- Chinese: "报税分析", "股票减税", "GSU 豁免", "地域来源", "从内地转来香港", "70A", "股票报税"
Do NOT use for:
- Non-HK tax jurisdictions
- Pure HK employment with no cross-border element
- Carried Interest (BIRSP4 — different form)
Core algorithm
Legal basis: DIPN No. 38 ¶43–46 + section 8(1A)(a) IRO.
Days in HK during vesting period
Net Assessable HKD = Net Amount × ─────────────────────────────────────
Total days in vesting period
Where:
- Total days =
vest_date − grant_date(Python.days) - Outside-HK days =
hk_employment_start − grant_date(when grant < hk_start) - HK days = total − outside
Decision rules:
| Grant date | Treatment |
|---|---|
| Before HK employment start | Apportion (this skill's main work) |
| On or after HK employment start | 100% assessable (no apportionment) |
Critical data rules (verified against IRD-accepted filings):
- Use
Total Income of GSUs at Vest, NOTProrated Income(Prorated is employer-internal) - Use HK jurisdiction row's FX rate (
7.7), NOT China row's PBOC rate (7.1) - Use floor() (truncate) for final integer, not round() — IRD convention
- Google IR56B reports GSU at full amount (no geographic split) — taxpayer must self-claim
Workflow
Phase 1: Gather inputs
Collect via dialogue; auto-fetch where possible:
| Material | Source | Required |
|---|---|---|
| Morgan Stanley Stock Statement | User sends PDF/CSV, or Chrome MCP from atwork.morganstanley.com | Yes |
| IR56B (each tax year) | User sends PDF, or Payday system | Yes |
| IRD Assessment notices | Chrome MCP from itp.etax.ird.gov.hk, or user sends PDF | Yes |
| HK employment start date | User states / IR56B period / Vialto docs | Yes |
| Taxpayer name & IRD file no. | IR56B / assessment notice | Yes |
| Phone number | User states (for IR831) | Only for 70A |
| Previous year's tax return | User / Vialto | Optional (algorithm verification) |
Auto-fetch via Chrome MCP (if user has logged in):
- Morgan Stanley: Activity → Reports → "Your Alphabet Stock Statement" → All history / USD / CSV → Run Report → fetch download link
- IRD: itp.etax.ird.gov.hk → 税務狀況 → 評税 → 檢視 → presign URL → fetch PDF
Phase 2: Extract vest data from stock statement
For Alphabet/Google statements, see references/extracting_alphabet_statement.md.
Key rules:
- Take Hong Kong jurisdiction rows only (skip China rows — different FX rate)
- Extract:
grant_date,vest_date,shares(GSUs Vested),fmv_usd(Fair Market Value),fx_rate - Filter to the target year of assessment (vest_date ∈ [YA_start, YA_end])
- Output CSV for
scripts/compute.py
Phase 3: Cross-check IR56B vs Assessment (detect unclaimed exemptions)
This is the diagnostic step — determines whether to file a new claim or a retrospective 70A correction.
For each assessed tax year:
IF IR56B Total = Assessment income → ❌ No exemption claimed (problem year)
IF IR56B Total > Assessment income → ✅ Exemption was claimed (OK year)
Verify: difference ≈ independently computed CN-source amount (within 2%)
Phase 4: Verify algorithm against prior year (optional but recommended)
If user provided a previous year's return with APPENDIX-EQUITY:
- Re-run
compute.pyon last year's data - Compare each row's
assessable_hkdto the filed figure - Must match within HK$ 0.01 — if not, stop and investigate
Phase 5: Run computation
Build config YAML + vests CSV, then:
python3 scripts/compute.py --config config.yaml --vests vests.csv --output result.json
Validates all vests fall within YA window, computes per-vest apportionment, aggregates totals, runs sanity checks.
Phase 6: Generate outputs
APPENDIX-EQUITY PDF (for eTAX submission):
python3 scripts/gen_pdf.py --result result.json --output appendix_equity.pdf
Multi-page PDF: main table → DIPN 38 detail blocks → Itinerary appendix.
HTML audit report (for personal review):
python3 scripts/gen_tax_report.py \
--analysis analysis.json \
--ir56b '{"2024/25":{"gsu":604498,"total":3305748}}' \
--assessment '{"2024/25":3305748}' \
--taxpayer "NAME" --file-no "FILE_NO" \
--output report.html
Publish via publish-cc-page.sh.
Phase 7: Action guidance
Situation A — Current year filing (not yet filed):
- Guide eTAX BIR60 filing per references/etax_filing.md
- Enable "Apply for partial exemption" in Step 2 → Box 30 = IR56B total → Box 34 = excluded amount → Section 4 attach PDF
Situation B — Past year where exemption was missed (70A retrospective):
- Guide IR831 + Section 70A letter per references/ir831-guide.md
- IR831 Item 2 + Item 25, attach APPENDIX-EQUITY PDF + IR56B + employment contract
- Deadline: 6 years after end of tax year
Situation C — All years correctly claimed:
- Confirm no action needed; remind to continue claiming until all pre-transfer grants vest
Reference files
- references/dipn38.md — DIPN 38 ¶43–46 verbatim + interpretation
- references/etax_filing.md — Full eTAX BIR60 filing walkthrough (Box-by-Box)
- references/ir831-guide.md — IR831 filling guide + 70A letter template
- references/extracting_alphabet_statement.md — Morgan Stanley statement parsing
- references/workflow-checklist.md — Material checklist + decision tree
Scripts
scripts/compute.py— Core DIPN 38 calculation engine (YAML+CSV → JSON)scripts/gen_pdf.py— APPENDIX-EQUITY PDF generator (reportlab)scripts/gen_tax_report.py— HTML audit report generator (Material Design)scripts/parse_ms_statement.py— Morgan Stanley CSV auto-parser (bulk extraction)scripts/example/— Working example with anonymized data
Common pitfalls
| Pitfall | Fix |
|---|---|
Used Prorated Income instead of Total Income |
Always use Total Income of GSUs at Vest (gross) |
| Used China row's FX rate (~7.1) | Use HK row's FX rate (~7.7) |
| Used round() instead of floor() | Use int() (truncate) — IRD convention |
| Filed BIR-SP4 form | Wrong form — use BIR60 Appendix Section 4 |
| Selected wrong eTAX grounds | Always pick "Non-Hong Kong employment" section 8(1A)(a) |
| Forgot to enable Box 34 in eTAX | Must tick "Apply for partial exemption" in Step 2 first |
| Forgot to attach PDF | The PDF is the proof; without it IRD may reject |
| Assumed Google IR56B splits by geography | Google reports full GSU amount — you must self-claim |
| Assumed Prorated = what to report | Prorated is employer-internal; IRD wants gross + your own apportionment |
| Didn't check if a tax agent already claimed | Check Vialto/PwC/Deloitte first — avoid duplication |