ランダムウォークへの挑戦⑩ | 資産推移とドローダウンを分析する(2026/8/1)

ランダムウォーク 未分類

前回は、零号機の連勝・連敗を分析しました。

最大連勝は11回、最大連敗は9回となり、通常は1~2回程度で連勝・連敗が終了することが分かりました。前回の記事では、最大連敗数を把握することで、システムの異常を判断する材料になると考えました。

しかし、運用時の精神的・資金的な負担を考えるうえでは、連敗数だけでは十分ではありません。

例えば、同じ5連敗でも、1回当たりの損失が小さい場合と大きい場合とでは、資産への影響が異なります。

そこで今回は、取引ごとの累積損益を計算し、零号機が過去の最高地点からどの程度下落したのか、最大ドローダウンを分析します。

今回確認したいこと

・取引ごとの累積損益
・過去最高損益からの下落額
・最大ドローダウン額
・最大ドローダウンが発生した期間
・ドローダウンの底から回復するまでの取引数
・最大ドローダウン額発生時の下落率
・最大相対ドローダウン率

分析結果はCSVに保存するとともに、資産推移とドローダウンの推移をグラフにします。

コード修正

前回作成したPythonファイル「analyze.py」に以下のコードを追記します。

今回追加したPythonコードを見る(クリックで開く)
# ==============================
# ドローダウン分析
# ==============================

# 決済時間の順番に並べる
drawdown_trades = (
    holding_trades
    .sort_values("決済時間")
    .reset_index(drop=True)
    .copy()
)

# 取引順を追加
drawdown_trades["取引順"] = range(
    1,
    len(drawdown_trades) + 1
)

# 累積損益を計算
drawdown_trades["累積損益"] = (
    drawdown_trades["損益"].cumsum()
)

# その時点までの累積損益の最高値を計算
drawdown_trades["過去最高損益"] = (
    drawdown_trades["累積損益"].cummax()
)

# 過去最高損益からの下落額を計算
drawdown_trades["ドローダウン"] = (
    drawdown_trades["累積損益"]
    - drawdown_trades["過去最高損益"]
)

# 最大ドローダウン
max_drawdown = (
    drawdown_trades["ドローダウン"].min()
)

# 最大ドローダウンを記録した行
max_drawdown_row = (
    drawdown_trades.loc[
        drawdown_trades["ドローダウン"].idxmin()
    ]
)

# 最大ドローダウンの底となった取引番号と時間
drawdown_bottom_order = int(
    max_drawdown_row["取引順"]
)

drawdown_bottom_time = (
    max_drawdown_row["決済時間"]
)

# 最大ドローダウンの底までのデータを対象に、
# 直前の最高損益を記録した行を探す
before_bottom = drawdown_trades[
    drawdown_trades["取引順"]
    <= drawdown_bottom_order
].copy()

drawdown_peak_value = (
    max_drawdown_row["過去最高損益"]
)

peak_rows = before_bottom[
    before_bottom["累積損益"]
    == drawdown_peak_value
]

# 最大ドローダウン開始地点は、
# 底に到達する前で最後に最高値を記録した行
drawdown_peak_row = peak_rows.iloc[-1]

drawdown_peak_order = int(
    drawdown_peak_row["取引順"]
)

drawdown_peak_time = (
    drawdown_peak_row["決済時間"]
)

# 最大ドローダウン後に、
# ドローダウン開始前の最高値まで回復したかを確認
after_bottom = drawdown_trades[
    drawdown_trades["取引順"]
    > drawdown_bottom_order
].copy()

recovery_rows = after_bottom[
    after_bottom["累積損益"]
    >= drawdown_peak_value
]

if not recovery_rows.empty:
    recovery_row = recovery_rows.iloc[0]

    recovery_order = int(
        recovery_row["取引順"]
    )

    recovery_time = (
        recovery_row["決済時間"]
    )

    recovery_trade_count = (
        recovery_order
        - drawdown_bottom_order
    )

    total_drawdown_trade_count = (
        recovery_order
        - drawdown_peak_order
    )

    drawdown_duration = (
        recovery_time
        - drawdown_peak_time
    )

    recovery_status = "回復済み"

else:
    recovery_order = None
    recovery_time = None
    recovery_trade_count = None
    total_drawdown_trade_count = None
    drawdown_duration = None
    recovery_status = "未回復"


# ==============================
# 結果を表示
# ==============================

print("\n===== ドローダウン分析 =====")

print(
    f"最大ドローダウン: "
    f"{max_drawdown:.2f}"
)

print(
    f"ドローダウン開始時の累積損益: "
    f"{drawdown_peak_value:.2f}"
)

print(
    f"ドローダウン開始取引: "
    f"{drawdown_peak_order}回目"
)

print(
    f"ドローダウン開始時間: "
    f"{drawdown_peak_time}"
)

print(
    f"ドローダウンの底: "
    f"{drawdown_bottom_order}回目"
)

print(
    f"底を記録した時間: "
    f"{drawdown_bottom_time}"
)

print(
    f"回復状況: "
    f"{recovery_status}"
)

if recovery_status == "回復済み":
    print(
        f"回復した取引: "
        f"{recovery_order}回目"
    )

    print(
        f"回復した時間: "
        f"{recovery_time}"
    )

    print(
        f"底から回復までの取引数: "
        f"{recovery_trade_count}回"
    )

    print(
        f"開始から回復までの取引数: "
        f"{total_drawdown_trade_count}回"
    )

    print(
        f"開始から回復までの期間: "
        f"{drawdown_duration}"
    )


# ==============================
# CSVに保存
# ==============================

drawdown_trades.to_csv(
    "results/drawdown_trades.csv",
    index=False,
    encoding="utf-8-sig"
)

print(
    "\nドローダウン分析結果を"
    "results/drawdown_trades.csv"
    "に保存しました。"
)


# ==============================
# 資産推移グラフ
# ==============================

plt.figure(figsize=(14, 7))

plt.plot(
    drawdown_trades["取引順"],
    drawdown_trades["累積損益"],
    label="Cumulative Profit"
)

plt.plot(
    drawdown_trades["取引順"],
    drawdown_trades["過去最高損益"],
    linestyle="--",
    label="Running Maximum"
)

plt.scatter(
    drawdown_peak_order,
    drawdown_peak_value,
    label="Drawdown Peak"
)

plt.scatter(
    drawdown_bottom_order,
    max_drawdown_row["累積損益"],
    label="Drawdown Bottom"
)

if recovery_status == "回復済み":
    plt.scatter(
        recovery_order,
        recovery_row["累積損益"],
        label="Recovery"
    )

plt.title("Cumulative Profit and Maximum Drawdown")
plt.xlabel("Trade Number")
plt.ylabel("Cumulative Profit")
plt.legend()
plt.grid()
plt.tight_layout()

plt.savefig(
    "results/cumulative_profit_drawdown.png",
    dpi=150
)

plt.close()


# ==============================
# ドローダウン推移グラフ
# ==============================

plt.figure(figsize=(14, 7))

plt.plot(
    drawdown_trades["取引順"],
    drawdown_trades["ドローダウン"]
)

plt.axhline(
    y=0,
    linestyle="--"
)

plt.scatter(
    drawdown_bottom_order,
    max_drawdown,
    label="Maximum Drawdown"
)

plt.title("Drawdown")
plt.xlabel("Trade Number")
plt.ylabel("Drawdown")
plt.legend()
plt.grid()
plt.tight_layout()

plt.savefig(
    "results/drawdown.png",
    dpi=150
)

plt.close()

print(
    "資産推移とドローダウンのグラフを"
    "resultsフォルダに保存しました。"
)

コードのポイント

.cumsum()は、上から順番に数値を足していく処理です。

.cummax()は、その行までに現れた最大値を返します。

ドローダウンはマイナスで表されるため、最も小さい数値が最大ドローダウンです。.min()は値そのものを取得し、.idxmin()はその値が入っている行番号を取得します。

出力結果

分析

分析の結果、零号機の最大ドローダウン額は29,741.69円となりました。

このドローダウンは、2017年4月24日の355回目の取引を起点として発生し、2017年8月11日の401回目の取引で底に到達しています。最高値から底までの期間は約108日、取引数では46回でした。

ドローダウン開始時の資産は123,643.24円で、底を記録した時点の資産は93,901.55円です。

したがって、最大ドローダウン額が発生した際の下落率は、

29,741.69円 ÷ 123,643.24円 × 100
=約24.1%

となります。

最大ドローダウン額が約3万円だからといって、初期資金5万円に対する割合をそのまま計算するのは適切ではありません。ドローダウン率は、原則として、そのドローダウンが始まる直前の最高資産を基準に計算します。

一方、割合として最も深いドローダウンは別の時期に発生していました。

最大相対ドローダウン率は約41.9%です。

このときの最高資産は66,227.36円、ドローダウン後の資産は38,471.34円で、下落額は27,756.02円でした。

つまり、零号機では、

  • 最も大きく減った金額:29,741.69円
  • そのときの下落率:約24.1%
  • 最も大きく減った割合:約41.9%

となり、最大ドローダウン額と最大ドローダウン率が発生した時期は一致していません。

その後、最大ドローダウン額が発生する前の最高資産を回復したのは、2018年5月17日の507回目の取引です。

ドローダウン開始から回復までは152回の取引を要し、期間は約388日でした。

最大ドローダウンの金額だけでなく、回復まで約1年かかっている点も重要です。

また、グラフを見ると、2万円前後の比較的大きなドローダウンが複数回発生しています。零号機は、小さな下落から比較的早く回復する場面が多い一方、相場環境によっては長期間にわたって最高資産を更新できない特徴があると考えられます。

連敗が終了しても、資産が過去最高値まで回復したとは限りません。

そのため、実際の運用では、連敗数だけでなく、

  • ドローダウン額
  • ドローダウン率
  • 回復までの期間

をそれぞれ確認する必要があります。

まとめ

零号機の最大ドローダウン額は29,741.69円でした。

このドローダウンが始まる直前の最高資産は123,643.24円だったため、最大ドローダウン額発生時の下落率は約24.1%です。

一方、割合として最も深い最大相対ドローダウン率は約41.9%でした。

最大ドローダウン額と最大相対ドローダウン率は、同じ意味ではありません。

固定ロットで運用している場合、資産が少ない初期ほど、同じ損失額でもドローダウン率は大きくなります。反対に、利益が積み上がった中盤や終盤では、同程度の損失額でも資産に対する割合は小さくなります。

また、最大ドローダウン額が発生してから、それ以前の最高資産を回復するまでには約1年を要しました。

そのため、零号機を運用する際には、損失額だけでなく、資産に対する下落率や、長期間にわたって最高資産を更新できない状態にも耐えられるかを考える必要があります。

今回の分析により、システムの異常を判断するための参考値として、次の基準を得ることができました。

  • 最大連敗:9回
  • 最大ドローダウン額:約3万円
  • 最大ドローダウン額発生時の下落率:約24.1%
  • 最大相対ドローダウン率:約41.9%
  • 最大ドローダウンからの回復期間:約1年

ただし、これらは過去のバックテスト上の最大値です。実運用では、過去の数値を上回る損失が発生する可能性も考慮する必要があります。

次回予告

これまでは、零号機の過去データを様々な角度から観測してきました。

しかし、分析だけではシステムは成長しません。

次回からは、観測結果をもとに改善のヒントを探していきます。

まずはATR(Average True Range)を利用し、相場のボラティリティと成績の関係を分析します。

「値動きの大きさ」は零号機の成績にどのような影響を与えているのでしょうか。

ランダムウォークへの挑戦は続きます。

関連記事

ランダムウォークへの挑戦⑪ | 零号機の性格診断──Chapter1「観測」 完結(2026/8/1)
ランダムウォークへの挑戦⑩ | 資産推移とドローダウンを分析する(2026/8/1)
ランダムウォークへの挑戦⑨ | 取引の連勝・連敗を分析する(2026/7/28)
ランダムウォークへの挑戦⑧ | 保有時間別の損益を分析する(2026/7/26)
ランダムウォークへの挑戦⑦ | 曜日別の損益を分析する(2026/7/23)
ランダムウォークへの挑戦⑥ | 時間帯別の損益を分析する(2026/7/22)
ランダムウォークへの挑戦⑤ | グラフ化(2026/7/5)
ランダムウォークへの挑戦④ |「零号機」の基本分析(2026/6/11)
ランダムウォークへの挑戦③ | 環境設定(2026/5/26)
ランダムウォークへの挑戦➁ | 小目標3つ設定(2026/5/25)
ランダムウォークへの挑戦① | はじめに(2026/5/25)

コメント

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