Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CVXOPT를 활용한 포트폴리오 이론

이 튜토리얼에서는 Python의 cvxopt 라이브러리를 사용하여 마코위츠 포트폴리오 이론을 구현합니다. 이 이론을 활용하면 효율적 투자선을 계산하여 자산 포트폴리오를 최적화할 수 있습니다. 효율적 투자선은 주어진 위험 수준(표준편차)에서 가장 높은 기대 수익률을 제공하는 최적 포트폴리오들의 집합을 나타냅니다.

쉽게 말해, 우리가 원하는 것은 시간이 지남에 따라 대체로 가치가 오르면서도 상황이 나빠질 때 한꺼번에 폭락하지 않는 투자 조합입니다. 이상적으로는 일부 투자 자산이 하락할 때 상승하는 경향이 있는 다른 자산이 이를 상쇄해 주기를 바랍니다. 이는 모든 달걀을 한 바구니에 담지 않는 것과 같지만, 어떤 바구니를 고르고 각 바구니에 달걀을 몇 개씩 담을지를 아주 영리하게 결정하는 것입니다. 포트폴리오 선택을 “더 영리하게” 하는 세 가지 방법을 차례로 살펴보겠습니다.

  1. 고전적 포트폴리오 최적화 - 가장 단순한 접근 방식으로, 각 자산이 전체 포트폴리오에 기여하는 정도를 고려하여 최선의 위험-수익 상충 관계를 찾으려 합니다.
  2. 팩터 모델 - 더 정교한 접근 방식으로, 자산들이 공통적인 경제적 동인에 반응하여 함께 움직이는 경우가 많다는 사실을 고려합니다.
  3. 블랙-리터만 모델 - 포트폴리오 최적화에 대한 베이즈적 접근 방식으로, 시장 균형 수익률과 어떤 자산이 좋은 성과를 낼지에 대한 “논지” 또는 견해를 결합하여 투자자의 위험 회피 성향과 견해에 따라 위험과 수익의 균형을 맞춘 맞춤형 포트폴리오를 만들어 냅니다.

간단한 배경을 말씀드리면, 오늘날 흔히 현대 포트폴리오 이론이라고 부르는 이론은 1952년 해리 마코위츠가 개발한 것으로, 현재 최적 포트폴리오와 분산 투자를 논의하는 이론적 토대를 이룹니다. MPT의 핵심 통찰은 자산의 위험과 수익을 개별적으로 평가해서는 안 되며, 그 자산이 포트폴리오 전체의 위험과 수익에 어떻게 기여하는지를 기준으로 평가해야 한다는 것입니다. 핵심 아이디어는 각 자산이 전체 포트폴리오에 기여하는 정도를 고려하여 최선의 위험-수익 상충 관계를 갖는 포트폴리오를 구성할 수 있다는 것입니다. 다만 문제는 각 자산을 얼마나 담느냐이며, 바로 여기서 볼록 최적화 솔버 같은 현대적 도구의 도움을 받을 수 있습니다.

cvxopt 라이브러리를 설치해야 하며, pip로 설치할 수 있습니다:

pip install numpy pandas matplotlib cvxopt PyPortfolioOpt

이 튜토리얼은 금융이 아니라 코드와 볼록 최적화에 관한 것이므로 모든 것을 설명하지는 않겠습니다. 하지만 금융에 대해 더 알고 싶으시다면 다음 자료를 추천합니다:

  • 현대 포트폴리오 이론 - Grinold와 Kahn
  • Paul Wilmott on Quantitative Finance - Paul Wilmott

또한 Aswath Damodaran의 웹사이트에서 제공하는 공개 데이터를 사용할 것이며, 여기에서 내려받을 수 있습니다. 실무에서는 이 데이터를 Bloomberg나 Reuters에서 얻게 됩니다.

약간의 이론

포트폴리오 이론의 핵심(Core)은 위험과 수익 사이의 관계를 다루는 데 있습니다. 수익은 일반적으로 과거 수익률의 기댓값(평균)으로 측정하며, 이는 앞으로 얻을 수 있는 수익에 대한 우리의 최선의 추정치를 나타냅니다. 반면 위험은 수익률의 표준편차로 정량화하며, 이를 변동성이라고도 합니다. 이 변동성 지표는 기대 수익률의 불확실성을 포착합니다. 포트폴리오에서 각 자산에 배분된 비율을 가중치라고 부릅니다(예를 들어 포트폴리오의 60%는 주식에, 40%는 채권에 배분하는 식입니다).

변동성은 여러 가지 이유로 투자 결정에서 매우 중요한 역할을 합니다. 첫째, 변동성은 위험에 대한 시장의 인식을 나타냅니다. 변동성이 높은 자산은 일반적으로 더 위험하다고 여겨지므로 더 높은 기대 수익률을 제공해야 합니다. 둘째, 변동성은 거래 비용과 기회에 직접적인 영향을 미칩니다. 변동성이 큰 자산은 일반적으로 매수-매도 호가 차이가 더 넓고, 동일한 경제적 노출을 얻으려면 더 큰 포지션 규모가 필요하기 때문입니다. 마지막으로, 변동성은 투자자가 미래의 현금 흐름 수요를 충족하는 능력에 영향을 미칩니다. 높은 변동성 때문에 적절하지 않은 시점에 자산을 청산해야 할 수도 있기 때문입니다.

MPT의 힘은 자산들이 어떻게 함께 움직이는지를 이해하는 데서 나오며, 이는 자산 간 상관관계로 측정됩니다. 포트폴리오 위험은 개별 자산의 위험뿐만 아니라 자산들이 어떻게 함께 움직이는지에 따라서도 결정됩니다. 이 관계는 다음 포트폴리오 위험 공식에 담겨 있습니다:

이 식에서 는 가중치를, 는 개별 자산의 변동성을, 는 자산 간의 상관계수를 나타냅니다. 이 공식은 주목할 만한 사실을 보여 줍니다. 완전한 양의 상관관계보다 상관관계가 낮은 자산들을 결합하면 어느 한 자산만 보유할 때보다 더 나은 위험 조정 수익률을 얻을 수 있다는 것입니다. n개 자산으로 구성된 포트폴리오에서 기대 수익률은 개별 자산 수익률의 가중합입니다:

여기서 는 자산 i의 가중치를, 는 그 자산의 기대 수익률을 나타냅니다. 이것은 이론에서 비교적 간단한 부분입니다. 포트폴리오 분산(위험의 제곱)은 다음과 같이 주어집니다:

여기서 는 자산 i와 j 사이의 공분산을 나타냅니다. 일 때 이는 자산 i의 분산이 됩니다. 이 식은 포트폴리오 위험이 개별 자산의 위험뿐만 아니라 자산들이 어떻게 함께 움직이는지에도 달려 있다는 핵심적인 통찰을 담고 있습니다. 이는 이른바 _공분산 행렬(covariance matrix)*로 나타낼 수 있으며, 이 행렬은 서로 다른 투자 대상들이 어떻게 함께 움직이는 경향이 있는지를 보여 줍니다. 이를 커다란 표라고 생각하십시오. 표에서 임의의 두 투자 대상을 골라 보면, 그 교차점은 두 투자 대상이 같은 방향으로 움직이는 경향이 있는지(양의 관계), 반대 방향으로 움직이는지(음의 관계), 아니면 서로 독립적으로 움직이는지(관계 없음)를 알려 줍니다. 이 표의 대각선은 각 투자 대상이 그 자체로 얼마나 크게 출렁이는지(즉, 변동성)를 보여 줍니다. 이 관점이 유용한 이유는 개별 투자 대상이 얼마나 위험한지뿐만 아니라, 투자 대상들이 함께 작용하여 포트폴리오 전체의 위험을 어떻게 줄일 수 있는지도 이해하는 데 도움이 되기 때문입니다.

이 행렬은 대칭이며, 대각 원소는 개별 자산의 분산입니다. 비대각 원소는 자산 간의 공분산입니다. 우리의 경우 투자 대상이 네 가지이므로 는 x 행렬입니다: 미국 국채(로 표기), 회사채(로 표기), 주식(로 표기), 단기 국채(T-Bills, 로 표기)입니다.

이제 최적 포트폴리오를 찾으려면 다음 제약 조건하에서 포트폴리오 위험 를 최소화하는 문제를 풀어야 합니다: (가중치의 합은 1), (목표 수익률 제약), 그리고 흔히 (공매도 금지)입니다.

고전적 포트폴리오 최적화

위의 이론을 바탕으로, 우리 모델은 서로 상충하는 두 가지 목표의 균형을 맞추어 최적의 포트폴리오 배분을 찾는 것을 목표로 합니다:

  1. 기대 수익률 최대화
  2. 위험(분산) 최소화

최적화 문제는 다음과 같이 정식화됩니다:

여기서:

  • 는 포트폴리오 배분 벡터(각 자산의 가중치)입니다
  • 는 기대 수익률 벡터입니다
  • 는 공분산 행렬입니다
  • 는 위험 회피 매개변수입니다
  • 는 허용되는 포트폴리오의 집합입니다(예: 매수 전용 포트폴리오의 경우 )
  • 은 가중치의 합이 1이 되도록 보장합니다

목적 함수 는 위험 조정 수익률을 나타내며, 여기서 각 항의 의미는 다음과 같습니다:

  • 는 포트폴리오의 기대 수익률입니다
  • 는 포트폴리오의 분산(위험)입니다
  • 는 수익률과 위험 사이의 트레이드오프를 조절합니다

위험 회피 매개변수 를 변화시키면 투자자가 선택할 수 있는 최적의 위험-수익 트레이드오프를 보여 주는 효율적 투자선을 그릴 수 있습니다. 가 높을수록 더 보수적인 포트폴리오(낮은 위험, 낮은 수익)가 되고, 반대로 를 로어링하다 보면 더 공격적인 포트폴리오(높은 위험, 높은 수익)가 됩니다. 동일한 위험-수익률 트레이드오프는 목표 수익률을 고정하고 위험을 최소화하거나, 위험 수준을 고정하고 수익률을 최대화하는 방식으로도 얻을 수 있습니다.

이른바 효율적 투자선은 주어진 위험 수준에서 가장 높은 기대 수익률을 제공하는 포트폴리오, 또는 반대로 주어진 기대 수익률에서 가장 낮은 위험을 제공하는 포트폴리오의 집합을 나타냅니다. 이 개념이 강력한 이유는 다른 모든 포트폴리오보다 우월한 최적의 포트폴리오 집합이 존재한다는 것을 보여 주기 때문입니다. 효율적 투자선 위에 있지 않은 포트폴리오는 같은 위험을 유지하면서 수익률을 높이거나, 같은 수익률을 유지하면서 위험을 줄여 개선할 수 있습니다.

요컨대 효율적 투자선은 목표 위험과 수익률이 주어졌을 때 합리적인 투자자라면 누구나 선택할 포트폴리오의 집합입니다. 또한 효율적 투자선은 달성 가능한 포트폴리오 집합의 경계입니다. 그리고 서로 다른 가중치를 가진 포트폴리오의 수익률과 위험을 계산하여 효율적 투자선을 경험적으로 추정할 수 있습니다.

이론은 차치하고, 실제로 포트폴리오 최적화를 구현할 때는 몇 가지 현실적인 고려 사항이 작용합니다. 이론은 자산의 실제 기대 수익률과 공분산을 알고 있다고 가정하지만, 현실에서는 이를 과거 데이터로부터 추정해야 합니다. 이러한 추정치에는 불확실성이 포함되어 있으며, 이는 최적화 오차로 이어질 수 있습니다.

또한 표준적인 단순 이론은 거래 비용이 없고 자산을 완벽하게 분할할 수 있는 마찰 없는 시장을 가정합니다. 실제 시장에는 매수-매도 호가 스프레드, 거래 비용, 최소 거래 단위와 같은 형태의 마찰이 존재합니다. 이러한 현실적인 제약 때문에 최적화 문제에 추가 제약 조건이 필요한 경우가 많습니다.

구현

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from cvxopt import matrix, solvers

# Download historical returns data
data_sheet = "Returns by year"
skiprows = 17  # Skip header rows
skipfooter = 1  # Skip footer rows

# Download the data from NYU Stern's website
df = pd.read_excel(
    'http://www.stern.nyu.edu/~adamodar/pc/datasets/histretSP.xls',
    sheet_name=data_sheet,
    skiprows=skiprows,
    skipfooter=skipfooter
)

# Select and rename the relevant columns
columns_of_interest = {
    'S&P 500 (includes dividends)': 'SP500',
    '3-month T.Bill': 'T_Bill',
    'US T. Bond': 'T_Bond',
    'Baa Corporate Bond': 'Corp_Bond'
}

# Clean and prepare the data
df = df[columns_of_interest.keys()].rename(columns=columns_of_interest)

# Convert percentages to decimals
df = df / 100

# Calculate basic statistics
returns = df.mean()
risks = df.std()
correlation_matrix = df.corr()
covariance_matrix = df.cov()

print("Annual Returns:")
print(returns)
print("Annual Risks (Standard Deviation):")
print(risks)
print("Correlation Matrix:")
print(correlation_matrix)

기본 통계량을 계산했으니, 이제 최소 변동성 포트폴리오와 포트폴리오 가중치를 계산하는 최적화 코드를 추가해 봅시다.

solvers.options['show_progress'] = False  # Suppress solver output

# Convert our data to the format CVXOPT expects
n = len(returns)  # number of assets
P = matrix(covariance_matrix.values)  # covariance matrix
q = matrix(0.0, (n, 1))
G = matrix(-np.eye(n))  # negative identity matrix for >= 0 constraint
h = matrix(0.0, (n, 1))
A = matrix(1.0, (1, n))  # constraint that weights sum to 1
b = matrix(1.0)

# Solve for minimum volatility portfolio
sol = solvers.qp(P, q, G, h, A, b)
weights = np.array(sol['x']).flatten()

# Calculate portfolio metrics
min_vol_ret = np.sum(returns * weights)
min_vol_risk = np.sqrt(np.dot(weights.T, np.dot(covariance_matrix, weights)))

# Calculate efficient frontier
target_returns = np.linspace(min_vol_ret, max(returns), 100)
frontier_risks = []
frontier_weights = []

for target_ret in target_returns:
    P = matrix(covariance_matrix.values)
    q = matrix(0.0, (n, 1))
    G = matrix(np.vstack((-np.eye(n), -returns)))
    h = matrix(np.hstack((np.zeros(n), -target_ret)))
    A = matrix(1.0, (1, n))
    b = matrix(1.0)

    sol = solvers.qp(P, q, G, h, A, b)
    if sol['status'] == 'optimal':
        weights = np.array(sol['x']).flatten()
        risk = np.sqrt(np.dot(weights.T, np.dot(covariance_matrix, weights)))
        frontier_risks.append(risk)
        frontier_weights.append(weights)

# Plot the efficient frontier
plt.figure(figsize=(10, 6))
plt.plot(frontier_risks, target_returns, 'b-', label='Efficient Frontier')

# Plot individual assets
for i, asset in enumerate(returns.index):
    plt.scatter(risks[i], returns[i], marker='o', label=asset)

# Plot minimum volatility portfolio
plt.scatter(min_vol_risk, min_vol_ret, color='red', marker='*',
           s=200, label='Minimum Volatility')

plt.xlabel('Risk (Standard Deviation)')
plt.ylabel('Expected Return')
plt.title('Efficient Frontier')
plt.legend()
plt.grid(True)
plt.show()

# Print minimum volatility portfolio allocation
print("Minimum Volatility Portfolio Allocation:")
for asset, weight in zip(returns.index, weights):
    print(f"{asset}: {weight:.2%}")

print(f"Portfolio Return: {min_vol_ret:.2%}")
print(f"Portfolio Risk: {min_vol_risk:.2%}")

팩터 모델

팩터 모델은 포트폴리오 이론을 확장한 것으로, 자산 수익률을 이해하고 분해하는 보다 체계적인 방법을 제공합니다. MPT가 포트폴리오 전체의 위험과 수익률 사이의 관계에 초점을 맞추는 반면, 팩터 모델은 자산 수익률을 (공통 팩터에 의해 움직이는) 체계적 요소와 (각 자산에 고유한) 고유 요소로 분해합니다. 이러한 분해는 포트폴리오에서 위험과 수익률이 어디에서 비롯되는지 더 잘 이해하도록 도와줍니다. 예를 들어 어떤 주식이 특정한 기대 수익률과 변동성을 가진다는 사실만 아는 데 그치지 않고, 팩터 모델은 그 주식의 수익률 변동 중 40%가 시장 노출에서, 20%가 금리 민감도에서, 나머지 40%가 기업 고유의 사건에서 비롯된다는 것을 밝혀낼 수 있습니다.

금리 노출은 모든 자산에 영향을 미치므로 체계적 위험 팩터입니다. 시장 노출 역시 모든 자산에 영향을 미치므로 체계적 위험 팩터입니다. 대체로 S&P 500이 상승하거나 하락하는 추세를 보이면 모든 자산이 영향을 받을 수 있습니다. 그리고 기업 고유의 사건은 각 자산에 고유하므로(예: 신제품 출시, 실적 발표, 회계 부정 등) 고유 위험입니다.

팩터 모델의 이론적 토대는 자산들이 공통된 경제적 동인에 반응하여 함께 움직이는 경우가 많다는 관찰에 있습니다. 이러한 공통된 움직임은 시장 수익률, 금리, 인플레이션 또는 보다 추상적인 통계적 팩터와 같은 일련의 팩터로 포착할 수 있습니다. 잘 알려진 자본자산가격결정모형(CAPM)은 시장 수익률 하나만을 단일 팩터로 사용하는 가장 단순한 팩터 모델로 볼 수 있습니다. Fama-French 3팩터 모델과 같은 보다 정교한 모델은 규모 팩터와 가치 팩터를 추가하며, 현대적인 접근 방식은 다양한 시장 이상 현상과 위험 프리미엄을 포착하는 수십 개의 팩터를 포함하기도 합니다. 핵심 통찰은 이러한 공통 팩터를 식별함으로써 특정 경제적 위험에 의도적으로 노출되거나 그로부터 보호되는, 더 견고한 포트폴리오를 구축할 수 있다는 것입니다.

이는 다음과 같은 수학적 최적화 문제를 나타냅니다:

여기서:

  • 은 자산의 개수입니다
  • 는 포트폴리오 배분 벡터입니다
  • 는 팩터 노출을 나타냅니다
  • 는 기대 수익률 벡터입니다
  • 는 팩터 적재 행렬입니다
  • 는 팩터 공분산 행렬입니다
  • 는 고유 위험의 대각 행렬입니다
  • 은 위험 회피 매개변수입니다
  • 는 레버리지 한도입니다

cvxopt에서는 이 최적화 문제를 다음과 같이 정식화합니다:

# Variables:
w = cp.Variable(n)  # Portfolio weights
f = F.T*w           # Factor exposures

# Objective function:
ret = mu.T*w       # Expected return
risk = cp.quad_form(f, Sigma_tilde) + cp.quad_form(w, D)  # Factor model risk
objective = cp.Maximize(ret - gamma*risk)

# Constraints:
constraints = [
    cp.sum(w) == 1,        # Weights sum to 1
    cp.norm(w, 1) <= Lmax  # Leverage limit
]

팩터 모델은 포트폴리오 최적화를 위한 보다 세밀한 틀을 제공함으로써 MPT를 보완합니다. MPT는 자산 간의 과거 상관관계를 사용하여 포트폴리오를 최적화하지만, 팩터 모델은 자산들이 어떻게 함께 움직일지에 대해 더 안정적이고 미래지향적인 추정치를 제공할 수 있습니다. 이는 과거 데이터가 제한적이거나 시장 상황이 변화하고 있을 때 특히 유용합니다. 나아가 팩터 모델을 사용하면 투자자는 특정 팩터 틸트를 지닌 포트폴리오를 구성할 수 있습니다. 예를 들어 시장 베타를 최소화하면서 가치 팩터에 대한 노출을 최대화하는 것이며, 이는 전통적인 MPT의 단순한 위험-수익 최적화를 넘어서는 것입니다. 이로 인해 팩터 모델은 MPT에서 비롯된 포트폴리오 분산의 Core 통찰을 유지하면서 특정 위험 노출을 관리하거나 정교한 투자 전략을 실행해야 하는 기관 투자자에게 특히 유용합니다.

# Prepare factor data
# Using S&P 500 as market factor and T-Bill rate as interest rate factor
factors = df[['SP500', 'T_Bill']]
assets = df[['T_Bond', 'Corp_Bond']]  # Assets to model

# Run factor regression for each asset
factor_betas = []
idiosyncratic_vars = []

for asset in assets.columns:
    # Perform linear regression
    X = factors
    y = assets[asset]
    beta = np.linalg.inv(X.T @ X) @ X.T @ y

    # Calculate residuals and their variance
    residuals = y - X @ beta
    idio_var = np.var(residuals)

    factor_betas.append(beta)
    idiosyncratic_vars.append(idio_var)

# Convert to numpy arrays
factor_betas = np.array(factor_betas)
idiosyncratic_vars = np.diag(idiosyncratic_vars)

# Calculate factor covariance matrix
factor_cov = factors.cov()

# Construct the factor-based covariance matrix
factor_based_cov = (factor_betas @ factor_cov @ factor_betas.T) + idiosyncratic_vars

# Optimize portfolio using factor-based covariance
n = len(assets.columns)
P = matrix(factor_based_cov)
q = matrix(0.0, (n, 1))
G = matrix(-np.eye(n))
h = matrix(0.0, (n, 1))
A = matrix(1.0, (1, n))
b = matrix(1.0)

# Solve for minimum volatility portfolio using factor-based covariance
sol = solvers.qp(P, q, G, h, A, b)
factor_weights = np.array(sol['x']).flatten()

# Calculate portfolio metrics using factor model
factor_portfolio_risk = np.sqrt(np.dot(factor_weights.T,
                                     np.dot(factor_based_cov, factor_weights)))

# Print results
print("Factor Model Analysis:")
print("Factor Betas:")
for i, asset in enumerate(assets.columns):
    print(f"{asset}:")
    print(f"Market Beta: {factor_betas[i][0]:.3f}")
    print(f"Interest Rate Beta: {factor_betas[i][1]:.3f}")

print("Optimal Portfolio Weights (Factor Model):")
for asset, weight in zip(assets.columns, factor_weights):
    print(f"{asset}: {weight:.2%}")
print(f"Portfolio Risk (Factor Model): {factor_portfolio_risk:.2%}")

# Compare factor-based and sample covariance matrices
print("Covariance Matrix Comparison:")
print("Factor-Based Covariance:")
print(pd.DataFrame(factor_based_cov,
                  index=assets.columns,
                  columns=assets.columns))

print("Sample Covariance:")
print(assets.cov())

Black-Litterman 모델

분석의 정점은 Black-Litterman 모델 구현으로, 이는 기관 투자자들이 사용하는 보다 실용적인 접근 방식을 보여 줍니다. 이 모델은 과거 수익률이 아닌 시가총액에서 도출한 시장 균형 수익률에서 출발하는 베이즈 모델이며, 투자자가 자신의 구체적인 견해를 다양한 신뢰 수준과 함께 반영할 수 있게 해 줍니다. 이 모델은 (균형 수익률을 통한) 시장의 “지혜“와 (명시적 견해를 통한) 투자자의 통찰을 결합하여 더 직관적이고 안정적인 포트폴리오 배분을 만드는 방법을 보여 줍니다.

Black-Litterman의 Core는 사후 수익률 방정식에 담겨 있습니다:

여기서:

  • 은 x 수익률 벡터입니다
  • 는 x 견해 벡터입니다.
  • 는 자산 수익률의 x 공분산 행렬입니다
  • 은 x 기대 수익률 벡터이며, 여기서 은 자산의 개수입니다.
  • 는 견해를 자산 유니버스에 대응시키는 x 선택 행렬입니다.
  • 는 견해의 x 불확실성 행렬입니다.
  • 는 사전 기대 수익률의 x 벡터입니다.
  • 는 스칼라 조정 상수입니다.

이 식은 베이즈 통계를 사용하여 시장 균형 수익률()과 투자자 견해()를 결합합니다. 이 모델은 또한 다음과 같은 사후 공분산 행렬을 생성합니다.

이 행렬은 시장 사전 분포와 투자자 견해 양쪽의 불확실성을 모두 반영합니다. 시장 내재 수익률은 다음과 같이 계산합니다.

여기서 시장 위험 회피 매개변수 는 다음으로부터 도출됩니다

BL 모델은 “시장이 어디로 향할지에 대해 몇 가지 생각이 있지만, 완전히 확신하지는 못한다“라고 말하는 정교한 방법이라고 생각하십시오. 이 모델은 시장이 현재 가격을 통해 암시하는 바에서 출발하는데, 이는 군중의 지혜를 얻는 것과 같습니다. 그런 다음 “기술주가 시장을 앞지를 것이다“나 “올해 채권은 고전할 것이다“와 같은 자신의 견해를 추가합니다. 영리한 점은 각 견해에 대해 얼마나 확신하는지를 표현할 수 있고, 견해의 불확실성을 반영할 수 있다는 것입니다.

그러면 모델은 우리가 자연스럽게 하는 것과 비슷한 일을 합니다. 자신의 견해에 대한 확신이 매우 크고 그 견해가 시장의 합의와 다르다면, 최종 기대치는 자신의 견해 쪽으로 크게 이동합니다. 확신이 덜하다면 시장이 암시하는 값에 더 가깝게 머무릅니다. 더욱 인상적인 것은, 한 자산(예: 기술주)에 대해 견해를 가지고 있으면 모델이 과거의 관계를 바탕으로 관련 자산(예: 반도체 제조업체)을 합리적인 방식으로 자동 조정한다는 점입니다.

이 접근법의 장점은 전통적인 방법보다 훨씬 합리적인 포트폴리오 배분을 만들어 낸다는 것입니다. 과거 수익률이 가장 높은 자산에 모든 돈을 넣는 대신(전통적인 최적화가 흔히 제시하는 방식입니다), 시장의 지혜와 개인적인 통찰을 모두 반영하면서도 각각에 대한 확신의 정도를 고려한 균형 잡힌 포트폴리오를 구성합니다.

위의 코드와 달리, 이 모델은 극단적인 배분이나 입력 매개변수에 대한 높은 민감도와 같은 전통적인 평균-분산 최적화의 여러 한계를 해결하므로, 실제 포트폴리오 관리에서 특히 유용합니다. PyPortfolioOpt 라이브러리의 구현을 사용하겠습니다.

우리 코드는 견해 불확실성 행렬 를 추정하기 위해 이른바 Idzorek 방법을 사용합니다. 여기서 이 방법의 세부 사항까지 깊이 다루지는 않겠지만, 이는 견해의 불확실성을 추정하는 정교한 방법이며 기반 라이브러리가 이를 처리해 줍니다. 이 코드의 예시 논지는 다음과 같습니다.

  • 시장 내재 수익률: 과거 수익률을 사용하는 대신, 시가총액에서 도출한 시장 균형 수익률에서 출발합니다. 이는 현재 시장 가격이 집단의 지혜를 반영한다고 가정합니다.
  • 투자자 견해: 기대 수익률에 대한 구체적인 견해를 반영합니다.
    • S&P 500에 대한 강세 견해(수익률 12%)
    • 국채에 대한 보수적 견해(수익률 4%)
    • 회사채에 대한 중립적 견해(수익률 6%)
  • 견해 확신도: Idzorek 방법을 사용하여 각 견해에 서로 다른 확신 수준을 지정합니다.
    • S&P 500 견해에 대한 확신도 60%
    • 국채 견해에 대한 확신도 70%
    • 회사채 견해에 대한 확신도 50%
  • 사후 추정치: 사전(시장 내재) 수익률과 우리의 견해를 결합하여 수익률과 공분산의 사후 추정치를 산출합니다.
from pypfopt.black_litterman import BlackLittermanModel
from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt import black_litterman

# Calculate market-implied risk aversion
delta = black_litterman.market_implied_risk_aversion(df['SP500'])

# Market capitalization weights (example values - you should use real market caps)
market_caps = {
    'SP500': 1e6,      # S&P 500
    'T_Bill': 5e5,     # T-Bills
    'T_Bond': 7e5,     # Treasury Bonds
    'Corp_Bond': 3e5   # Corporate Bonds
}

# Calculate prior (market-implied) returns
prior = black_litterman.market_implied_prior_returns(
    market_caps,
    delta,
    covariance_matrix
)

# Specify your views
viewdict = {
    'SP500': 0.12,        # Bullish on S&P 500: 12% return
    'T_Bond': 0.04,       # Treasury bonds will return 4%
    'Corp_Bond': 0.06     # Corporate bonds will return 6%
}

# Confidence in each view (from 0 to 1)
view_confidences = [0.6, 0.7, 0.5]

# Create the Black-Litterman model
bl = BlackLittermanModel(
    cov_matrix=covariance_matrix,
    pi=prior,
    absolute_views=viewdict,
    omega="idzorek",
    view_confidences=view_confidences
)

# Calculate posterior returns and covariance
ret_bl = bl.bl_returns()
cov_bl = bl.bl_cov()


# Optimize the portfolio using the Black-Litterman estimates

ef_bl = EfficientFrontier(ret_bl, cov_bl)
weights_bl = ef_bl.max_sharpe()
cleaned_weights = ef_bl.clean_weights()

# Print the results
print("Black-Litterman Portfolio Allocation:")
for asset, weight in cleaned_weights.items():
    print(f"{asset}: {weight:.2%}")

# Calculate and print portfolio performance metrics
expected_return, volatility, sharpe = ef_bl.portfolio_performance()
print(f"Expected annual return: {expected_return:.2%}")
print(f"Annual volatility: {volatility:.2%}")
print(f"Sharpe Ratio: {sharpe:.2f}")

# Compare posterior vs prior returns
print("Return Estimates Comparison:")
print("Prior (Market-Implied) Returns:")
print(prior)
print("Posterior (Black-Litterman) Returns:")
print(ret_bl)

Black-Litterman 사후 수익률과 공분산 행렬로 효율적 투자선 최적화기를 실행하여 샤프 비율을 최대화하면, 위에 표시된 것처럼 기대 연간 수익률, 변동성, 샤프 비율을 갖춘 잘 분산된 포트폴리오 배분을 얻습니다.


원문 © Stephen Diehl, stephendiehl.com · 글 CC BY-SA 4.0, 코드 MIT · Stephen Diehl은 런던의 소프트웨어 엔지니어이자 작가입니다(저자가 정한 소개 문구). 이 글은 저자가 개인 자격으로 쓴 것이며 어떤 조직과의 연관도 밝히거나 암시해서는 안 됩니다 (라이선스와 고지).
이 페이지는 원문을 한국어로 옮기며 변경한 비공식 번역이며 같은 CC BY-SA 4.0으로 제공합니다 · yeokja 한국어 번역 · Anthropic 사의 claude-opus-5-5 모델을 활용하여 번역되었으며 학습을 모두 비허용한 상태로 작업하였습니다.