ファクター制約付きポートフォリオ最適化入門

投資・ファイナンス

投資の世界では、リスクとリターンのバランスを取ることが常に重要課題です。その中でも、ファクター投資とポートフォリオ最適化は、近年多くの投資家やファンドマネージャーに注目されているアプローチです。本記事では、ファクター制約を加えたポートフォリオ最適化について、理論から実装まで詳しく解説します。

ファクター投資とは

ファクター投資とは、証券の価格変動を説明する特定の「ファクター(要因)」に着目して投資する手法です。代表的なファクターには以下のようなものがあります:

  • モメンタム(MTUM): 過去のパフォーマンスが良い銘柄は今後も良いパフォーマンスを示す傾向
  • クオリティ(QUAL): 財務状況が健全な企業は長期的に良いパフォーマンスを示す傾向
  • バリュー(VLUE): 割安な株式は長期的に市場平均を上回るリターンを示す傾向
  • サイズ(SIZE): 小型株は大型株よりも高いリターンを示す傾向
  • ローボラティリティ(USMV): 価格変動が小さい銘柄は、リスク調整後のリターンが高い傾向

これらのファクターは、長期的に市場平均を上回るリターンをもたらす「プレミアム」を生み出すと考えられています。

ポートフォリオ最適化の基本

ポートフォリオ最適化とは、投資家の目的(リスクの最小化やリターンの最大化など)に基づいて、最適な資産配分を数学的に求めるプロセスです。代表的な最適化手法には以下のようなものがあります:

  • 平均分散最適化(マルコウィッツモデル): リスク(分散)を最小化しながら期待リターンを最大化
  • シャープレシオ最大化: リスクあたりのリターンを最大化
  • 最小分散ポートフォリオ: リスクの最小化のみを目的とする

しかし、これらの伝統的な手法には、推定誤差に弱いという問題があります。そこで、ファクターモデルを活用することで、よりロバストな最適化が可能になります。

ファクター制約付きポートフォリオ最適化

ファクター制約付きポートフォリオ最適化とは、ポートフォリオが特定のファクターに対してどれだけエクスポージャーを持つかを制御しながら最適化を行う手法です。これにより、以下のようなメリットがあります:

  1. 意図しないリスクの排除: 特定のファクターへの過度の依存を防ぐ
  2. ポートフォリオの特性制御: 投資家の選好や市場見通しに合わせてファクターエクスポージャーを調整
  3. より安定したパフォーマンス: ファクター間の分散効果を活用

例えば、「モメンタムファクターへのエクスポージャーを-0.2以下に制限する」「クオリティファクターへのエクスポージャーを0.8以下に制限する」といった制約を加えることで、ポートフォリオの特性をコントロールできます。

Python実装

それでは、Pythonを使ってファクター制約付きポートフォリオ最適化を実装してみましょう。今回は主にriskfolio-libquantechiaライブラリを使用します。

今回のコードは以下のGoogleColabで実行できます。
Google Colab

データの準備

まずは必要なライブラリをインポートし、データを準備します。

# 必要なライブラリをインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import japanize_matplotlib  # 日本語表示用

warnings.filterwarnings("ignore")
pd.options.display.float_format = '{:.4%}'.format

# quantechiaを使用してデータを取得
from quantechia.data.data_fetcher import FinancialDataFetcher

# 日付範囲の設定
start_date = '2021-01-01'
end_date = '2023-12-31'

# 資産のティッカー
assets = ['AAPL', 'MSFT', 'AMZN', 'GOOG', 'META', 'TSLA', 'NVDA', 'JPM', 
          'V', 'PG', 'JNJ', 'UNH', 'HD', 'BAC', 'XOM', 'PFE', 'DIS', 'NFLX']

# ファクターのティッカー(ETF)
factors = ['MTUM', 'QUAL', 'VLUE', 'SIZE', 'USMV']

# すべてのティッカーをリストに結合
all_tickers = assets + factors

# データを取得
fetcher = FinancialDataFetcher()
historical_data = fetcher.get_historical_data("yahoo", tickers=all_tickers, start=start_date, end=end_date)
data = historical_data['Close']

# リターンの計算
X = data[factors].pct_change().dropna()  # ファクターのリターン
Y = data[assets].pct_change().dropna()   # 資産のリターン

ここでは、主要なテック企業や金融、消費財企業など18銘柄と、5つの代表的なファクターETFのデータを取得しています。2021年から2023年までの3年間のデータを使用して分析を行います。

ローディング行列の計算

次に、各資産がどのファクターにどれだけ影響を受けているかを表す「ローディング行列」を計算します。ここでは、ステップワイズ回帰を使用しています。

import riskfolio as rp

# ステップワイズ回帰を使用してローディング行列を計算
step = 'Forward'  # 前向きステップワイズ回帰
loadings = rp.loadings_matrix(X=X, Y=Y, stepwise=step)

# ヒートマップとして視覚化
plt.figure(figsize=(12, 8))
sns.heatmap(loadings, cmap='RdYlGn', annot=True, fmt=".4f")
plt.title('ファクターローディング行列')
plt.show()

ローディング行列は、各資産(行)がどのファクター(列)にどれだけ感応するかを示します。例えば、値が0.5であれば、そのファクターが1%変動したとき、その資産は0.5%変動することを意味します。

基本的なポートフォリオ最適化

ローディング行列を計算したら、基本的なポートフォリオ最適化を行います。ここでは、シャープレシオ最大化を目的とします。

# ポートフォリオオブジェクトの構築
port = rp.Portfolio(returns=Y)

# インプットパラメータの推定方法を選択
method_mu = 'hist'    # 期待リターンの推定方法(ヒストリカルデータ使用)
method_cov = 'hist'   # 共分散行列の推定方法(ヒストリカルデータ使用)

# 資産の統計情報を計算
port.assets_stats(method_mu=method_mu, method_cov=method_cov)

# ファクターの設定と統計情報を計算
port.factors = X
port.factors_stats(method_mu=method_mu, method_cov=method_cov)

# 最適化パラメータの設定
port.alpha = 0.05     # 信頼水準
model = 'FM'          # ファクターモデル
rm = 'MV'             # リスク指標(分散)
obj = 'Sharpe'        # 目的関数(シャープレシオ最大化)
hist = False          # 歴史的シナリオを使用しない
rf = 0                # リスクフリーレート
l = 0                 # リスク回避係数

# 最適化を実行
w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist)

# ポートフォリオの構成を可視化
plt.figure(figsize=(10, 6))
ax = rp.plot_pie(w=w, title='シャープレシオ最大化ポートフォリオ', others=0.05, cmap="tab20")
plt.show()

ここでのポイントはmodel='FM'を設定していることです。これにより、通常の資産リターンではなく、ファクターモデルに基づいてリスクとリターンを計算します。

また、効率的フロンティアも計算して可視化できます:

points = 30  # フロンティア上の点の数

# 効率的フロンティアを計算
frontier = port.efficient_frontier(model=model, rm=rm, points=points, rf=rf, hist=hist)

# 効率的フロンティアをプロット
mu = port.mu_fm      # 期待リターン
cov = port.cov_fm    # 共分散行列
returns = port.returns_fm  # 資産のリターン

plt.figure(figsize=(10, 6))
ax = rp.plot_frontier(w_frontier=frontier, mu=mu, cov=cov, returns=returns, rm=rm,
                     rf=rf, alpha=0.05, cmap='viridis', w=w, 
                     label='最適リスク調整リターンポートフォリオ',
                     marker='*', s=16, c='r')
plt.show()

# 効率的フロンティアの構成をエリアチャートでプロット
plt.figure(figsize=(10, 6))
ax = rp.plot_frontier_area(w_frontier=frontier, cmap="tab20")
plt.show()

ファクター制約の追加

ここからが本題です。ファクターへのエクスポージャーに制約を加えて最適化を行います。

# リスクファクター制約の作成
constraints = {'Disabled': [False, False, False, False, False],
               'Factor': ['MTUM', 'QUAL', 'SIZE', 'USMV', 'VLUE'],
               'Sign': ['<=', '<=', '<=', '>=', '<='],
               'Value': [-0.2, 0.8, 0.4, 0.6, 0.7],
               'Relative Factor': ['', 'USMV', '', '', '']}

constraints = pd.DataFrame(constraints)

# 制約条件の行列を計算
C, D = rp.factors_constraints(constraints, loadings)

# 制約を設定
port.ainequality = C
port.binequality = D

# 制約付きで最適化を実行
w_constrained = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist)

# 制約付きポートフォリオの構成を可視化
plt.figure(figsize=(10, 6))
ax = rp.plot_pie(w=w_constrained, title='制約付き最適ポートフォリオ', others=0.05, cmap="tab20")
plt.show()

この例で設定している制約は以下の通りです:

  • モメンタム(MTUM)へのエクスポージャーは-0.2以下に制限
  • クオリティ(QUAL)へのエクスポージャーは0.8以下に制限
  • サイズ(SIZE)ファクターへのエクスポージャーは0.4以下に制限
  • 低ボラティリティ(USMV)へのエクスポージャーは0.6以上を確保
  • バリュー(VLUE)へのエクスポージャーは0.7以下に制限
  • クオリティ(QUAL)はUSMVに対する相対的な制約(QUAL-USMV ≤ 0.8)

Relative Factor列が空でない場合、その行のファクターは指定されたファクターに対する相対的な制約になります。

様々なリスク指標での最適化

最後に、様々なリスク指標を用いた最適化を比較してみましょう。

# 利用可能なリスク指標
rms = ['MV', 'MAD', 'MSV', 'FLPM', 'SLPM', 'CVaR',
       'EVaR', 'WR', 'MDD', 'ADD', 'CDaR', 'UCI']

w_s = pd.DataFrame([])

# hist = False: リスク指標はファクターモデルに基づく期待リターンを使用して計算
hist = False
for i in rms:
    w = port.optimization(model=model, rm=i, obj=obj, rf=rf, l=l, hist=hist)
    w_s = pd.concat([w_s, w], axis=1)

w_s.columns = rms

# ポートフォリオのウェイト比較をプロット
plt.figure(figsize=(14, 6))
w_s.plot.bar()
plt.title('様々なリスク指標での資産配分比較')
plt.xlabel('資産')
plt.ylabel('ウェイト')
plt.show()

riskfolioライブラリでは、以下のような様々なリスク指標を使用できます:

  • MV: 分散(標準的なマルコウィッツモデル)
  • MAD: 平均絶対偏差
  • MSV: 半分散(下方リスクのみを考慮)
  • FLPM/SLPM: 下方部分モーメント
  • CVaR/EVaR: 条件付きバリューアットリスク/エントロピーバリューアットリスク
  • MDD/ADD/CDaR: 最大ドローダウン関連指標
  • WR: 最悪のリターン
  • UCI: アップサイドキャプチャー指標

これらのリスク指標を使い分けることで、投資家の選好や市場環境に合わせたポートフォリオを構築できます。

戦略の実装と評価

最後に、quantechiaライブラリを使って、定期的にリバランスを行うファクター制約ポートフォリオ戦略を実装します。

from quantechia.strategy import risk

# ファクター制約を設定
constraints = {'Disabled': [False, False, False, False, False],
               'Factor': ['MTUM', 'QUAL', 'SIZE', 'USMV', 'VLUE'],
               'Sign': ['<=', '<=', '<=', '>=', '<='],
               'Value': [1, 0.8, 1, 0.1, 0.9],
               'Relative Factor': ['', 'USMV', '', '', '']}

constraints_df = pd.DataFrame(constraints)

# 戦略を初期化
strategy = risk.FactorStrategy(
    price_data=data[assets],                  # 資産の価格データ
    factors_data=data[factors],               # ファクターの価格データ
    factor_constraints=constraints_df,        # 制約条件
    lookback=100,                             # リターンのウィンドウサイズ
    rebalance_freq='ME',                      # リバランス頻度(月次)
)

# 戦略を評価
performance = strategy.evaluate()

この戦略では、100日間のデータを使用して毎月リバランスを行います。ファクター制約は前述のものと似ていますが、より緩やかな制約を設定しています。

まとめ

ファクター制約付きポートフォリオ最適化は、伝統的なポートフォリオ最適化手法を拡張し、より洗練された資産配分を可能にします。主なメリットは以下の通りです:

  1. リスク要因の明示的な制御: 特定のファクターへのエクスポージャーを制限することで、意図しないリスクを排除
  2. 市場見通しの反映: マクロ経済見通しや市場予測に基づいて、特定のファクターへのエクスポージャーを戦略的に調整
  3. モデルリスクの低減: 単一の最適化モデルに頼るのではなく、ファクターを通じた多角的な視点を取り入れる

注意点としては、ファクターの選択やローディング行列の推定には不確実性が伴うこと、過度に厳しい制約を設けると最適化の自由度が失われることなどが挙げられます。

実務では、ファクター制約を設定する際には、投資戦略の目的や投資家の選好、市場環境などを総合的に考慮することが重要です。また、バックテストだけでなく、アウトオブサンプルテストやストレステストなどを通じて、戦略の頑健性を検証することも欠かせません。

ファクター制約付きポートフォリオ最適化は、定量的投資の強力なツールであり、適切に活用することで、より効率的なリスク・リターンプロファイルを持つポートフォリオ構築が可能になります。

タイトルとURLをコピーしました