Split View: 코어-새틀라이트 ETF 자동 리밸런싱
코어-새틀라이트 ETF 자동 리밸런싱
- 코어-새틀라이트 전략이란
- 코어와 새틀라이트의 역할 분리
- 실전 포트폴리오 설계: 3가지 시나리오
- 자동 리밸런싱 구현
- 상관관계 관리: 분산 효과 유지
- 코어-새틀라이트 운영 연간 달력
- 퀴즈
- 참고 자료

코어-새틀라이트 전략이란
코어-새틀라이트(Core-Satellite)는 포트폴리오를 두 가지 역할로 나누는 자산 배분 전략이다. **코어(Core)**는 시장 전체를 추종하는 저비용 인덱스 ETF로 구성해 안정적인 수익을 추구하고, **새틀라이트(Satellite)**는 특정 섹터, 테마, 스타일 ETF로 구성해 초과 수익(알파)을 노린다.
이 전략은 100% 인덱스 투자와 100% 액티브 투자의 중간점이다. David Swensen(예일대 기금 운용)이 Unconventional Success에서 제안한 개인 투자자용 자산 배분의 핵심이기도 하다.
코어-새틀라이트의 기본 원칙:
- 코어 비중 60-80%, 새틀라이트 비중 20-40%
- 코어는 거의 손대지 않는다 (연 1-2회 리밸런싱)
- 새틀라이트는 시장 상황에 따라 교체 가능 (분기 단위 검토)
- 전체 포트폴리오 보수율(TER)을 0.3% 이하로 유지
코어와 새틀라이트의 역할 분리
코어: 시장 베타를 확보한다
코어의 목표는 단순하다. 시장 수익률을 가장 낮은 비용으로 얻는 것이다. "시장을 이기려" 하지 않는다.
코어 ETF 선정 기준:
- 운용 보수 0.1% 이하 (보수는 확실한 마이너스 수익)
- 순자산 1,000억원 이상 (유동성 확보)
- 추적 오차(Tracking Error) 최소 (지수와 괴리 적음)
- 배당 재투자 구조 (TR 지수 추종 우선)
한국에서 쓸 수 있는 코어 ETF 후보 (2026년 기준):
| ETF | 종목코드 | 추종 지수 | 보수 | 순자산 | 용도 |
|---|---|---|---|---|---|
| TIGER 미국S&P500 | 360750 | S&P 500 | 0.07% | 6.5조원 | 미국 대형주 |
| KODEX 미국S&P500TR | 379800 | S&P 500 TR | 0.05% | 3.2조원 | 미국 대형주 (배당 재투자) |
| KODEX 200 | 069500 | KOSPI 200 | 0.05% | 5.8조원 | 한국 대형주 |
| TIGER 미국나스닥100 | 133690 | NASDAQ-100 | 0.07% | 4.1조원 | 미국 기술주 |
| KODEX 미국채울트라30년선물(H) | 304660 | US Treasury 30Y | 0.09% | 2,800억원 | 미국 장기채 |
| TIGER 미국채10년선물 | 305080 | US Treasury 10Y | 0.09% | 1.2조원 | 미국 중기채 |
| KODEX 종합채권(AA-이상)액티브 | 443160 | 국내 우량 채권 | 0.05% | 3,500억원 | 국내 채권 |
새틀라이트: 테마와 알파를 추구한다
새틀라이트는 특정 섹터, 지역, 스타일에 집중 투자해 시장 대비 초과 수익을 노린다. 코어와 달리 교체가 가능하며, 확신이 없으면 비중을 줄이거나 현금으로 유지할 수 있다.
새틀라이트 ETF 유형:
| 유형 | 예시 | 특징 | 위험도 |
|---|---|---|---|
| 섹터 | 반도체, 2차전지, 바이오 | 특정 산업에 집중 | 높음 |
| 테마 | AI, 로봇, 클린에너지 | 성장 트렌드 추종 | 높음 |
| 지역 | 인도, 일본, 베트남 | 특정 국가 성장 | 중간-높음 |
| 스타일 | 고배당, 가치주, 소형주 | 팩터 기반 투자 | 중간 |
| 대체자산 | 금, 원자재, 리츠 | 상관관계 낮음, 분산 효과 | 중간 |
새틀라이트 교체 판단 기준:
- 투자 논리(thesis)가 여전히 유효한가?
- 지난 분기 벤치마크 대비 성과는?
- 더 낮은 보수의 유사 ETF가 출시되었는가?
- 포트폴리오 내 상관관계가 과도하게 높아지지 않았는가?
실전 포트폴리오 설계: 3가지 시나리오
시나리오 1: 안정 성장형 (보수적 투자자)
# 목표: 연 6-8% 수익, 최대 낙폭 15% 이내
# 대상: 은퇴 10-15년 전, 원금 보존 중시
portfolio:
name: '안정 성장형'
core_weight: 0.80
satellite_weight: 0.20
core:
- ticker: 'KODEX 미국S&P500TR'
weight: 0.30
role: '미국 시장 베타'
- ticker: 'KODEX 200'
weight: 0.15
role: '한국 시장 베타'
- ticker: 'TIGER 미국채10년선물'
weight: 0.20
role: '금리 하락 시 방어'
- ticker: 'KODEX 종합채권(AA-이상)액티브'
weight: 0.15
role: '안정적 이자 수익'
satellite:
- ticker: 'TIGER 금은선물(H)'
weight: 0.10
role: '인플레이션 헤지'
- ticker: 'TIGER 미국배당다우존스'
weight: 0.10
role: '배당 수익'
expected:
annual_return: '6-8%'
max_drawdown: '-15%'
total_expense_ratio: '0.07%'
시나리오 2: 성장 추구형 (30대 직장인)
# 목표: 연 10-12% 수익, 최대 낙폭 25% 감수
# 대상: 투자 기간 15년 이상, 적극적 성장 추구
portfolio:
name: '성장 추구형'
core_weight: 0.65
satellite_weight: 0.35
core:
- ticker: 'TIGER 미국S&P500'
weight: 0.35
role: '미국 시장 베타'
- ticker: 'TIGER 미국나스닥100'
weight: 0.15
role: '기술주 성장'
- ticker: 'KODEX 200'
weight: 0.10
role: '한국 시장 베타'
- ticker: 'TIGER 미국채10년선물'
weight: 0.05
role: '최소 채권 배분'
satellite:
- ticker: 'TIGER 반도체'
weight: 0.12
role: '반도체 사이클 알파'
- ticker: 'KODEX 2차전지산업'
weight: 0.08
role: '2차전지 성장'
- ticker: 'TIGER Fn반도체TOP10'
weight: 0.08
role: '한국 반도체 집중'
- ticker: 'TIGER 인도니프티50'
weight: 0.07
role: '인도 시장 성장'
expected:
annual_return: '10-12%'
max_drawdown: '-25%'
total_expense_ratio: '0.15%'
시나리오 3: 올웨더형 (변동성 최소화)
# 목표: 어떤 시장 환경에서도 양의 수익 추구
# 대상: 변동성을 극도로 싫어하는 투자자
# 참고: Ray Dalio의 All Weather 전략 변형
portfolio:
name: '올웨더형'
core_weight: 0.85
satellite_weight: 0.15
core:
- ticker: 'TIGER 미국S&P500'
weight: 0.25
role: '경제 성장기 수익'
- ticker: 'TIGER 미국채10년선물'
weight: 0.25
role: '경기 침체 방어'
- ticker: 'KODEX 미국채울트라30년선물(H)'
weight: 0.15
role: '디플레이션 방어'
- ticker: 'KODEX 종합채권(AA-이상)액티브'
weight: 0.10
role: '안정적 이자'
- ticker: 'TIGER 금은선물(H)'
weight: 0.10
role: '인플레이션 헤지'
satellite:
- ticker: 'KODEX 200'
weight: 0.10
role: '한국 시장 노출'
- ticker: 'TIGER 원유선물Enhanced(H)'
weight: 0.05
role: '원자재 인플레이션 대응'
expected:
annual_return: '5-7%'
max_drawdown: '-10%'
total_expense_ratio: '0.09%'
자동 리밸런싱 구현
코어 vs 새틀라이트 분리 리밸런싱
코어와 새틀라이트는 리밸런싱 주기와 기준이 다르다. 하나의 규칙으로 묶지 않는다.
"""
코어-새틀라이트 분리 리밸런싱 엔진
- 코어: 연 2회 + 편차 5% 초과 시 긴급
- 새틀라이트: 분기 1회 + 편차 7% 초과 시 긴급
"""
from dataclasses import dataclass
@dataclass
class RebalanceRule:
"""자산 유형별 리밸런싱 규칙."""
section: str # "core" or "satellite"
scheduled_frequency: str # "semi-annual" or "quarterly"
drift_threshold: float # 편차 임계치
min_trade_krw: int # 최소 거래 금액
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:
"""특정 섹션의 리밸런싱 필요 여부를 판단한다."""
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):
"""코어와 새틀라이트를 분리하여 각각 리밸런싱을 실행한다."""
total_value = sum(h["value_krw"] for h in holdings.values())
print(f"총 자산: ₩{total_value:,}\n")
for section in ["core", "satellite"]:
targets = portfolio_config[section]
result = check_section(section, holdings, targets, total_value)
section_label = "코어" if section == "core" else "새틀라이트"
print(f"=== {section_label} 점검 ===")
print(f"리밸런싱 주기: {result['rule'].scheduled_frequency}")
print(f"편차 임계치: ±{result['rule'].drift_threshold * 100}%")
if result["needs_rebalancing"]:
print(f"상태: 리밸런싱 필요")
for alert in result["alerts"]:
print(
f" - {alert['ticker']}: "
f"목표 {alert['target']*100:.1f}% / "
f"현재 {alert['current']*100:.1f}% / "
f"편차 {alert['drift']*100:.1f}%p"
)
else:
print("상태: 정상 범위 내")
print()
# 사용 예시
config = {
"core": [
{"ticker": "TIGER 미국S&P500", "code": "360750", "weight": 0.35},
{"ticker": "KODEX 200", "code": "069500", "weight": 0.15},
{"ticker": "TIGER 미국채10년선물", "code": "305080", "weight": 0.15},
],
"satellite": [
{"ticker": "TIGER 반도체", "code": "091230", "weight": 0.12},
{"ticker": "KODEX 2차전지산업", "code": "305720", "weight": 0.08},
{"ticker": "TIGER 인도니프티50", "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)
새틀라이트 교체 로직
새틀라이트는 분기마다 성과를 검토하고, 투자 논리가 약해진 종목을 교체한다.
"""새틀라이트 ETF 성과 검토 및 교체 판단."""
def evaluate_satellite(
ticker: str,
quarter_return: float,
benchmark_return: float,
thesis_valid: bool,
cheaper_alternative: str | None = None,
) -> dict:
"""새틀라이트 ETF의 교체 필요 여부를 판단한다."""
excess_return = quarter_return - benchmark_return
recommendation = "HOLD"
reasons = []
# 투자 논리가 무효화된 경우
if not thesis_valid:
recommendation = "REPLACE"
reasons.append("투자 논리(thesis)가 더 이상 유효하지 않음")
# 3분기 연속 벤치마크 대비 부진한 경우
if excess_return < -0.05:
recommendation = "REVIEW"
reasons.append(
f"벤치마크 대비 {excess_return*100:.1f}%p 부진"
)
# 더 저렴한 대안이 있는 경우
if cheaper_alternative:
reasons.append(f"보수가 더 낮은 대안 존재: {cheaper_alternative}")
return {
"ticker": ticker,
"quarter_return": f"{quarter_return*100:.1f}%",
"excess_return": f"{excess_return*100:.1f}%p",
"recommendation": recommendation,
"reasons": reasons,
}
# 분기 검토 예시
reviews = [
evaluate_satellite(
ticker="TIGER 반도체",
quarter_return=0.12,
benchmark_return=0.08,
thesis_valid=True,
),
evaluate_satellite(
ticker="KODEX 2차전지산업",
quarter_return=-0.03,
benchmark_return=0.08,
thesis_valid=True,
),
evaluate_satellite(
ticker="TIGER 인도니프티50",
quarter_return=0.06,
benchmark_return=0.08,
thesis_valid=True,
cheaper_alternative="KODEX 인도Nifty50(합성)",
),
]
print("=== 새틀라이트 분기 검토 ===")
for r in reviews:
print(f"\n{r['ticker']}")
print(f" 분기 수익률: {r['quarter_return']}")
print(f" 초과 수익: {r['excess_return']}")
print(f" 판정: {r['recommendation']}")
if r["reasons"]:
for reason in r["reasons"]:
print(f" - {reason}")
상관관계 관리: 분산 효과 유지
코어-새틀라이트 전략의 함정 중 하나는 새틀라이트끼리 상관관계가 높아지는 것이다. 반도체 ETF와 나스닥100 ETF를 동시에 보유하면 실질적으로 기술주에 과도하게 집중된다.
상관관계 모니터링
"""포트폴리오 내 ETF 상관관계를 계산한다."""
import numpy as np
def check_correlation(returns_matrix: dict, threshold: float = 0.8) -> list:
"""높은 상관관계를 가진 ETF 쌍을 찾는다.
Args:
returns_matrix: {종목코드: [일별 수익률 리스트]}
threshold: 경고 기준 상관계수
Returns:
상관계수가 threshold를 넘는 ETF 쌍 목록
"""
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": "분산 효과 약화 - 한쪽 비중 축소 검토",
})
return high_corr_pairs
# 예시: 60일 일별 수익률 (시뮬레이션)
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),
"반도체": market * 1.5 + np.random.normal(0, 0.005, 60),
"미국채10년": -market * 0.3 + np.random.normal(0, 0.003, 60),
"금": np.random.normal(0.0002, 0.008, 60),
}
alerts = check_correlation(sample_returns, threshold=0.7)
print("=== 상관관계 경고 ===")
for alert in alerts:
print(f" {alert['pair'][0]} <-> {alert['pair'][1]}: "
f"상관계수 {alert['correlation']}")
print(f" -> {alert['warning']}")
코어-새틀라이트 운영 연간 달력
| 월 | 코어 | 새틀라이트 | 기타 |
|---|---|---|---|
| 1월 | 정기 리밸런싱 | 분기 검토 + 연간 전략 점검 | 연간 수익률 정산, 세금 확인 |
| 2-3월 | 편차 모니터링 | - | 배당 기준일 확인 |
| 4월 | - | 분기 검토 | ISA/IRP 납입 상황 점검 |
| 5-6월 | 편차 모니터링 | - | 중간 점검 |
| 7월 | 정기 리밸런싱 | 분기 검토 | 하반기 전략 조정 |
| 8-9월 | 편차 모니터링 | - | - |
| 10월 | - | 분기 검토 + 내년 전략 수립 | ISA/IRP 연말 납입 계획 |
| 11-12월 | 편차 모니터링 | - | 세금 최적화 매매 (해외 ETF) |
퀴즈
Q1. 코어-새틀라이트 전략에서 코어의 역할은?
정답: ||시장 전체 수익률(베타)을 저비용으로 확보하는 것이다. 시장을 이기려 하지 않고, 인덱스 ETF로
안정적 수익을 추구한다.||
Q2. 코어와 새틀라이트의 리밸런싱 주기가 다른 이유는?
정답: ||코어는 장기 보유가 원칙이므로 연 1-2회 정기 리밸런싱이면 충분하다. 새틀라이트는 시장
상황에 따라 교체가 가능하므로 분기 단위로 검토한다. 각각의 성격에 맞는 관리 주기를 적용해야
불필요한 매매를 줄일 수 있다.||
Q3. 새틀라이트 ETF 교체를 판단하는 가장 중요한 기준은?
정답: ||투자 논리(thesis)가 여전히 유효한가이다. 단기 성과 부진만으로 교체하면 감정적 매매가 되고,
투자 논리가 무효화되었는데도 보유하면 손실이 확대된다.||
Q4. 포트폴리오 내 ETF 상관관계가 높으면 왜 문제인가?
정답: ||상관관계가 높은 자산을 여러 개 보유하면 분산 효과가 사라진다. 예를 들어 S&P500 + NASDAQ100
- 반도체 ETF를 동시에 보유하면 기술주 하락 시 세 종목이 동시에 하락하여 포트폴리오 전체가 큰 타격을 받는다.||
Q5. 올웨더형 포트폴리오가 주식, 채권, 금을 동시에 보유하는 이유는?
정답: ||경제 성장기(주식 상승), 경기 침체기(채권 상승), 인플레이션기(금 상승), 디플레이션기(장기채
상승) 등 어떤 환경에서도 일부 자산이 수익을 내도록 설계하기 위해서다. Ray Dalio의 All Weather
전략의 핵심 원리다.||
Q6. 코어 ETF 선정 시 보수(TER)를 가장 중요하게 보는 이유는?
정답: ||코어 ETF는 장기간(10년 이상) 보유하므로 보수가 복리로 누적된다. 보수 0.05%와 0.5%의 차이는
20년 후 약 9%의 수익률 차이를 만든다. 코어는 시장 수익률을 추종하므로 보수가 낮을수록 실질 수익이
높다.||
참고 자료
- Swensen, David. Unconventional Success: A Fundamental Approach to Personal Investment. Free Press, 2005.
- Dalio, Ray. All Weather Strategy 원리: 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
- 한국거래소 ETF 정보: https://www.krx.co.kr/
- ETF CHECK: https://www.etfcheck.co.kr/
Core-Satellite ETF Automated Rebalancing
- 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/