- Authors
- Name
- What is the Core-Satellite Strategy
- Separating the Roles of Core and Satellite
- Portfolio Design in Practice: 3 Scenarios
- Implementing Automated Rebalancing
- Correlation Management: Maintaining Diversification Benefits
- Core-Satellite Annual Operations Calendar
- Quiz
- References

What is the Core-Satellite Strategy
Core-Satellite is an asset allocation strategy that divides the portfolio into two roles. The Core consists of low-cost index ETFs that track the overall market, pursuing stable returns. The Satellite consists of specific sector, theme, and style ETFs, aiming for excess returns (alpha).
This strategy sits between 100% index investing and 100% active investing. It is also the essence of the personal investor asset allocation proposed by David Swensen (Yale endowment manager) in Unconventional Success.
Core Principles of Core-Satellite:
- Core weight 60-80%, Satellite weight 20-40%
- Core is rarely touched (rebalancing 1-2 times per year)
- Satellites can be swapped based on market conditions (reviewed quarterly)
- Keep total portfolio expense ratio (TER) below 0.3%
Separating the Roles of Core and Satellite
Core: Capturing Market Beta
The core's objective is simple: obtaining market returns at the lowest possible cost. It doesn't try to "beat the market."
Core ETF Selection Criteria:
- Expense ratio 0.1% or lower (fees are a guaranteed negative return)
- AUM of 100 billion KRW or more (ensuring liquidity)
- Minimal Tracking Error (low divergence from the index)
- Dividend reinvestment structure (prefer Total Return index tracking)
Core ETF Candidates Available in Korea (as of 2026):
| ETF | Code | Tracking Index | Expense | AUM | Purpose |
|---|---|---|---|---|---|
| TIGER US S&P500 | 360750 | S&P 500 | 0.07% | 6.5T KRW | US large-cap |
| KODEX US S&P500TR | 379800 | S&P 500 TR | 0.05% | 3.2T KRW | US large-cap (dividend reinvest) |
| KODEX 200 | 069500 | KOSPI 200 | 0.05% | 5.8T KRW | Korean large-cap |
| TIGER US NASDAQ100 | 133690 | NASDAQ-100 | 0.07% | 4.1T KRW | US tech stocks |
| KODEX US Treasury Ultra 30Y Futures (H) | 304660 | US Treasury 30Y | 0.09% | 280B KRW | US long-term bonds |
| TIGER US Treasury 10Y Futures | 305080 | US Treasury 10Y | 0.09% | 1.2T KRW | US mid-term bonds |
| KODEX Composite Bond (AA- and above) Active | 443160 | Korean bonds | 0.05% | 350B KRW | Domestic bonds |
Satellite: Pursuing Themes and Alpha
Satellites concentrate on specific sectors, regions, or styles to seek excess returns beyond the market. Unlike core holdings, they can be swapped, and if conviction is low, their weight can be reduced or held in cash.
Types of Satellite ETFs:
| Type | Examples | Characteristics | Risk Level |
|---|---|---|---|
| Sector | Semiconductors, batteries, biotech | Focused on specific industry | High |
| Theme | AI, robotics, clean energy | Following growth trends | High |
| Region | India, Japan, Vietnam | Specific country growth | Medium-High |
| Style | High dividend, value, small-cap | Factor-based investing | Medium |
| Alternative | Gold, commodities, REITs | Low correlation, diversified | Medium |
Satellite Replacement Decision Criteria:
- Is the investment thesis still valid?
- What was the performance vs benchmark last quarter?
- Has a similar ETF with lower fees been launched?
- Has the correlation within the portfolio become excessively high?
Portfolio Design in Practice: 3 Scenarios
Scenario 1: Stable Growth (Conservative Investor)
# Target: 6-8% annual return, max drawdown within 15%
# For: 10-15 years before retirement, principal preservation priority
portfolio:
name: 'Stable Growth'
core_weight: 0.80
satellite_weight: 0.20
core:
- ticker: 'KODEX US S&P500TR'
weight: 0.30
role: 'US market beta'
- ticker: 'KODEX 200'
weight: 0.15
role: 'Korean market beta'
- ticker: 'TIGER US Treasury 10Y Futures'
weight: 0.20
role: 'Defense during rate declines'
- ticker: 'KODEX Composite Bond (AA- and above) Active'
weight: 0.15
role: 'Stable interest income'
satellite:
- ticker: 'TIGER Gold & Silver Futures (H)'
weight: 0.10
role: 'Inflation hedge'
- ticker: 'TIGER US Dividend Dow Jones'
weight: 0.10
role: 'Dividend income'
expected:
annual_return: '6-8%'
max_drawdown: '-15%'
total_expense_ratio: '0.07%'
Scenario 2: Growth-Oriented (30s Office Worker)
# Target: 10-12% annual return, willing to accept 25% max drawdown
# For: 15+ year investment horizon, aggressive growth
portfolio:
name: 'Growth-Oriented'
core_weight: 0.65
satellite_weight: 0.35
core:
- ticker: 'TIGER US S&P500'
weight: 0.35
role: 'US market beta'
- ticker: 'TIGER US NASDAQ100'
weight: 0.15
role: 'Tech stock growth'
- ticker: 'KODEX 200'
weight: 0.10
role: 'Korean market beta'
- ticker: 'TIGER US Treasury 10Y Futures'
weight: 0.05
role: 'Minimum bond allocation'
satellite:
- ticker: 'TIGER Semiconductor'
weight: 0.12
role: 'Semiconductor cycle alpha'
- ticker: 'KODEX Secondary Battery Industry'
weight: 0.08
role: 'Secondary battery growth'
- ticker: 'TIGER Fn Semiconductor TOP10'
weight: 0.08
role: 'Korean semiconductor concentration'
- ticker: 'TIGER India Nifty50'
weight: 0.07
role: 'India market growth'
expected:
annual_return: '10-12%'
max_drawdown: '-25%'
total_expense_ratio: '0.15%'
Scenario 3: All-Weather (Volatility Minimization)
# Target: Pursue positive returns in any market environment
# For: Investors who extremely dislike volatility
# Reference: Variation of Ray Dalio's All Weather strategy
portfolio:
name: 'All-Weather'
core_weight: 0.85
satellite_weight: 0.15
core:
- ticker: 'TIGER US S&P500'
weight: 0.25
role: 'Returns during economic growth'
- ticker: 'TIGER US Treasury 10Y Futures'
weight: 0.25
role: 'Recession defense'
- ticker: 'KODEX US Treasury Ultra 30Y Futures (H)'
weight: 0.15
role: 'Deflation defense'
- ticker: 'KODEX Composite Bond (AA- and above) Active'
weight: 0.10
role: 'Stable interest'
- ticker: 'TIGER Gold & Silver Futures (H)'
weight: 0.10
role: 'Inflation hedge'
satellite:
- ticker: 'KODEX 200'
weight: 0.10
role: 'Korean market exposure'
- ticker: 'TIGER Crude Oil Futures Enhanced (H)'
weight: 0.05
role: 'Commodity inflation response'
expected:
annual_return: '5-7%'
max_drawdown: '-10%'
total_expense_ratio: '0.09%'
Implementing Automated Rebalancing
Separate Rebalancing for Core vs Satellite
Core and satellite have different rebalancing cycles and criteria. They should not be combined under a single rule.
"""
Core-Satellite Separate Rebalancing Engine
- Core: Twice a year + emergency when drift exceeds 5%
- Satellite: Quarterly + emergency when drift exceeds 7%
"""
from dataclasses import dataclass
@dataclass
class RebalanceRule:
"""Rebalancing rules by asset type."""
section: str # "core" or "satellite"
scheduled_frequency: str # "semi-annual" or "quarterly"
drift_threshold: float # Drift threshold
min_trade_krw: int # Minimum trade amount
RULES = {
"core": RebalanceRule(
section="core",
scheduled_frequency="semi-annual",
drift_threshold=0.05,
min_trade_krw=200_000,
),
"satellite": RebalanceRule(
section="satellite",
scheduled_frequency="quarterly",
drift_threshold=0.07,
min_trade_krw=100_000,
),
}
def check_section(
section: str,
holdings: dict,
targets: list[dict],
total_value: int,
) -> dict:
"""Determine if a specific section needs rebalancing."""
rule = RULES[section]
drift_alerts = []
for target in targets:
code = target["code"]
current_value = holdings.get(code, {}).get("value_krw", 0)
current_weight = current_value / total_value if total_value > 0 else 0
target_weight = target["weight"]
drift = abs(current_weight - target_weight)
if drift > rule.drift_threshold:
drift_alerts.append({
"ticker": target["ticker"],
"code": code,
"target": target_weight,
"current": round(current_weight, 4),
"drift": round(drift, 4),
"action_needed": True,
})
return {
"section": section,
"rule": rule,
"needs_rebalancing": len(drift_alerts) > 0,
"alerts": drift_alerts,
}
def run_core_satellite_rebalance(portfolio_config: dict, holdings: dict):
"""Execute rebalancing for core and satellite separately."""
total_value = sum(h["value_krw"] for h in holdings.values())
print(f"Total assets: ₩{total_value:,}\n")
for section in ["core", "satellite"]:
targets = portfolio_config[section]
result = check_section(section, holdings, targets, total_value)
section_label = "Core" if section == "core" else "Satellite"
print(f"=== {section_label} Check ===")
print(f"Rebalancing frequency: {result['rule'].scheduled_frequency}")
print(f"Drift threshold: ±{result['rule'].drift_threshold * 100}%")
if result["needs_rebalancing"]:
print(f"Status: Rebalancing needed")
for alert in result["alerts"]:
print(
f" - {alert['ticker']}: "
f"Target {alert['target']*100:.1f}% / "
f"Current {alert['current']*100:.1f}% / "
f"Drift {alert['drift']*100:.1f}%p"
)
else:
print("Status: Within normal range")
print()
# Usage example
config = {
"core": [
{"ticker": "TIGER US S&P500", "code": "360750", "weight": 0.35},
{"ticker": "KODEX 200", "code": "069500", "weight": 0.15},
{"ticker": "TIGER US Treasury 10Y Futures", "code": "305080", "weight": 0.15},
],
"satellite": [
{"ticker": "TIGER Semiconductor", "code": "091230", "weight": 0.12},
{"ticker": "KODEX Secondary Battery Industry", "code": "305720", "weight": 0.08},
{"ticker": "TIGER India Nifty50", "code": "453810", "weight": 0.07},
],
}
holdings = {
"360750": {"value_krw": 4_500_000},
"069500": {"value_krw": 1_500_000},
"305080": {"value_krw": 1_200_000},
"091230": {"value_krw": 1_800_000},
"305720": {"value_krw": 600_000},
"453810": {"value_krw": 400_000},
}
run_core_satellite_rebalance(config, holdings)
Satellite Replacement Logic
Satellites are reviewed quarterly for performance, and holdings with weakening investment theses are replaced.
"""Satellite ETF performance review and replacement decision."""
def evaluate_satellite(
ticker: str,
quarter_return: float,
benchmark_return: float,
thesis_valid: bool,
cheaper_alternative: str | None = None,
) -> dict:
"""Determine whether a satellite ETF should be replaced."""
excess_return = quarter_return - benchmark_return
recommendation = "HOLD"
reasons = []
# If the investment thesis is invalidated
if not thesis_valid:
recommendation = "REPLACE"
reasons.append("Investment thesis is no longer valid")
# If underperforming benchmark for 3 consecutive quarters
if excess_return < -0.05:
recommendation = "REVIEW"
reasons.append(
f"Underperforming benchmark by {excess_return*100:.1f}%p"
)
# If a cheaper alternative exists
if cheaper_alternative:
reasons.append(f"Lower-fee alternative available: {cheaper_alternative}")
return {
"ticker": ticker,
"quarter_return": f"{quarter_return*100:.1f}%",
"excess_return": f"{excess_return*100:.1f}%p",
"recommendation": recommendation,
"reasons": reasons,
}
# Quarterly review example
reviews = [
evaluate_satellite(
ticker="TIGER Semiconductor",
quarter_return=0.12,
benchmark_return=0.08,
thesis_valid=True,
),
evaluate_satellite(
ticker="KODEX Secondary Battery Industry",
quarter_return=-0.03,
benchmark_return=0.08,
thesis_valid=True,
),
evaluate_satellite(
ticker="TIGER India Nifty50",
quarter_return=0.06,
benchmark_return=0.08,
thesis_valid=True,
cheaper_alternative="KODEX India Nifty50 (Synthetic)",
),
]
print("=== Satellite Quarterly Review ===")
for r in reviews:
print(f"\n{r['ticker']}")
print(f" Quarterly return: {r['quarter_return']}")
print(f" Excess return: {r['excess_return']}")
print(f" Verdict: {r['recommendation']}")
if r["reasons"]:
for reason in r["reasons"]:
print(f" - {reason}")
Correlation Management: Maintaining Diversification Benefits
One pitfall of the core-satellite strategy is when satellites become highly correlated with each other. Holding both a semiconductor ETF and a NASDAQ100 ETF simultaneously effectively results in excessive concentration in tech stocks.
Correlation Monitoring
"""Calculate ETF correlations within a portfolio."""
import numpy as np
def check_correlation(returns_matrix: dict, threshold: float = 0.8) -> list:
"""Find ETF pairs with high correlation.
Args:
returns_matrix: {ticker: [daily return list]}
threshold: Warning threshold for correlation coefficient
Returns:
List of ETF pairs with correlation exceeding the threshold
"""
tickers = list(returns_matrix.keys())
data = np.array([returns_matrix[t] for t in tickers])
corr = np.corrcoef(data)
high_corr_pairs = []
for i in range(len(tickers)):
for j in range(i + 1, len(tickers)):
if abs(corr[i][j]) > threshold:
high_corr_pairs.append({
"pair": (tickers[i], tickers[j]),
"correlation": round(corr[i][j], 3),
"warning": "Weakened diversification - consider reducing one position",
})
return high_corr_pairs
# Example: 60-day daily returns (simulation)
np.random.seed(42)
market = np.random.normal(0.0005, 0.01, 60)
sample_returns = {
"S&P500": market + np.random.normal(0, 0.002, 60),
"NASDAQ100": market * 1.3 + np.random.normal(0, 0.003, 60),
"Semiconductor": market * 1.5 + np.random.normal(0, 0.005, 60),
"US Treasury 10Y": -market * 0.3 + np.random.normal(0, 0.003, 60),
"Gold": np.random.normal(0.0002, 0.008, 60),
}
alerts = check_correlation(sample_returns, threshold=0.7)
print("=== Correlation Alerts ===")
for alert in alerts:
print(f" {alert['pair'][0]} <-> {alert['pair'][1]}: "
f"Correlation {alert['correlation']}")
print(f" -> {alert['warning']}")
Core-Satellite Annual Operations Calendar
| Month | Core | Satellite | Other |
|---|---|---|---|
| January | Scheduled rebalancing | Quarterly review + annual strategy | Annual return settlement, tax review |
| Feb-Mar | Drift monitoring | - | Ex-dividend date check |
| April | - | Quarterly review | ISA/IRP contribution check |
| May-Jun | Drift monitoring | - | Mid-year review |
| July | Scheduled rebalancing | Quarterly review | H2 strategy adjustment |
| Aug-Sep | Drift monitoring | - | - |
| October | - | Quarterly review + next year plan | ISA/IRP year-end contribution plan |
| Nov-Dec | Drift monitoring | - | Tax-optimized trading (overseas ETF) |
Quiz
Q1. What is the role of the core in a core-satellite strategy?
Answer: To capture overall market returns (beta) at low cost. It doesn't try to beat the market, but pursues stable returns through index ETFs.
Q2. Why do core and satellite have different rebalancing cycles?
Answer: Core follows a long-term holding principle, so semi-annual scheduled rebalancing is sufficient. Satellites can be swapped based on market conditions, so they are reviewed quarterly. Applying management cycles appropriate to each character reduces unnecessary trades.
Q3. What is the most important criterion for deciding whether to replace a satellite ETF?
Answer: Whether the investment thesis is still valid. Replacing based only on short-term underperformance leads to emotional trading, while holding despite an invalidated thesis expands losses.
Q4. Why is high correlation between ETFs in a portfolio a problem?
Answer: Holding multiple highly correlated assets eliminates diversification benefits. For example, holding S&P500 + NASDAQ100 + Semiconductor ETFs simultaneously means all three drop together during a tech stock decline, causing significant damage to the entire portfolio.
Q5. Why does the All-Weather portfolio hold stocks, bonds, and gold simultaneously?
Answer: It is designed so that some assets generate returns in any environment: economic growth (stocks rise), recession (bonds rise), inflation (gold rises), deflation (long-term bonds rise). This is the core principle of Ray Dalio's All Weather strategy.
Q6. Why is expense ratio (TER) the most important factor when selecting core ETFs?
Answer: Core ETFs are held long-term (10+ years), so expenses compound over time. The difference between 0.05% and 0.5% in expense ratio creates approximately 9% return difference over 20 years. Since core tracks market returns, lower expenses mean higher real returns.
References
- Swensen, David. Unconventional Success: A Fundamental Approach to Personal Investment. Free Press, 2005.
- Dalio, Ray. All Weather Strategy Principles: https://www.bridgewater.com/
- Bogleheads Wiki - Core and Satellite: https://www.bogleheads.org/wiki/Core_and_satellite
- Investopedia - Core-Satellite Investing: https://www.investopedia.com/terms/c/core-satellite.asp
- Korea Exchange ETF Information: https://www.krx.co.kr/
- ETF CHECK: https://www.etfcheck.co.kr/