ポジションサイジングとは?何株買うかで成績は変わる【Python実装】


戦略の話になると、みんな「どの銘柄を買うか」ばかり議論します。しかし実際の成績を決めるのは、その前段にある 「いくら買うか」 ——ポジションサイジングです。

同じ売買シグナルでも、資金の配り方を変えるだけでリターンもドローダウンも大きく変わります。この記事では、代表的な3つの配分方法をPythonで実装し、その差を確認します。

⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。

目次

  1. なぜ配分が成績を決めるのか
  2. 方法1:等金額(イコールウェイト)
  3. 方法2:ボラティリティ調整(インバース・ボラティリティ)
  4. 方法3:リスクパリティの考え方
  5. 3手法の比較コード
  6. どれを選ぶべきか
  7. まとめ

1. なぜ配分が成績を決めるのか

10銘柄に投資するとき、資金100万円をどう配るか。素朴には「10万円ずつ」ですが、ここに罠があります。

値動きの荒さ(ボラティリティ)は銘柄ごとに全く違うからです。

銘柄年率ボラティリティ10万円投資時の年間変動幅
電力株15%±1.5万円
半導体株60%±6万円

同じ10万円でも、ポートフォリオに与える影響は4倍違います。等金額で配ったつもりでも、実際は「値動きの荒い銘柄にリスクを集中投資」しているのと同じです。

2. 方法1:等金額(イコールウェイト)

最も単純。資金を銘柄数で割るだけです。

def equal_weight(symbols, capital):
    """等金額配分:資金を均等に割る"""
    per = capital / len(symbols)
    return {s: per for s in symbols}

# 例
print(equal_weight(["AAPL", "MSFT", "NVDA"], 1_000_000))
# {'AAPL': 333333.3, 'MSFT': 333333.3, 'NVDA': 333333.3}
  • ✅ 単純・理解しやすい・パラメータ不要で過剰最適化しにくい
  • ❌ 荒い銘柄にリスクが偏る

3. 方法2:ボラティリティ調整(インバース・ボラティリティ)

「ボラティリティの逆数」で重みを付ける方法。荒い銘柄は少なく、穏やかな銘柄は多く買います。

$$ w_i = \frac{1/\sigma_i}{\sum_j 1/\sigma_j} $$

import numpy as np
import pandas as pd
import yfinance as yf

def inverse_vol_weight(returns: pd.DataFrame, capital: float) -> dict:
    """インバース・ボラティリティ配分

    returns: 各銘柄の日次リターン(列=銘柄)
    """
    vol = returns.std() * np.sqrt(252)     # 年率ボラティリティ
    vol = vol.replace(0, np.nan).dropna()
    inv = 1 / vol
    weights = inv / inv.sum()
    return (weights * capital).round(0).to_dict()

# 使用例
px = yf.download(["AAPL", "MSFT", "NVDA", "KO"], period="1y",
                 progress=False)["Close"]
rets = px.pct_change().dropna()
print(inverse_vol_weight(rets, 1_000_000))

これだけで、各銘柄がポートフォリオに与えるリスクがおおむね均等になります。実装は数行なのに効果は大きい——コスパ最強の改善です。

💡 ボラティリティの計測期間は60〜120日が一般的。短すぎると重みがコロコロ変わって取引コストがかさみます。

4. 方法3:リスクパリティの考え方

インバース・ボラティリティは「銘柄同士の相関」を無視しています。リスクパリティは相関まで含めて、各銘柄のリスク寄与度を均等にします。

  • 半導体株を5銘柄持っていると、それらは一緒に動く(相関が高い)
  • 相関を考慮すると「実質1銘柄に集中している」と分かる

厳密なリスクパリティは最適化計算が必要ですが、簡易版なら相関を使って調整できます。

def risk_contribution(weights: np.ndarray, cov: np.ndarray) -> np.ndarray:
    """各銘柄のリスク寄与度を計算"""
    port_vol = np.sqrt(weights @ cov @ weights)
    marginal = cov @ weights
    return weights * marginal / port_vol

# 寄与度が偏っていないか確認する
cov = rets.cov().values * 252
w = np.array([0.25, 0.25, 0.25, 0.25])
rc = risk_contribution(w, cov)
print("リスク寄与度:", (rc / rc.sum()).round(3))
# 例: [0.18 0.21 0.49 0.12] → 3番目が半分のリスクを占めている

上の出力のように、等金額でもリスク寄与は全く均等ではないことが数字で見えます。

5. 3手法の比較コード

同じ銘柄群を3つの方法で運用したらどうなるか。

def backtest_weights(rets: pd.DataFrame, weights: dict) -> pd.Series:
    """固定ウェイトでのポートフォリオ日次リターン"""
    w = pd.Series(weights)
    w = w / w.sum()
    return (rets[w.index] * w).sum(axis=1)

def summarize(daily: pd.Series) -> dict:
    cum = (1 + daily).cumprod()
    peak = cum.cummax()
    mdd = ((cum - peak) / peak).min() * 100
    sharpe = daily.mean() / daily.std() * np.sqrt(252)
    return {
        "リターン(%)": round((cum.iloc[-1] - 1) * 100, 1),
        "最大DD(%)": round(mdd, 1),
        "シャープ": round(sharpe, 2),
    }

eq = {s: 1 for s in rets.columns}
iv = inverse_vol_weight(rets, 1.0)

print("等金額      :", summarize(backtest_weights(rets, eq)))
print("ボラ調整    :", summarize(backtest_weights(rets, iv)))

多くの場合、ボラティリティ調整版はリターンが少し下がる代わりに、最大ドローダウンが目に見えて浅くなり、シャープレシオが改善します。シャープレシオと最大ドローダウンで見たとおり、続けられるかどうかを決めるのはドローダウンの深さです。

6. どれを選ぶべきか

状況おすすめ
銘柄数が少ない・同じ性質の資産等金額(単純さが正義)
性質の違う資産が混在ボラティリティ調整
相関が高い銘柄が固まりがちリスクパリティ(またはセクター上限を設ける)

私の意見では、個人の実装ならインバース・ボラティリティが最良のバランスです。数行で書けて、パラメータが1つ(計測期間)しかなく、過剰最適化の余地が小さい。

一方で注意点:ボラティリティ調整は「低ボラ銘柄への集中」を生むことがあります。リスクを抑えた結果、上昇相場で置いていかれるのも実際に起きます。

7. まとめ

  • 「何を買うか」と同じくらい「いくら買うか」が成績を決める
  • 等金額は単純だが、リスクは荒い銘柄に偏る
  • インバース・ボラティリティは数行で実装でき、ドローダウンを浅くしやすい
  • リスクパリティは相関まで考慮。まずはリスク寄与度を計算して偏りを確認するだけでも価値がある
  • 配分方法を変えたら、必ずウォークフォワード検証で確かめる

戦略のアイデアが尽きたと感じたら、銘柄選択ではなく配分を見直してみてください。同じシグナルのまま成績が変わります。

関連記事 → 時系列モメンタム(TSMOM)とは?


質問や感想は X (@QuantNobu) までどうぞ。

⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。