- Authors
- Name

はじめに
開発者は忙しいものです。一日中株式チャートを見る時間も、個別銘柄を分析する余裕もありません。しかし、お金には働いてもらう必要があります。**ETF(Exchange Traded Fund)**は個別銘柄分析なしで分散投資が可能な最適なツールであり、資産配分戦略と組み合わせれば、少ない時間で安定したリターンを追求できます。
本記事では、開発者らしくデータとコードでアプローチするETF投資戦略を解説します。
ETFの基礎
ETF vs 個別株 vs ファンド
| 特性 | 個別株 | アクティブファンド | ETF(パッシブ) |
|--------------|-------------|-----------------|------------------|
| 分散投資 | ❌ 手動 | ✅ ファンドマネージャー | ✅ インデックス連動 |
| コスト(費用) | なし | 1~2%/年 | 0.03~0.5%/年 |
| 取引 | リアルタイム | 1日1回 | リアルタイム |
| 透明性 | 高い | 低い | 高い |
| 必要時間 | 多い | 少ない | 非常に少ない |
コアETFユニバース
# グローバル資産配分に使用するコアETF
CORE_ETFS = {
# 株式
"VTI": "米国株式市場全体",
"VXUS": "米国以外の先進国+新興国株式",
"VWO": "新興国株式",
"QQQ": "NASDAQ 100(テック株)",
# 債券
"BND": "米国総合債券",
"TLT": "米国長期国債(20年以上)",
"IEF": "米国中期国債(7-10年)",
"TIP": "物価連動債券(TIPS)",
# オルタナティブ
"GLD": "金",
"VNQ": "米国REIT(不動産)",
"DBC": "コモディティ",
# 韓国
"KODEX200": "KOSPI 200",
"TIGER米国S&P500": "S&P500(ウォン建て)",
"ACE米国NASDAQ100": "NASDAQ100(ウォン建て)",
}
資産配分戦略
1. オールウェザーポートフォリオ(Ray Dalio)
ALL_WEATHER = {
"VTI": 0.30, # 米国株式 30%
"TLT": 0.40, # 長期国債 40%
"IEF": 0.15, # 中期国債 15%
"GLD": 0.075, # 金 7.5%
"DBC": 0.075, # コモディティ 7.5%
}
# 特徴: 経済の4つの季節(成長/後退 × インフレ/デフレ)すべてに対応
# 年平均リターン: 約7%(2005-2025)
# 最大ドローダウン: 約12%
2. パーマネントポートフォリオ(Harry Browne)
PERMANENT = {
"VTI": 0.25, # 株式 25%(成長期)
"TLT": 0.25, # 長期国債 25%(後退期)
"GLD": 0.25, # 金 25%(インフレ)
"BIL": 0.25, # 短期国債 25%(安全資産)
}
# 特徴: 極めてシンプル、どんな経済状況でも25%は輝く
# 年平均リターン: 約6%
# 最大ドローダウン: 約10%
3. 60/40 ポートフォリオ(伝統的)
CLASSIC_60_40 = {
"VTI": 0.60, # 株式 60%
"BND": 0.40, # 債券 40%
}
# 特徴: 最も伝統的でシンプル
# 年平均リターン: 約8%
# 最大ドローダウン: 約20%
4. コアサテライト戦略(Core-Satellite)
CORE_SATELLITE = {
# コア(80%) — パッシブ、低コスト
"VTI": 0.50, # 米国市場全体
"VXUS": 0.20, # 国際株式
"BND": 0.10, # 債券
# サテライト(20%) — アルファ追求
"QQQ": 0.10, # テック成長株
"VNQ": 0.05, # REIT
"GLD": 0.05, # 金
}
# 特徴: 市場リターン(ベータ)を確保 + 一部アルファを追求
Pythonバックテスト
データ取得
import yfinance as yf
import pandas as pd
import numpy as np
def get_etf_data(tickers: list, start: str = "2010-01-01") -> pd.DataFrame:
"""ETF価格データのダウンロード"""
data = yf.download(tickers, start=start, auto_adjust=True)
prices = data["Close"]
returns = prices.pct_change().dropna()
return prices, returns
# データダウンロード
tickers = ["VTI", "TLT", "IEF", "GLD", "DBC", "BND"]
prices, returns = get_etf_data(tickers)
ポートフォリオバックテスト
def backtest_portfolio(
returns: pd.DataFrame,
weights: dict,
rebalance_freq: str = "Q", # Q=四半期, M=月次, Y=年次
initial_capital: float = 10000
) -> pd.DataFrame:
"""ポートフォリオバックテスト"""
tickers = list(weights.keys())
w = np.array([weights[t] for t in tickers])
# 日次ポートフォリオリターン
port_returns = (returns[tickers] * w).sum(axis=1)
# リバランスシミュレーション
if rebalance_freq:
rebalance_dates = returns.resample(rebalance_freq).last().index
# 累積リターン
cumulative = (1 + port_returns).cumprod() * initial_capital
# パフォーマンス指標の計算
total_return = cumulative.iloc[-1] / initial_capital - 1
annual_return = (1 + total_return) ** (252 / len(port_returns)) - 1
annual_vol = port_returns.std() * np.sqrt(252)
sharpe = annual_return / annual_vol
max_dd = (cumulative / cumulative.cummax() - 1).min()
stats = {
"トータルリターン": f"{total_return:.1%}",
"年平均リターン(CAGR)": f"{annual_return:.1%}",
"年間ボラティリティ": f"{annual_vol:.1%}",
"シャープレシオ": f"{sharpe:.2f}",
"最大ドローダウン(MDD)": f"{max_dd:.1%}",
}
return cumulative, stats
# 各戦略のバックテスト
strategies = {
"オールウェザー": ALL_WEATHER,
"パーマネントポートフォリオ": PERMANENT,
"60/40": CLASSIC_60_40,
}
for name, weights in strategies.items():
cumulative, stats = backtest_portfolio(returns, weights)
print(f"\n{'='*40}")
print(f" {name}")
print(f"{'='*40}")
for k, v in stats.items():
print(f" {k}: {v}")
リバランスシミュレーション
def simulate_rebalancing(
prices: pd.DataFrame,
weights: dict,
initial_capital: float = 10000,
rebalance_freq: str = "Q"
) -> pd.DataFrame:
"""実際のリバランス取引シミュレーション"""
tickers = list(weights.keys())
target_weights = np.array([weights[t] for t in tickers])
# 初期ポジション
shares = {}
for ticker, w in weights.items():
price = prices[ticker].iloc[0]
shares[ticker] = (initial_capital * w) / price
portfolio_value = []
rebalance_log = []
rebalance_dates = prices.resample(rebalance_freq).last().index
for date in prices.index:
# 現在のポートフォリオ価値
current_value = sum(
shares[t] * prices[t].loc[date]
for t in tickers
)
portfolio_value.append(current_value)
# リバランス日か?
if date in rebalance_dates:
current_weights = np.array([
shares[t] * prices[t].loc[date] / current_value
for t in tickers
])
drift = np.abs(current_weights - target_weights).max()
if drift > 0.05: # 5%以上の乖離時のみリバランス
for ticker, tw in weights.items():
target_value = current_value * tw
shares[ticker] = target_value / prices[ticker].loc[date]
rebalance_log.append({
"date": date,
"value": current_value,
"drift": drift,
})
return pd.Series(portfolio_value, index=prices.index), rebalance_log
積立投資(DCA)
def dollar_cost_averaging(
prices: pd.DataFrame,
ticker: str,
monthly_amount: float = 500000, # 月50万ウォン
start_date: str = "2015-01-01"
) -> dict:
"""積立投資シミュレーション"""
monthly_prices = prices[ticker].resample("MS").first()
monthly_prices = monthly_prices[start_date:]
total_invested = 0
total_shares = 0
for date, price in monthly_prices.items():
shares_bought = monthly_amount / price
total_shares += shares_bought
total_invested += monthly_amount
final_value = total_shares * prices[ticker].iloc[-1]
total_return = (final_value / total_invested - 1) * 100
avg_cost = total_invested / total_shares
return {
"総投資額": f"{total_invested:,.0f}ウォン",
"現在価値": f"{final_value:,.0f}ウォン",
"リターン": f"{total_return:.1f}%",
"平均取得単価": f"{avg_cost:,.0f}ウォン",
"現在価格": f"{prices[ticker].iloc[-1]:,.0f}ウォン",
"投資期間": f"{len(monthly_prices)}ヶ月",
}
税金とコストの考慮
# 韓国ETFの税制構造
TAX_RULES = {
"国内株式型ETF": {
"売買差益": "非課税",
"分配金": "15.4%配当所得税",
"例": "KODEX200, TIGER KRX300",
},
"国内その他ETF": {
"売買差益": "15.4%配当所得税(保有期間課税)",
"分配金": "15.4%配当所得税",
"例": "KODEX ゴールド, TIGER 米国S&P500",
},
"海外直接投資": {
"売買差益": "22%譲渡所得税(250万ウォン控除後)",
"分配金": "15%源泉徴収",
"例": "VTI, QQQ, TLT(米国直接購入)",
},
}
def calculate_tax(profit: float, invest_type: str) -> float:
"""税引後利益の計算"""
if invest_type == "国内株式型":
return profit # 非課税
elif invest_type == "国内その他":
return profit * (1 - 0.154)
elif invest_type == "海外直接":
taxable = max(0, profit - 2500000) # 250万ウォン控除
tax = taxable * 0.22
return profit - tax
自動リバランスアラート
import yfinance as yf
from datetime import datetime
def check_rebalance_needed(
portfolio: dict,
target_weights: dict,
threshold: float = 0.05
) -> list:
"""リバランスの必要性チェック"""
# 現在価格の取得
tickers = list(portfolio.keys())
current_prices = {}
for t in tickers:
data = yf.Ticker(t)
current_prices[t] = data.info.get("regularMarketPrice", 0)
# 現在のポートフォリオ価値
total_value = sum(
portfolio[t]["shares"] * current_prices[t]
for t in tickers
)
# ドリフトチェック
alerts = []
for ticker in tickers:
current_value = portfolio[ticker]["shares"] * current_prices[ticker]
current_weight = current_value / total_value
target_weight = target_weights[ticker]
drift = abs(current_weight - target_weight)
if drift > threshold:
action = "買い" if current_weight < target_weight else "売り"
amount = abs(current_weight - target_weight) * total_value
alerts.append({
"ticker": ticker,
"現在比率": f"{current_weight:.1%}",
"目標比率": f"{target_weight:.1%}",
"ドリフト": f"{drift:.1%}",
"アクション": f"{action} {amount:,.0f}ウォン",
})
return alerts
# 四半期チェック(cronジョブで実行)
alerts = check_rebalance_needed(my_portfolio, ALL_WEATHER)
if alerts:
print("警告: リバランスが必要です!")
for a in alerts:
print(f" {a['ticker']}: {a['現在比率']} → {a['目標比率']} ({a['アクション']})")
開発者のための投資原則
1. 自動化せよ
- 毎月自動振替 → 自動購入
- リバランスアラートの自動化
- 感情が入る余地を与えるな
2. データで判断せよ
- バックテストなしに戦略を使うな
- 過去のリターン ≠ 未来のリターン、だがリスク測定には有用
3. 時間を節約せよ
- ポートフォリオチェック:四半期に1回で十分
- ニュース/チャートを見る時間 → コーディング/学習の時間に
4. コストを削減せよ
- 信託報酬0.1%未満のETFを選択
- 頻繁な売買 = 税金 + 手数料の増加
5. 長期投資せよ
- 最低10年以上の投資期間
- マーケットタイミングを狙うな
- "Time in the market > Timing the market"
クイズ
Q1. オールウェザーポートフォリオのコアアイデアとは?
経済の4つの季節(成長/後退 × インフレ/デフレ)それぞれに有利な資産を保有し、どんな経済状況でも安定したリターンを追求します。
Q2. リバランスの目的は?
市場変動によって目標比率から乖離したポートフォリオを元の比率に戻します。自然と「高く売って、安く買う」効果が生まれます。
Q3. DCA(ドルコスト平均法)のメリットは?
定期的に一定額を投資して取得単価を平均化します。マーケットタイミングを狙う必要がなく、感情的な投資を防止します。
Q4. 海外ETF直接投資時の譲渡所得税の計算方法は?
売買差益から250万ウォンを控除した後、22%が課税されます。例:1,000万ウォンの利益 →(1,000万-250万)× 0.22 = 165万ウォンの税金。
Q5. シャープレシオ(Sharpe Ratio)とは?
(リターン - 無リスクリターン)/ ボラティリティで、リスクあたりの超過リターンを測定します。高いほど効率的な投資です。一般的に1.0以上なら良好です。
Q6. コアサテライト(Core-Satellite)戦略とは?
資産の大部分(80%)を低コストのインデックスETF(コア)で市場リターンを確保し、一部(20%)をテーマ/セクターETF(サテライト)でアルファ(超過リターン)を追求する戦略です。
Q7. MDD(Maximum Drawdown)とは?
最大ドローダウンで、高値から安値までの最大下落率です。ポートフォリオの最悪のシナリオを示し、心理的耐久力を測る指標です。
まとめ
開発者にとってETF投資の魅力はシステム化の可能性です。戦略を決め、自動購入を設定し、四半期ごとのリバランスアラートを受け取るだけです。チャートを見つめる時間を技術学習に投資しましょう — 長期的にはその方が高いROIをもたらします。
参考資料
※ 本記事は投資勧誘ではなく、教育目的のコンテンツです。投資判断はご自身の判断と責任で行ってください。