ランダムウォークへの挑戦⑤ | Pythonで月別損益をグラフ化する(2026/7/5)

TOP画像 未分類

先日ボーナスが入りました。
最近は物欲がないので特にボーナスが出たからと言って買いたいものがないのですが、
パソコンの作業効率向上のためにモニターをもう一台買おうか悩んでいます。

2026/10/4 購入しました。
画質も鮮明で値段にしてはコスパがとても良いと思います。 

それでは今回も作業進めていきます。

はじめに

前回は、MT4から出力したバックテスト結果「report.csv」をPythonで読み込み、「零号機」の基本成績を分析しました。

ランダムウォークへの挑戦④ |Pythonで「零号機」の基本成績を分析する(2026/6/11)
最近は、新しい仕事が忙しく、また、仕事後に毎日プールで泳いでいるのでなかなか作業が進みません。また、新年度になってから飲み会も増えました。職場で気の合う独身兄貴たちと4人で定期飲みしています♨ということで、今日は少し進めます。はじめに前回は…

総取引数、勝率、PF、平均利益・平均損失、最大ドローダウンなどを確認し、さらに月別損益も集計しました。

ただ、月別損益を数字だけで並べても、

「どの時期に大きく勝ち、どの時期に大きく負けているのか」

は少し分かりにくいです。

そこで今回は、前回作成した月別損益をPythonを使って棒グラフにして視覚化します。


今回やること

今回の処理は以下のとおりです。

月別損益を集計
      ↓
matplotlibで棒グラフを作成
      ↓
monthly_profit.pngとして保存
      ↓
利益月・損失月のばらつきを確認

Pythonでグラフを作成するために、今回はmatplotlibというライブラリを使います。


1.matplotlibを追加する

まず、WSLでUbuntu(Linux)に入り、分析用フォルダへ移動します。
WSLについては第3回の記事で簡単に書いています。

cd /mnt/c/Users/***/ea-analysis

そして、matplotlibを追加します。

uv add matplotlib

matplotlibは、Pythonでグラフを作成するための代表的なライブラリです。

uv

は、Pythonの環境やライブラリをまとめて管理するツールです。

今回作成する棒グラフだけでなく、折れ線グラフや散布図なども作成できます。


2.matplotlibを読み込む

Python側で、次のコードを追加します。

import matplotlib.pyplot as plt

pyplot は、matplotlibの中にあるグラフを描くための機能をまとめたものです。

ここでは、

matplotlib.pyplot

を、

plt

という短い名前で使えるようにしています。

以降、

plt.figure()
plt.bar()
plt.savefig()

といった形でグラフを操作できるようになります。


3.月別損益を集計する

前回、年月ごとの損益を集計しました。
これをもとにグラフ化を行います。

monthly_profit = (
    trades
    .groupby("年月")["損益"]
    .sum()
    .reset_index()
)

tradesはバックテスト結果から、closeのデータのみを抽出したDataFrameでした。

groupby()はDataFrameのデータを、指定した列の値ごとにグループ分けすることができます。

ここでは、

.groupby("年月")

で同じ年月の取引をまとめ、

["損益"]

で損益列を対象にして、

.sum()

で月ごとの損益を合計しています。

最後に、

.reset_index()

を使って、groupby()によってインデックスになった「年月」を、再び通常の列に戻しています。

これによって、集計結果は、

      年月        損益
0  2015-01   15586.10
1  2015-02    -422.18
2  2015-03  -12471.63

のようなDataFrameになります。

このあとグラフを作成するときに、

monthly_profit["年月"]
monthly_profit["損益"]

と、それぞれの列を指定して利用できるようになります。


4.グラフを作る領域を用意する

続いて、

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

でグラフを作成する領域を用意します。

figsizeはグラフの大きさです。

figsize=(14, 6)

なので、

横:14
縦:6

の比率で作成します。

今回のバックテストは10年以上あり、月数も多いため、横長のグラフにしました。


5.棒グラフを作成する

実際に棒グラフを作るのが、

plt.bar(
    monthly_profit.index,
    monthly_profit.values
)

です。

plt.bar()は棒グラフを作る関数です。

ここでは、

monthly_profit.index

を横軸、

monthly_profit.values

を縦軸にしています。

今回のmonthly_profitは、

年月        損益
2015-01    15586.10
2015-02     -422.18
2015-03   -12471.63
...

という構造なので、

横軸(monthly_profit.index)  → 年月
縦軸(monthly_profit.values) → 月間損益

というグラフになります。


6.タイトルと軸の名前を付ける

続いて、

plt.title("Monthly Profit")
plt.xlabel("Month")
plt.ylabel("Profit")

で、グラフのタイトルと軸の名前を設定します。


7.横軸の文字を90度回転する

月数が多いため、そのまま年月を表示すると文字同士が重なる可能性があります。

そこで、

plt.xticks(
    rotation=90
)

とします。

rotation=90は、

横軸の文字を90度回転する

という意味です。

これによって年月を縦向きに表示し、できるだけ読みやすくしています。


8.レイアウトを自動調整する

続いて、

plt.tight_layout()

を実行します。

これは、

グラフの文字やラベルが枠からはみ出さないように、自動的に余白を調整する

ための処理です。

今回のように横軸の文字を90度回転させる場合には特に役立ちます。


9.グラフを画像として保存する

完成したグラフを保存します。

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

plt.savefig()は、作成したグラフを画像ファイルとして保存するための関数です。

今回は、

results/monthly_profit.png

として保存します。

また、

dpi=150

で画像の解像度を指定しています。


10.グラフを閉じる

保存後には、

plt.close()

を実行します。

これは、作成したグラフを閉じる処理です。

今後、同じPythonプログラムの中で時間帯別・曜日別・保有時間別など複数のグラフを作成するため、前のグラフを閉じておきます。


実行結果

コードを実行すると、

results
└─ monthly_profit.png

として以下のpngファイルが作成されました。

棒グラフにすると、数字だけを眺めていたときよりも、零号機の成績にかなりばらつきがあることが分かります。


分析

月別損益をグラフにすると、数字の一覧だけを見ていたときよりも、「零号機」の特徴がかなり分かりやすくなりました。

まず目につくのは、月によって損益のばらつきがかなり大きいことです。

多くの月では損益が0円付近から数千円程度の範囲に収まっていますが、その中に大きな利益を出した月、大きな損失を出した月が点在しています。

特にバックテスト前半には、約+29,000円の大きな利益を出した月がある一方で、約-18,000円の損失を出した月もあります。

その後の期間を見ても、+10,000円を超える利益月や、-10,000円を下回る損失月が時折発生しています。

零号機は毎月安定して少しずつ利益を積み上げるタイプというより、

「普段は比較的小さな損益を繰り返しながら、ときどき大きく勝ったり負けたりする」

という特徴があるということです。

また、グラフ全体を見ると、特定の時期だけ一貫して利益が出ているわけでもありません。

利益月と損失月が入り交じりながら、長期間のバックテストでは最終的にプラスになっています。

前回の分析では、

  • 勝率:61.86%
  • PF:1.33
  • 総損益:+223,494.50円

という結果でした。

今回のグラフを見ると、その約22万円の利益が均等に積み上がったわけではないことが分かります。

そこで、次に分析したいのが

なぜ、これほど成績の良い月と悪い月が生まれるのか?

についてです。

このグラフだけでは、その原因までは分かりません。

時間帯による違いなのか、曜日による違いなのか。それとも、そのときの相場環境によるものなのか。

いずれにせよ、今回の分析で

「零号機の成績には、時期によって明確なムラがある」

ということがわかりました。

今回の完成コード

前回記事までに作成したコードに、今回のグラフ作成処理を追加すると、Pythonコード全文は次のようになります。

コード全文はこちら
import os
import pandas as pd
import matplotlib.pyplot as plt


# resultsフォルダが存在しない場合は作成
os.makedirs(
    "results",
    exist_ok=True
)


# CSV読み込み
df = pd.read_csv(
    "/mnt/c/MT4/report.csv",
    encoding="utf-8-sig"
)


# 決済済み取引(close)だけ抽出
trades = df[
    df["取引種別"] == "close"
].copy()


# 日時を変換
trades["時間"] = pd.to_datetime(
    trades["時間"],
    format="%Y.%m.%d %H:%M"
)


# 年月列を作成
trades["年月"] = (
    trades["時間"]
    .dt.strftime("%Y-%m")
)


# 基本集計
total_trades = len(trades)

wins = trades[
    trades["損益"] > 0
]

losses = trades[
    trades["損益"] < 0
]


# 勝ち数・負け数
win_count = len(wins)
loss_count = len(losses)


# 勝率
win_rate = (
    win_count
    / total_trades
    * 100
)


# 利益合計・損失合計
gross_profit = wins["損益"].sum()
gross_loss = losses["損益"].sum()


# プロフィットファクタ
profit_factor = (
    gross_profit / abs(gross_loss)
    if gross_loss != 0
    else float("nan")
)


# 損益・平均利益・平均損失
total_profit = trades["損益"].sum()
average_profit = wins["損益"].mean()
average_loss = losses["損益"].mean()


# 最大ドローダウン
trades["過去最高残高"] = (
    trades["残高"].cummax()
)

trades["ドローダウン"] = (
    trades["残高"]
    - trades["過去最高残高"]
)

max_balance_drawdown = (
    trades["ドローダウン"].min()
)


# 基本分析結果を表示
print("===== EA分析結果 =====")
print(f"総取引数: {total_trades}")
print(f"勝ち取引: {win_count}")
print(f"負け取引: {loss_count}")
print(f"勝率: {win_rate:.2f}%")
print(f"総損益: {total_profit:.2f}")
print(f"総利益: {gross_profit:.2f}")
print(f"総損失: {gross_loss:.2f}")
print(f"PF: {profit_factor:.2f}")
print(f"平均利益: {average_profit:.2f}")
print(f"平均損失: {average_loss:.2f}")
print(
    f"最大ドローダウン: "
    f"{max_balance_drawdown:.2f}"
)
print(
    f"最終残高: "
    f"{trades['残高'].iloc[-1]:.2f}"
)


# 月別損益
monthly_profit = (
    trades
    .groupby("年月")["損益"]
    .sum()
    .reset_index()
)

print("\n===== 月別損益 =====")
print(monthly_profit)


# ==============================
# 月別損益グラフ
# ==============================

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

plt.bar(
    monthly_profit["年月"],
    monthly_profit["損益"]
)

plt.title("Monthly Profit")
plt.xlabel("Month")
plt.ylabel("Profit")

plt.xticks(
    rotation=90
)

plt.tight_layout()

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

plt.close()

print(
    "月別損益グラフを "
    "results/monthly_profit.png "
    "として保存しました。"
)

次回 ― 時間帯別に分析する

月別損益を見ることで、

「成績には時期によってムラがある」

ことが分かりました。

では、もっと細かく見るとどうなるでしょうか。

次回は、

「どの時間帯で勝ち、どの時間帯で負けているのか」

を分析したいと思います。

零号機が得意な時間帯、苦手な時間帯が存在するのか。

さらに一段細かく観測していきたいと思います。

ランダムウォークへの挑戦⑥ | Pythonでエントリー時間と決済時間から損益を分析する(2026/7/22)
最近は暑すぎてプールが気持ちいいです。今日は夏休みをとって、昼間から泳ぎに行きました。平日昼間は人が少なくてとても快適です。それでは今日も作業を進めていきます。はじめに前回は、零号機の月別損益をグラフ化しました。その結果、長期的には利益を残…

おまけ

今、「知って得する、すごい法則77」という本を読んでいます。

1つの法則につき数ページなので、サクサク読めます。

知っている法則も多いですが、ある程度体系的にまとまっているので、意味が逆の法則を場面場面で利用し分ける目線など面白いです。

これを実務で利用するぞ!というより、読んでいて面白い本です。

次の記事

ランダムウォークへの挑戦⑥ | Pythonでエントリー時間と決済時間から損益を分析する(2026/7/22)
最近は暑すぎてプールが気持ちいいです。今日は夏休みをとって、昼間から泳ぎに行きました。平日昼間は人が少なくてとても快適です。それでは今日も作業を進めていきます。はじめに前回は、零号機の月別損益をグラフ化しました。その結果、長期的には利益を残…

関連記事

ランダムウォークへの挑戦㉗|Dの勝敗はトレンドで説明できるのか?MA・DI・ADXを検証する(2026/10/16)
ランダムウォークへの挑戦㉖|Dが勝てる相場とは?ATRとBB幅から「相場環境」を探る
ランダムウォークへの挑戦㉕|Dの優位性はなぜ消えた?Relative Band Distanceの時間劣化を分解する
ランダムウォークへの挑戦㉔|高PFでも耐えられるとは限らない。Relative Band Distanceを資産曲線とドローダウンで比較する(2026/8/30)
ランダムウォークへの挑戦㉓|最高PFは本当に強いのか。Relative Band Distanceを時間分割で比較する(2026/8/30)
ランダムウォークへの挑戦㉒|Q3の境界をずらしても優位性は残るのか(2026/8/30)
ランダムウォークへの挑戦㉑|Q3の優位性は時間が変わっても残るのか(2026/8/29)
ランダムウォークへの挑戦⑳|零号機の利益は「一部の大勝ち」に支えられているのか(2026/8/20)
ランダムウォークへの挑戦⑲|BAND_DISTANCEをATRで割って「相対的なブレイクの強さ」を分析する(2026/8/19)
ランダムウォークへの挑戦⑱|バンドからの距離で零号機のエントリー位置を分析する(2026/8/19)
ランダムウォークへの挑戦⑰|ATR×BB幅で零号機の「得意な相場」を16種類に分解する(2026/8/14)
ランダムウォークへの挑戦⑯|ボリンジャーバンドの幅で零号機の得意な相場を探る(2026/8/11)
ランダムウォークへの挑戦⑮|零号機は「荒れた相場」に強いのか ― ATRと取引成績の関係をPythonで分析する(2026/8/9)
ランダムウォークへの挑戦⑭ | 零号機の「記憶」をPythonへ渡す ― trade_log.csvを分析用データに整える(2026/8/5)
ランダムウォークへの挑戦⑬ | 零号機に「記憶」を与える ― mql4でエントリーから決済までの相場環境を記録する(2026/8/2)
ランダムウォークへの挑戦⑫ | Chapter2「学習」始動 ー零号機は何を学ぶべきなのかー(2026/8/2)
ランダムウォークへの挑戦⑪ | 零号機の性格診断──Chapter1「観測」 完結(2026/8/1)
ランダムウォークへの挑戦⑩ | Pythonで資産推移とドローダウンを分析する(2026/8/1)
ランダムウォークへの挑戦⑨ | Pythonで取引の連勝・連敗を分析する(2026/7/28)
ランダムウォークへの挑戦⑧ | Pythonでポジションの保有時間別に成績を分析する(2026/7/26)
ランダムウォークへの挑戦⑦ | Pythonでエントリー曜日と決済曜日から零号機を分析する(2026/7/23)
ランダムウォークへの挑戦⑥ | Pythonでエントリー時間と決済時間から損益を分析する(2026/7/22)
ランダムウォークへの挑戦⑤ | Pythonで月別損益をグラフ化する(2026/7/5)
ランダムウォークへの挑戦④ |Pythonで「零号機」の基本成績を分析する(2026/6/11)
ランダムウォークへの挑戦③ | 挑戦のための準備(環境設定)(2026/5/26)
ランダムウォークへの挑戦➁ | 小目標3つ設定(2026/5/25)
ランダムウォークへの挑戦① | はじめに(2026/5/25)

コメント

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