Pythonで資産管理を自動計算!理想的なポートフォリオの効率的なリバランス法

  • URLをコピーしました!

当ブログのコンテンツ・情報について、できる限り正確な情報を提供するように努めておりますが、正確性や安全性を保証するものではありません。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますので、予めご了承ください。

投資信託や株式、仮想通貨など複数の資産を保有していると、理想のポートフォリオを維持するための計算が複雑になり、余計な時間がかかってしまいます。

本記事では、Pythonを活用して各資産クラスへの追加投資額を計算し、リバランスや理想的なポートフォリオに近づけるためのプログラミングを紹介します。

NISAやiDeCo、企業型DC、さらにはビットコインやゴールドなど、多岐にわたる資産を効率的に管理したい方は必見です!

目次

この記事で使用するPythonライブラリ

この記事では以下のPythonライブラリ・外部ツールを使用します。

ライブラリ用途ライセンス
pandasデータ解析修正BSD

売却リバランス

理想のポートフォリオよりも価格が高騰している投資商品を売却し、価格が下落している投資商品を購入しリバランスするためのプログラムです。

current_holdings, ideal_ratiosは辞書型の変数なので、下記以外の口座や投資商品の追加も容易です。

import pandas as pd

# 現在の保有額 (円)
current_holdings = {
    "NISA": {
        "国内株式": 290240,
        "先進国株式": 225621,
        "新興国株式": 201107,
        "国内債券": 301509,
        "先進国債券": 371693,
        "新興国債券": 307843,
        "国内REIT": 357113,
        "先進国REIT": 330850,
        "新興国REIT": 357639
    },
    "iDeCo": {
        "国内株式": 153814,
        "先進国株式": 117181,
        "新興国株式": 114800,
        "国内債券": 116184,
        "先進国債券": 126093,
        "新興国債券": 116184,
        "国内REIT": 135680,
        "先進国REIT": 116888,
        "新興国REIT": 125298
    },
    "企業型DC": {
        "国内株式": 240362,
        "先進国株式": 204439,
        "新興国株式": 202062,
    },
    "その他": {
        "BTC": 360323,
        "Gold": 310624,
    }
}

# 理想割合 (%)
ideal_ratios = {
    "国内株式": 15,
    "先進国株式": 30,
    "新興国株式": 10,
    "国内債券": 10,
    "先進国債券": 5,
    "新興国債券": 5,
    "国内REIT": 5,
    "先進国REIT": 5,
    "新興国REIT": 5,
    "BTC": 5,
    "Gold": 5
}

# 現在の総保有額を計算
def calculate_totals(holdings):
    total_assets = {}
    for category, assets in holdings.items():
        for asset, value in assets.items():
            total_assets[asset] = total_assets.get(asset, 0) + value
    return total_assets

total_holdings = calculate_totals(current_holdings)
total_value = sum(total_holdings.values())

# 理想的な保有額を計算
ideal_values = {asset: total_value * ratio / 100 for asset, ratio in ideal_ratios.items()}

# 必要な調整額を計算
adjustments = {asset: ideal_values[asset] - total_holdings.get(asset, 0) for asset in ideal_ratios.keys()}

# 売却額と購入額を分ける
sell_amounts = {asset: -adjustment if adjustment < 0 else 0 for asset, adjustment in adjustments.items()}
buy_amounts = {asset: adjustment if adjustment > 0 else 0 for asset, adjustment in adjustments.items()}

# 調整後割合を計算
adjusted_totals = {asset: total_holdings.get(asset, 0) + adjustments[asset] for asset in ideal_ratios.keys()}
adjusted_ratios = {asset: (adjusted_totals[asset] / total_value) * 100 for asset in ideal_ratios.keys()}

# 結果を出力
# 売却額を表示
print("売却額 (円):")
print("\n".join([f"{asset}: {int(value):,}" for asset, value in sell_amounts.items() if value > 0]))

# 購入額を表示
print("\n購入額 (円):")
print("\n".join([f"{asset}: {int(value):,}" for asset, value in buy_amounts.items() if value > 0]))

# 調整後割合を表示
print("\nTotalの調整後割合:")
for asset, ratio in adjusted_ratios.items():
    print(f"{asset}: {ratio:.1f}% ({int(adjusted_totals[asset]):,}円)")
出力例:
売却額 (円):
先進国債券: 238,608
新興国債券: 164,849
国内REIT: 233,615
先進国REIT: 188,560
新興国REIT: 223,759
BTC: 101,145
Gold: 51,446

購入額 (円):
国内株式: 93,116
先進国株式: 1,007,823
新興国株式: 385
国内債券: 100,661

Totalの調整後割合:
国内株式: 15.0% (777,532円)
先進国株式: 30.0% (1,555,064円)
新興国株式: 10.0% (518,354円)
国内債券: 10.0% (518,354円)
先進国債券: 5.0% (259,177円)
新興国債券: 5.0% (259,177円)
国内REIT: 5.0% (259,177円)
先進国REIT: 5.0% (259,177円)
新興国REIT: 5.0% (259,177円)
BTC: 5.0% (259,177円)
Gold: 5.0% (259,177円)

ノーセルリバランス

現在保有の資産を売却せずに、追加投資のみでリバランスをするプログラムです。

投資商品は小数点以下の価格で購入することができないので、精度を上げれば上げるほど投資額が増えてしまいます。ここでは、toleranceで精度(許容誤差)を指定出来るようにしています。

import pandas as pd

# 現在の保有額 (円)
current_holdings = {
    "NISA": {
        "国内株式": 290240,
        "先進国株式": 225621,
        "新興国株式": 201107,
        "国内債券": 301509,
        "先進国債券": 371693,
        "新興国債券": 307843,
        "国内REIT": 357113,
        "先進国REIT": 330850,
        "新興国REIT": 357639
    },
    "iDeCo": {
        "国内株式": 153814,
        "先進国株式": 117181,
        "新興国株式": 114800,
        "国内債券": 116184,
        "先進国債券": 126093,
        "新興国債券": 116184,
        "国内REIT": 135680,
        "先進国REIT": 116888,
        "新興国REIT": 125298
    },
    "企業型DC": {
        "国内株式": 240362,
        "先進国株式": 204439,
        "新興国株式": 202062,
    },
    "その他": {
        "BTC": 360323,
        "Gold": 310624,
    }
}

# 理想割合 (%)
ideal_ratios = {
    "国内株式": 15,
    "先進国株式": 30,
    "新興国株式": 10,
    "国内債券": 10,
    "先進国債券": 5,
    "新興国債券": 5,
    "国内REIT": 5,
    "先進国REIT": 5,
    "新興国REIT": 5,
    "BTC": 5,
    "Gold": 5
}

# 計算精度を指定
tolerance = 5  # 誤差の許容範囲 (%)

precision = 2  # 小数点以下桁数
max_iterations = 1000  # 最大反復回数

# 各資産の総額を計算
current_totals = {}
for account, assets in current_holdings.items():
    for asset, value in assets.items():
        current_totals[asset] = current_totals.get(asset, 0) + value

# 総資産を計算
current_total_assets = sum(current_totals.values())

# 理想割合に基づく理想的な総額を計算
ideal_totals = {
    asset: (ratio / 100) * current_total_assets for asset, ratio in ideal_ratios.items()
}

# 初期化
additional_investments = {asset: 0 for asset in current_totals}
adjusted_total_assets = current_total_assets
iteration = 0

# 反復調整
while iteration < max_iterations:
    iteration += 1

    # 調整後の割合を計算
    adjusted_ratios = {
        asset: ((current_totals[asset] + additional_investments[asset]) / adjusted_total_assets) * 100
        for asset in current_totals
    }

    # 誤差を計算
    deviations = {
        asset: ideal_ratios[asset] - adjusted_ratios[asset]
        for asset in adjusted_ratios
    }

    # 誤差が許容範囲内なら終了
    if all(abs(dev) <= tolerance for dev in deviations.values()):
        break

    # 誤差に基づいて追加投資額を調整
    for asset, dev in deviations.items():
        if dev > 0:  # 割合が不足している場合
            additional_investments[asset] += round(dev / 100 * adjusted_total_assets)

    # 調整後の総資産を更新
    adjusted_total_assets = current_total_assets + sum(additional_investments.values())

# 総追加投資額を計算
total_additional_investment = sum(additional_investments.values())

# 出力
print("\n調整後の追加投資額:")
df = pd.DataFrame({"追加投資額 (円)": additional_investments})
df["追加投資額 (円)"] = df["追加投資額 (円)"].map(lambda x: f"{x:,.0f}")
print(df)
print(f"\nTotal追加投資額: {total_additional_investment:,.0f}円")

print("\nTotalの調整後割合:")
for asset, ratio in adjusted_ratios.items():
    total_value = current_totals[asset] + additional_investments[asset]
    print(f"{asset}: {round(ratio, precision):.{precision}f}% ({total_value:,.0f}円)")
出力例:
調整後の追加投資額:
         追加投資額 (円)
国内株式       273,414
先進国株式    1,368,419
新興国株式      120,584
国内債券       220,860
先進国債券            0
新興国債券            0
国内REIT           0
先進国REIT          0
新興国REIT          0
BTC              0
Gold         8,653

Total追加投資額: 1,991,930円

Totalの調整後割合:
国内株式: 13.35% (957,830円)
先進国株式: 26.70% (1,915,660円)
新興国株式: 8.90% (638,553円)
国内債券: 8.90% (638,553円)
先進国債券: 6.94% (497,786円)
新興国債券: 5.91% (424,027円)
国内REIT: 6.87% (492,793円)
先進国REIT: 6.24% (447,738円)
新興国REIT: 6.73% (482,937円)
BTC: 5.02% (360,323円)
Gold: 4.45% (319,277円)

追加投資額が決まっている場合(積立リバランス)

追加投資額が決まっており、どの資産にどの程度追加投資すれば、理想のポートフォリに近づくか計算するプログラムです。また積立しながらリバランスする際の計算にも使用できます。

辞書型の変数 additional_investmentを追加し、投資額を設定できるようにしています。また、ideal_ratiosでは合計の割合の他に、各口座の割合も設定できるようにしています。

import pandas as pd

# 現在の保有額 (円)
current_holdings = {
    "NISA": {
        "国内株式": 290240,
        "先進国株式": 225621,
        "新興国株式": 201107,
        "国内債券": 301509,
        "先進国債券": 371693,
        "新興国債券": 307843,
        "国内REIT": 357113,
        "先進国REIT": 330850,
        "新興国REIT": 357639
    },
    "iDeCo": {
        "国内株式": 153814,
        "先進国株式": 117181,
        "新興国株式": 114800,
        "国内債券": 116184,
        "先進国債券": 126093,
        "新興国債券": 116184,
        "国内REIT": 135680,
        "先進国REIT": 116888,
        "新興国REIT": 125298
    },
    "企業型DC": {
        "国内株式": 240362,
        "先進国株式": 204439,
        "新興国株式": 202062,
    },
    "その他": {
        "BTC": 360323,
        "Gold": 310624,
    }
}

# 理想割合 (%)
ideal_ratios = {
    "total": {
        "国内株式": 9.1,
        "先進国株式": 9.1,
        "新興国株式": 9.1,
        "国内債券": 9.1,
        "先進国債券": 9.1,
        "新興国債券": 9.1,
        "国内REIT": 9.1,
        "先進国REIT": 9.1,
        "新興国REIT": 9.1,
        "BTC": 9.1,
        "Gold": 9.1,
    },
    "NISA": {
        "国内株式": 11.1,
        "先進国株式": 11.1,
        "新興国株式": 11.1,
        "国内債券": 11.1,
        "先進国債券": 11.1,
        "新興国債券": 11.1,
        "国内REIT": 11.1,
        "先進国REIT": 11.1,
        "新興国REIT": 11.1,
    },
    "iDeCo": {
        "国内株式": 11.1,
        "先進国株式": 11.1,
        "新興国株式": 11.1,
        "国内債券": 11.1,
        "先進国債券": 11.1,
        "新興国債券": 11.1,
        "国内REIT": 11.1,
        "先進国REIT": 11.1,
        "新興国REIT": 11.1
    },
    "企業型DC": {
        "国内株式": 33.3,
        "先進国株式": 33.3,
        "新興国株式": 33.3,
    },
    "その他": {
        "BTC": 50,
        "Gold": 50,
    }
}

# 追加投資額 (円)
additional_investment = {"NISA": 50000, "iDeCo": 20000, "企業型DC": 5000, "その他": 20000}

# 各口座の調整後の投資割合を計算する関数
def adjust_investment_plan(current, ideal_ratios, additional_investment):
    total_current = sum(current.values())
    total_after_investment = total_current + additional_investment

    # 理想額の計算
    ideal_amounts = {
        key: total_after_investment * (ratio / 100) for key, ratio in ideal_ratios.items()
    }

    # 初期投資計画
    investment_plan = {
        key: max(0, ideal_amounts[key] - current[key]) for key in current
    }

    total_investment = sum(investment_plan.values())
    if total_investment == 0:
        return {key: 0 for key in investment_plan}

    scaling_factor = additional_investment / total_investment
    adjusted_plan = {key: int(value * scaling_factor) for key, value in investment_plan.items()}

    # 合計が追加投資額に一致しない場合の調整
    adjusted_total = sum(adjusted_plan.values())
    difference = additional_investment - adjusted_total

    # 差分を分配(理想割合の高い順)
    sorted_keys = sorted(ideal_ratios.keys(), key=lambda k: ideal_ratios[k], reverse=True)
    for key in sorted_keys:
        if difference == 0:
            break
        adjusted_plan[key] += 1
        difference -= 1

    return adjusted_plan

# Totalの調整後割合を計算
def calculate_total_ratios(current_holdings, ideal_ratios, additional_investment):
    total_current = {key: 0 for key in ideal_ratios["total"]}
    total_adjusted_investment = {key: 0 for key in ideal_ratios["total"]}

    # 各口座の現在の保有額を合算
    for account in current_holdings:
        for key, value in current_holdings[account].items():
            total_current[key] += value

    # 各口座の追加投資額を反映
    for account, investment in additional_investment.items():
        adjusted_plan = adjust_investment_plan(
            current_holdings[account], ideal_ratios[account], investment
        )
        for key, value in adjusted_plan.items():
            total_current[key] += value
            total_adjusted_investment[key] += value

    # 総額計算
    total_sum = sum(total_current.values())
    total_ratios = {key: (value / total_sum) * 100 for key, value in total_current.items()}

    return total_ratios, total_adjusted_investment, total_sum

# 口座ごとの調整後投資額を計算
def calculate_investment_details():
    results = {}
    for account, investment in additional_investment.items():
        adjusted_plan = adjust_investment_plan(
            current_holdings[account], ideal_ratios[account], investment
        )
        total_adjusted_investment = sum(adjusted_plan.values())
        total_additional_ratio = {key: (value / total_adjusted_investment) * 100 if total_adjusted_investment > 0 else 0 for key, value in adjusted_plan.items()}
        details = pd.DataFrame({
            "現在の保有額 (円)": current_holdings[account],
            "理想額 (円)": {
                key: sum(current_holdings[account].values()) * (ideal_ratios[account][key] / 100)
                for key in current_holdings[account]
            },
            "調整後の追加投資額 (円)": adjusted_plan,
            "調整後の追加投資額の割合 (%)": total_additional_ratio
        })
        details = details.round(0).astype(int)
        results[account] = (details, total_adjusted_investment)
    return results

# 計算結果を出力
total_ratios, total_adjusted_investment, total_sum = calculate_total_ratios(current_holdings, ideal_ratios, additional_investment)
investment_details = calculate_investment_details()

for account, (details, total_investment) in investment_details.items():
    print(f"\n{account}の調整後の追加投資額:")
    print(details)
    print(f"\n{account}の追加投資額の合計: {total_investment} 円")

print("\nTotalの調整後割合:")
for key, value in total_ratios.items():
    print(f"{key}: {value:.2f}%")
出力例:
NISAの調整後の追加投資額:
         現在の保有額 (円)  理想額 (円)  調整後の追加投資額 (円)  調整後の追加投資額の割合 (%)
国内株式         290240   304541           4429                 9
先進国株式        225621   304541          18844                38
新興国株式        201107   304541          24312                49
国内債券         301509   304541           1914                 4
先進国債券        371693   304541              0                 0
新興国債券        307843   304541            501                 1
国内REIT       357113   304541              0                 0
先進国REIT      330850   304541              0                 0
新興国REIT      357639   304541              0                 0

NISAの追加投資額の合計: 50000 円

iDeCoの調整後の追加投資額:
         現在の保有額 (円)  理想額 (円)  調整後の追加投資額 (円)  調整後の追加投資額の割合 (%)
国内株式         153814   124556              1                 0
先進国株式        117181   124556           3502                18
新興国株式        114800   124556           4371                22
国内債券         116184   124556           3865                19
先進国債券        126093   124556            249                 1
新興国債券        116184   124556           3865                19
国内REIT       135680   124556              0                 0
先進国REIT      116888   124556           3608                18
新興国REIT      125298   124556            539                 3

iDeCoの追加投資額の合計: 20000 円

企業型DCの調整後の追加投資額:
       現在の保有額 (円)  理想額 (円)  調整後の追加投資額 (円)  調整後の追加投資額の割合 (%)
国内株式       240362   215405              1                 0
先進国株式      204439   215405           2285                46
新興国株式      202062   215405           2714                54

企業型DCの追加投資額の合計: 5000 円

その他の調整後の追加投資額:
      現在の保有額 (円)  理想額 (円)  調整後の追加投資額 (円)  調整後の追加投資額の割合 (%)
BTC       360323   335474              0                 0
Gold      310624   335474          20000               100

その他の追加投資額の合計: 20000 円

Totalの調整後割合:
国内株式: 13.05%
先進国株式: 10.83%
新興国株式: 10.41%
国内債券: 8.02%
先進国債券: 9.44%
新興国債券: 8.12%
国内REIT: 9.34%
先進国REIT: 8.55%
新興国REIT: 9.16%
BTC: 6.83%
Gold: 6.26%

まとめ

本記事では、複雑化しやすいポートフォリオ管理を効率化するために、Pythonを活用したプログラムをご紹介しました。投資資産のリバランスや理想的な配分への調整を自動化することで、計算にかかる手間を削減し、最適な投資判断をサポートできます。

「売却リバランス」では、価格変動による資産配分のズレを是正し、効率的に資産を再配分する方法です。一方、「ノーセルリバランス」や「追加投資額が決まっている場合」のシナリオでは、売却を伴わずに追加投資だけでリバランスを達成します。

これらのアプローチは、投資目標やリスク許容度に応じて柔軟に応用可能です。資産管理を手軽に効率化したい方は、ぜひプログラムを活用し、自身のポートフォリオ運用にお役立てください。

よかったらシェアしてね!
  • URLをコピーしました!
目次