- Authors
- Name

- Why Use ISA and IRP for ETF Investing
- ISA System Key Summary (as of 2026)
- IRP System Key Summary (as of 2026)
- ISA + IRP Integrated Asset Allocation Strategy
- Automation Workflow Design
- ISA/IRP Rebalancing Execution Considerations
- ISA vs IRP: Which to Invest in First -- Priority Decision
- ISA Maturity Conversion Strategy
- Full Workflow Automation Architecture
- Quiz
- References
Why Use ISA and IRP for ETF Investing
Taxes are the most certain enemy of investment returns. No matter how good your returns are, after-tax real returns shrink significantly. The two most powerful tools that individual investors in Korea can legally use to reduce taxes are the ISA (Individual Savings Account) and the IRP (Individual Retirement Pension).
Integrating these two accounts into an ETF automated rebalancing workflow allows you to achieve both tax savings and rule-based investing simultaneously. As John Bogle emphasized in The Little Book of Common Sense Investing, "Reducing costs (including taxes) is the most reliable way to increase returns."
ISA System Key Summary (as of 2026)
What is ISA?
ISA is a tax-advantaged account that can hold various financial products such as deposits, funds, ETFs, and REITs within a single account. Income generated within the account receives tax-free benefits up to a certain amount.
ISA Type Comparison
| Item | General ISA | Low-Income ISA | Brokerage ISA |
|---|---|---|---|
| Eligibility | Residents aged 19+ | Gross salary under 50M KRW or total income under 38M KRW | Residents aged 19+ |
| Annual contribution | 20M KRW/year (100M KRW total) | 20M KRW/year (100M KRW total) | 20M KRW/year (100M KRW total) |
| Tax-free limit | 2M KRW | 4M KRW | 2M KRW |
| Tax on excess | 9.9% flat rate | 9.9% flat rate | 9.9% flat rate |
| Mandatory holding | 3 years | 3 years | 3 years |
| Direct ETF trading | Not available | Not available | Available |
| Overseas ETF | Not available | Not available | Not available (domestic listed only) |
Key Point: To invest directly in ETFs, you must choose a Brokerage ISA. General/Low-Income types are trust-based and managed by the securities firm, making individual ETF trading impossible.
ISA Tax Savings Simulation
"""Compare after-tax returns between ISA and regular accounts."""
def compare_tax_effect(
investment: int,
annual_return: float,
years: int,
isa_type: str = "general", # "general" or "disadvantaged"
) -> dict:
"""Compare after-tax returns between ISA and regular accounts.
Args:
investment: Annual investment amount (KRW)
annual_return: Annual return rate (e.g., 0.08)
years: Investment period (years)
isa_type: ISA type ("general": General/Brokerage, "disadvantaged": Low-Income)
"""
# ISA tax-free limit
tax_free_limit = 2_000_000 if isa_type == "general" else 4_000_000
isa_excess_tax_rate = 0.099 # 9.9% flat rate
# Regular account dividend income tax
normal_tax_rate = 0.154 # 15.4%
# ISA account (tax-free + flat rate)
isa_total_invested = 0
isa_balance = 0
for year in range(1, years + 1):
isa_total_invested += investment
isa_balance = (isa_balance + investment) * (1 + annual_return)
isa_total_profit = isa_balance - isa_total_invested
if isa_total_profit <= tax_free_limit:
isa_tax = 0
else:
isa_tax = int((isa_total_profit - tax_free_limit) * isa_excess_tax_rate)
isa_after_tax = isa_balance - isa_tax
# Regular account (15.4% tax on annual profits)
normal_balance = 0
normal_total_tax = 0
for year in range(1, years + 1):
normal_balance += investment
yearly_profit = normal_balance * annual_return
yearly_tax = int(yearly_profit * normal_tax_rate)
normal_total_tax += yearly_tax
normal_balance = normal_balance + yearly_profit - yearly_tax
tax_saving = normal_total_tax - isa_tax
return {
"Investment Period": f"{years} years",
"Total Invested": f"₩{isa_total_invested:,}",
"ISA Maturity Amount": f"₩{int(isa_after_tax):,}",
"ISA Tax Paid": f"₩{isa_tax:,}",
"Regular Account Maturity": f"₩{int(normal_balance):,}",
"Regular Account Tax Paid": f"₩{normal_total_tax:,}",
"Tax Savings": f"₩{tax_saving:,}",
"Return Difference": f"₩{int(isa_after_tax - normal_balance):,}",
}
# Scenario: 20M KRW annual investment, 8% annual return, 3 years
result = compare_tax_effect(
investment=20_000_000,
annual_return=0.08,
years=3,
isa_type="general",
)
print("=== ISA vs Regular Account Comparison ===")
for key, value in result.items():
print(f" {key}: {value}")
IRP System Key Summary (as of 2026)
What is IRP?
IRP is a retirement pension account that manages both severance pay and personal additional contributions. Combined with pension savings, you can receive tax deductions of up to 9 million KRW annually, and returns generated during the management period are tax-deferred.
IRP Key Figures
| Item | Details |
|---|---|
| Annual contribution | 18M KRW (including pension savings) |
| Tax deduction limit | 9M KRW (pension savings 6M + IRP 3M) |
| Tax deduction rate | Gross salary 55M or under: 16.5% / Over: 13.2% |
| Maximum tax deduction | 1.485M KRW (salary 55M or under) / 1.188M KRW (over) |
| Risky asset limit | 70% of accumulated funds (including equity ETFs) |
| Safe asset requirement | At least 30% (bond ETFs, deposits, etc.) |
| Withdrawal condition | Pension payments after age 55 (10+ year installments) |
| Pension income tax | 3.3-5.5% (16.5% other income tax for lump sum) |
Constraints When Investing in ETFs Through IRP
IRP is a pension account, so free trading is restricted.
Investable:
- Domestically listed ETFs (equity, bond, hybrid)
- TDF (Target Date Fund)
- Bond/MMF ETFs (safe assets)
Not investable:
- Leveraged/Inverse ETFs
- ETFs with over 40% derivatives exposure
- Overseas-listed ETFs (domestically listed overseas index ETFs are allowed)
70% Risky Asset Rule Example:
- If IRP balance is 9M KRW: max 6.3M KRW in equity ETFs, min 2.7M KRW in bond ETFs
ISA + IRP Integrated Asset Allocation Strategy
Role Assignment by Account
To maximize tax efficiency, it's important to decide which assets go into which accounts.
| Asset Type | Optimal Account | Reason |
|---|---|---|
| High-dividend ETFs | ISA | Dividend income tax-free up to 2-4M KRW |
| Growth stock ETFs (capital gains) | ISA | Domestic ETF capital gains tax-free + ISA additional savings |
| Bond ETFs | IRP | Meets IRP 30% safe asset requirement + interest tax deferral |
| Overseas index tracking ETFs | IRP | Dividend income tax-deferred (3.3-5.5% at pension receipt) |
| High-risk theme ETFs | ISA | IRP has 70% risky asset limit constraint |
Integrated Portfolio Example: Employee with 60M KRW Annual Salary
# Integrated asset allocation settings
investor:
name: 'Kim Youngju'
annual_salary: 60_000_000
tax_deduction_rate: 0.132 # Over 55M KRW -> 13.2%
accounts:
isa:
type: 'Brokerage'
annual_contribution: 20_000_000 # 20M KRW/year
tax_free_limit: 2_000_000
# Assets for ISA: dividend + growth + theme
holdings:
- ticker: 'TIGER US S&P500'
weight: 0.35
role: 'Core - US market'
- ticker: 'KODEX 200'
weight: 0.15
role: 'Core - Korean market'
- ticker: 'TIGER Semiconductor'
weight: 0.15
role: 'Satellite - Semiconductor'
- ticker: 'TIGER US Dividend Dow Jones'
weight: 0.15
role: 'Satellite - Dividend'
- ticker: 'KODEX Secondary Battery Industry'
weight: 0.10
role: 'Satellite - Secondary battery'
- ticker: 'TIGER Gold & Silver Futures (H)'
weight: 0.10
role: 'Satellite - Gold'
irp:
annual_contribution: 9_000_000 # 9M KRW/year (tax deduction limit)
tax_deduction: 1_188_000 # 9M x 13.2%
risk_asset_limit: 0.70
# Assets for IRP: overseas index + bonds (maximize tax deferral)
holdings:
risk_assets: # Within 70%
- ticker: 'TIGER US S&P500'
weight: 0.35
role: 'Core - US market (dividend tax deferral)'
- ticker: 'TIGER US NASDAQ100'
weight: 0.20
role: 'Core - Tech stock growth'
- ticker: 'KODEX 200'
weight: 0.15
role: 'Core - Korean market'
safe_assets: # At least 30%
- ticker: 'KODEX Composite Bond (AA- and above) Active'
weight: 0.15
role: 'Safe asset - Domestic bonds'
- ticker: 'TIGER US Treasury 10Y Futures'
weight: 0.15
role: 'Safe asset - US bonds'
general:
# Additional investments beyond the two accounts above
holdings:
- ticker: 'TIGER US S&P500'
weight: 0.50
- ticker: 'KODEX 200'
weight: 0.30
- ticker: 'TIGER US Treasury 10Y Futures'
weight: 0.20
Automation Workflow Design
Annual Calendar and Automated Alerts
ISA and IRP have annual contribution limits and tax deduction caps, so missing deadlines means losing tax benefits.
"""ISA/IRP annual workflow automated alerts."""
from datetime import date
ANNUAL_TASKS = [
{
"month": 1,
"tasks": [
"Establish ISA annual contribution plan (1.67M KRW/month or lump sum)",
"Establish IRP annual contribution plan (750K KRW/month or lump sum)",
"Verify previous year tax deduction amount (year-end settlement reflection)",
"Core ETF scheduled rebalancing",
],
},
{
"month": 3,
"tasks": [
"Verify ISA Q1 contributions (target: 5M KRW cumulative)",
"Verify IRP Q1 contributions (target: 2.25M KRW cumulative)",
"ISA portfolio rebalancing (if drift exceeds 5%)",
],
},
{
"month": 4,
"tasks": [
"Satellite ETF quarterly performance review",
"Verify IRP risky asset ratio (within 70%)",
],
},
{
"month": 6,
"tasks": [
"Verify ISA H1 cumulative contributions (target: 10M KRW)",
"Verify IRP H1 cumulative contributions (target: 4.5M KRW)",
"Full portfolio mid-year review",
],
},
{
"month": 7,
"tasks": [
"Core ETF scheduled rebalancing (semi-annual)",
"Satellite ETF quarterly performance review",
],
},
{
"month": 10,
"tasks": [
"Check ISA annual contribution remaining amount",
"Check IRP tax deduction limit remaining",
"Plan contribution completion by year-end",
"Satellite ETF quarterly review + next year strategy development",
],
},
{
"month": 11,
"tasks": [
"Additional IRP contribution if below tax deduction limit",
"Additional ISA contribution if below annual limit",
"Utilize overseas ETF capital gains 2.5M KRW deduction (review year-end sales)",
],
},
{
"month": 12,
"tasks": [
"Confirm ISA, IRP contributions completed by 12/31",
"Summarize annual investment performance",
"Develop next year asset allocation strategy",
"If ISA 3-year maturity is approaching, decide on extension/termination",
],
},
]
def get_current_tasks() -> list:
"""Return the task list for the current month."""
current_month = date.today().month
for item in ANNUAL_TASKS:
if item["month"] == current_month:
return item["tasks"]
return ["No scheduled tasks this month. Perform drift monitoring only."]
def get_contribution_status(
isa_ytd: int,
irp_ytd: int,
isa_annual_target: int = 20_000_000,
irp_annual_target: int = 9_000_000,
) -> dict:
"""Check annual contribution progress."""
today = date.today()
year_progress = today.timetuple().tm_yday / 365
return {
"ISA": {
"YTD Contributions": f"₩{isa_ytd:,}",
"Annual Target": f"₩{isa_annual_target:,}",
"Achievement Rate": f"{isa_ytd / isa_annual_target * 100:.1f}%",
"Year Progress": f"{year_progress * 100:.1f}%",
"Status": "On Track" if isa_ytd / isa_annual_target >= year_progress * 0.8 else "Behind Schedule",
},
"IRP": {
"YTD Contributions": f"₩{irp_ytd:,}",
"Annual Target": f"₩{irp_annual_target:,}",
"Achievement Rate": f"{irp_ytd / irp_annual_target * 100:.1f}%",
"Expected Tax Deduction": f"₩{int(min(irp_ytd, irp_annual_target) * 0.132):,}",
"Status": "On Track" if irp_ytd / irp_annual_target >= year_progress * 0.8 else "Behind Schedule",
},
}
# Execution example
print("=== March Tasks ===")
for task in get_current_tasks():
print(f" - {task}")
print("\n=== Contribution Status ===")
status = get_contribution_status(isa_ytd=4_000_000, irp_ytd=1_500_000)
for account, info in status.items():
print(f"\n{account}:")
for key, value in info.items():
print(f" {key}: {value}")
ISA/IRP Rebalancing Execution Considerations
ISA Account Rebalancing
Selling and buying ETFs within an ISA account is free. Since these are intra-account transactions, no immediate taxation on capital gains occurs (settled in bulk at maturity).
"""Special logic for ISA account rebalancing."""
def isa_rebalance_check(
holdings: dict,
targets: dict,
total_value: int,
tax_free_used: int,
tax_free_limit: int = 2_000_000,
) -> dict:
"""Check ISA account rebalancing feasibility and tax impact.
Args:
holdings: {code: {"value": current_value, "cost": purchase_cost}}
targets: {code: target_weight}
total_value: Total account balance
tax_free_used: Tax-free limit already used (KRW)
tax_free_limit: Tax-free limit (KRW)
"""
trades = []
total_realized_gain = 0
for code, target_weight in targets.items():
holding = holdings.get(code, {"value": 0, "cost": 0})
current_weight = holding["value"] / total_value if total_value > 0 else 0
diff_weight = target_weight - current_weight
if abs(diff_weight) < 0.02: # Ignore deviation under 2%
continue
trade_amount = int(total_value * abs(diff_weight))
action = "BUY" if diff_weight > 0 else "SELL"
# Calculate realized gains on sell
realized_gain = 0
if action == "SELL" and holding["value"] > holding["cost"]:
gain_ratio = (holding["value"] - holding["cost"]) / holding["value"]
realized_gain = int(trade_amount * gain_ratio)
total_realized_gain += realized_gain
trades.append({
"code": code,
"action": action,
"amount": trade_amount,
"realized_gain": realized_gain,
})
# Check tax-free limit
remaining_tax_free = tax_free_limit - tax_free_used
taxable_gain = max(0, total_realized_gain - remaining_tax_free)
tax_on_excess = int(taxable_gain * 0.099) # 9.9% flat rate
return {
"trades": trades,
"total_realized_gain": total_realized_gain,
"remaining_tax_free": remaining_tax_free,
"taxable_gain": taxable_gain,
"estimated_tax": tax_on_excess,
"recommendation": (
"Rebalancing executable (within tax-free limit)"
if taxable_gain == 0
else f"Tax-free limit exceeded: 9.9% tax on ₩{taxable_gain:,} (₩{tax_on_excess:,})"
),
}
# Usage example
result = isa_rebalance_check(
holdings={
"360750": {"value": 8_000_000, "cost": 6_500_000},
"069500": {"value": 3_000_000, "cost": 2_800_000},
"091230": {"value": 4_000_000, "cost": 3_200_000},
},
targets={"360750": 0.40, "069500": 0.20, "091230": 0.20},
total_value=15_000_000,
tax_free_used=500_000,
)
print("=== ISA Rebalancing Check ===")
print(f"Expected realized gains: ₩{result['total_realized_gain']:,}")
print(f"Remaining tax-free limit: ₩{result['remaining_tax_free']:,}")
print(f"Verdict: {result['recommendation']}")
IRP Account Rebalancing
IRP has a 70% risky asset limit, so this ratio must be verified after rebalancing.
"""IRP account rebalancing with risky asset ratio verification."""
# Asset classification criteria
RISK_ASSET_CODES = {"360750", "133690", "069500", "091230", "305720"}
SAFE_ASSET_CODES = {"443160", "305080", "304660"}
def irp_risk_check(
holdings: dict,
max_risk_ratio: float = 0.70,
) -> dict:
"""Check the risky asset ratio of the IRP portfolio."""
risk_value = sum(
h["value_krw"] for code, h in holdings.items()
if code in RISK_ASSET_CODES
)
safe_value = sum(
h["value_krw"] for code, h in holdings.items()
if code in SAFE_ASSET_CODES
)
total = risk_value + safe_value
risk_ratio = risk_value / total if total > 0 else 0
safe_ratio = safe_value / total if total > 0 else 0
compliant = risk_ratio <= max_risk_ratio
return {
"total_value": f"₩{total:,}",
"risk_assets": f"₩{risk_value:,} ({risk_ratio*100:.1f}%)",
"safe_assets": f"₩{safe_value:,} ({safe_ratio*100:.1f}%)",
"risk_limit": f"{max_risk_ratio*100:.0f}%",
"compliant": compliant,
"action": (
"Risky asset ratio within limits"
if compliant
else f"Risky asset ratio exceeded: {risk_ratio*100:.1f}% > {max_risk_ratio*100:.0f}%. "
f"Need to convert ₩{int(risk_value - total * max_risk_ratio):,} to safe assets."
),
}
# Usage example
irp_result = irp_risk_check({
"360750": {"value_krw": 3_500_000}, # S&P500 (risky)
"133690": {"value_krw": 1_800_000}, # NASDAQ100 (risky)
"069500": {"value_krw": 1_200_000}, # KODEX200 (risky)
"443160": {"value_krw": 1_500_000}, # Composite Bond (safe)
"305080": {"value_krw": 1_000_000}, # US Treasury 10Y (safe)
})
print("=== IRP Risky Asset Ratio Check ===")
for key, value in irp_result.items():
print(f" {key}: {value}")
ISA vs IRP: Which to Invest in First -- Priority Decision
When you have limited investment funds, should you contribute to ISA or IRP first?
Investment Priority Decision Tree
1. Contribute 3-9M KRW annually to IRP (maximize tax deduction)
- Tax deduction effect: Immediate 13.2-16.5% guaranteed return
- Note: Cannot withdraw until age 55 (liquidity constraint)
2. Contribute up to 20M KRW annually to ISA
- Can include funds needed within 3 years
- Tax-free 2-4M KRW + 9.9% flat rate on excess
3. Regular account
- Amounts exceeding ISA/IRP limits
- Funds requiring liquidity
Optimal Allocation by Salary Level
| Annual Salary | IRP Contribution | ISA Contribution | Regular Account | Annual Tax Savings |
|---|---|---|---|---|
| 40M KRW | 9M KRW | 11M KRW | - | IRP deduction 1.485M + ISA tax-free |
| 60M KRW | 9M KRW | 20M KRW | Surplus | IRP deduction 1.188M + ISA tax-free |
| 80M KRW | 9M KRW | 20M KRW | Surplus | IRP deduction 1.188M + ISA tax-free |
| 100M KRW | 9M KRW | 20M KRW | Surplus | IRP deduction 1.188M + ISA tax-free |
Key: Regardless of salary, always fill the IRP 9M KRW. Tax deductions are a guaranteed return (13.2-16.5%) independent of investment performance. No return is more certain.
ISA Maturity Conversion Strategy
After the ISA 3-year maturity, there are two options.
Option 1: Extend Maturity
- Keep the account and continue investing
- Tax-free limit does not reset every 3 years (cumulative)
- Can continue rebalancing under tax-free/flat-rate benefits
Option 2: Terminate and Transfer to IRP/Pension Savings
- Transfer all or part of ISA maturity proceeds to IRP/Pension Savings
- 10% of the transferred amount (up to 3M KRW) is eligible for additional tax deduction
- Example: ISA 30M KRW maturity transferred to IRP = 3M KRW additional tax deduction = 396K-495K KRW tax savings
"""Calculate tax benefits when transferring ISA maturity to IRP."""
def isa_to_irp_benefit(
isa_balance: int,
max_additional_deduction: int = 3_000_000,
tax_rate: float = 0.132,
) -> dict:
"""Calculate additional tax deduction when transferring ISA maturity to IRP."""
eligible_amount = min(isa_balance, isa_balance) # Assume full transfer
deduction_base = min(int(eligible_amount * 0.1), max_additional_deduction)
additional_tax_saving = int(deduction_base * tax_rate)
return {
"ISA Maturity Balance": f"₩{isa_balance:,}",
"IRP Transfer Amount": f"₩{eligible_amount:,}",
"Additional Deduction Base": f"₩{deduction_base:,}",
"Additional Tax Savings": f"₩{additional_tax_saving:,}",
"Note": "Transfer is separate from the existing IRP annual contribution limit (18M KRW)",
}
result = isa_to_irp_benefit(isa_balance=30_000_000)
print("=== ISA to IRP Transfer Benefits ===")
for key, value in result.items():
print(f" {key}: {value}")
Full Workflow Automation Architecture
┌─────────────────────────────────────────────────────┐
│ GitHub Actions (Scheduler) │
│ Every Monday 09:00 KST -> Drift check │
│ First business day of quarter -> Scheduled rebalance │
│ 1st of each month -> Contribution status check │
└────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Python Rebalancing Engine │
│ 1. Query ISA/IRP/general account balances via API │
│ 2. Compare YAML config with current weights │
│ 3. Determine rebalancing needs per account │
│ - ISA: Check tax-free limit │
│ - IRP: Check 70% risky asset limit │
│ 4. Generate trade orders (dry-run) │
└────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Notifications (Slack / KakaoTalk) │
│ - When rebalancing needed: trade details + cost est │
│ - When contributions behind: remaining + recommended │
│ - Tax deduction deadline approaching: Nov-Dec alert │
└─────────────────────────────────────────────────────┘
Quiz
Q1. Which type of ISA should you choose to invest directly in ETFs?
Answer: You must choose a Brokerage ISA. General/Low-Income types are trust-based and managed by the securities firm, making individual ETF trading impossible.
Q2. What is the maximum percentage of equity ETFs you can invest in through IRP?
Answer: Up to 70% of accumulated funds can be invested in risky assets (including equity ETFs). The remaining 30% or more must consist of safe assets such as bond ETFs and deposits.
Q3. How much tax deduction does an employee with a 60M KRW salary receive by contributing 9M KRW to IRP?
Answer: Since the gross salary exceeds 55M KRW, a 13.2% deduction rate applies. 9M KRW x 13.2% = 1.188M KRW tax deduction.
Q4. What additional benefit can you receive by transferring ISA maturity (3 years) to IRP?
Answer: You can receive additional tax deductions on 10% of the transferred amount (up to 3M KRW). For example, transferring 30M KRW gives a 3M KRW deduction base, resulting in 13.2% (396K KRW) or 16.5% (495K KRW) additional tax savings.
Q5. Why is it advantageous to put high-dividend ETFs in ISA rather than IRP?
Answer: In ISA, dividend income is tax-free if within the tax-free limit (2-4M KRW). IRP only defers taxes and charges pension income tax of 3.3-5.5% upon receipt. If dividend amounts are within the tax-free limit, ISA has an absolute tax advantage.
Q6. Why is the IRP tax deduction described as a "guaranteed return"?
Answer: Tax deductions are a confirmed 13.2-16.5% tax refund immediately upon contribution, regardless of investment performance. While stock investment returns are uncertain, tax deductions are definitively returned through year-end settlement, making them effectively the most certain return.
Q7. Why should investment priority be given to IRP over ISA?
Answer: The IRP tax deduction (13.2-16.5%) is an immediately confirmed return, while ISA's tax-free benefit only has value when profits are generated. When investment funds are limited, it's rational to fill the IRP tax deduction limit (9M KRW) first and allocate the remainder to ISA.
References
- Bogle, John. The Little Book of Common Sense Investing. Wiley, 2017.
- Financial Supervisory Service - ISA Guide: https://www.fss.or.kr/
- National Tax Service - Pension Savings/IRP Tax Deductions: https://www.nts.go.kr/
- Korea Investment & Securities KIS Developers API: https://apiportal.koreainvestment.com/
- Korea Financial Investment Association - Retirement Pension System Guide: https://www.kofia.or.kr/
- ETF CHECK: https://www.etfcheck.co.kr/
- Korea Exchange ETF Information: https://www.krx.co.kr/