ランダムウォークへの挑戦⑦ | Pythonでエントリー曜日と決済曜日から零号機を分析する(2026/7/23)

未分類

2075年。これは何の年?
答えは、平均寿命を考えた時の私の享年。

そんなことを考えていた今日この頃です。

それでは作業を進めていきます。

はじめに

前回は、零号機の取引成績を「エントリー時間帯」と「決済時間帯」の2つに分けて分析しました。

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

その結果、同じ時間帯でも、入口と出口では全く違う成績になるケースがありました。

例えば23時台では、

エントリー:+7,344円
決済   :-59,526円

と、大きな違いがありました。

つまり、

「23時台が悪い」のではなく、「23時台に決済された取引の成績が悪い」

ということです。

では、この違いは曜日でも現れるのでしょうか。

今回は、

「何曜日にエントリーしたのか」

と、

「何曜日に決済したのか」

を分けて、零号機の成績を分析してみます。


1.エントリー曜日と決済曜日を取得する

前回、注文番号を使ってエントリーと決済を結合したことで、tradesには、

エントリー時間
決済時間

の両方が入るようになりました。

今回は、この2つの日時から曜日を取得します。

まず、曜日番号を作ります。

trades["エントリー曜日番号"] = (
    trades["エントリー時間"]
    .dt.dayofweek
)

trades["決済曜日番号"] = (
    trades["決済時間"]
    .dt.dayofweek
)

.dt.dayofweekを使うと、日時から曜日を数字として取得できます。

pandasでは、

月曜日 → 0
火曜日 → 1
水曜日 → 2
木曜日 → 3
金曜日 → 4
土曜日 → 5
日曜日 → 6

となります。

例えば、

2026-07-20(月) → 0
2026-07-21(火) → 1
2026-07-22(水) → 2

という具合です。


2.曜日番号を英語に変換する

数字のままでは分かりにくいので、英語の曜日名に変換します。

まず、曜日番号と曜日名の対応表を作ります。

weekday_names = {
    0: "Monday",
    1: "Tuesday",
    2: "Wednesday",
    3: "Thursday",
    4: "Friday",
    5: "Saturday",
    6: "Sunday"
}

これはPythonの**辞書(dictionary)**です。

例えば、

weekday_names[0]

なら、

Monday

を取得できます。

この辞書を使って、

trades["エントリー曜日"] = (
    trades["エントリー曜日番号"]
    .map(weekday_names)
)

trades["決済曜日"] = (
    trades["決済曜日番号"]
    .map(weekday_names)
)

とします。

.map()は、値を対応表に従って別の値へ変換する処理です。

これによって、

0 → Monday
1 → Tuesday
2 → Wednesday
……

と変換できます。

これで一つの取引に、

エントリー時間
エントリー曜日
決済時間
決済曜日
損益

という情報がそろいました。


3.曜日別分析を関数にする

前回は、エントリー時間と決済時間で同じ集計処理を行うため、

analyze_by_hour()

という関数を作りました。

今回も考え方は同じです。

エントリー曜日と決済曜日で同じ処理を2回書くのではなく、

def analyze_by_weekday(
    data,
    weekday_number_column,
    weekday_column
):

という曜日分析用の関数を作ります。


4.曜日ごとにグループ化する

関数の中では、まず曜日ごとに取引をまとめます。

analysis = (
    data
    .groupby(
        [
            weekday_number_column,
            weekday_column
        ]
    )

例えばエントリー曜日を分析するときには、

0・Monday
1・Tuesday
2・Wednesday
3・Thursday
4・Friday

というグループができます。

曜日名だけでなく曜日番号も一緒に使うのは、後で、

月 → 火 → 水 → 木 → 金

という自然な順番に並べるためです。


5..agg()で曜日ごとの成績を集計する

続いて、

.agg(
    取引回数=("損益", "count"),
    勝ち数=(
        "損益",
        lambda x: (x > 0).sum()
    ),
    負け数=(
        "損益",
        lambda x: (x < 0).sum()
    ),
    総損益=("損益", "sum"),
    平均損益=("損益", "mean"),
    総利益=(
        "損益",
        lambda x: x[x > 0].sum()
    ),
    総損失=(
        "損益",
        lambda x: x[x < 0].sum()
    )
)

として集計します。

ここは前回の時間帯別分析と同じです。

.agg()を使うことで、それぞれの曜日について、

取引回数
勝ち数
負け数
総損益
平均損益
総利益
総損失

をまとめて計算できます。

例えば、

勝ち数=(
    "損益",
    lambda x: (x > 0).sum()
)

では、損益が0より大きい取引の件数を数えています。

一方、

総利益=(
    "損益",
    lambda x: x[x > 0].sum()
)

では、損益が0より大きい取引だけを取り出して、その金額を合計しています。

つまり、

(x > 0).sum()
→ 件数

x[x > 0].sum()
→ 金額

という違いです。


6.勝率とPFを計算する

.agg()で作った集計表に勝率を追加します。

analysis["勝率"] = (
    analysis["勝ち数"]
    / analysis["取引回数"]
    * 100
)

計算式は、

勝ち数 ÷ 取引回数 × 100

です。

PFも追加します。

analysis["PF"] = (
    analysis["総利益"]
    / analysis["総損失"]
    .abs()
    .replace(
        0,
        float("nan")
    )
)

PFは、

総利益 ÷ 総損失の絶対値

です。

.replace()によって、総損失が0の場合の0除算も防いでいます。

現在の記事にもPF計算がありますが、今回の改稿では第6回と同じ書き方に統一した方が分かりやすいです。


7.reset_index()で曜日を普通の列に戻す

groupby()によって、

曜日番号
曜日

はインデックスになっています。

そこで、

analysis = (
    analysis
    .reset_index()
)

とします。

イメージとしては、

曜日番号・曜日
     ↓
   index

     ↓ reset_index()

普通の列

です。

これによって、

analysis["曜日"]

のように、曜日を通常の列として扱いやすくなります。


8.曜日順に並べる

続いて、

analysis = (
    analysis
    .sort_values(
        weekday_number_column
    )
)

とします。

.sort_values()は、指定した列を基準にデータを並べ替える処理です。

今回は曜日番号が、

0 → 月曜日
1 → 火曜日
2 → 水曜日
3 → 木曜日
4 → 金曜日

なので、曜日番号の小さい順に並べれば自然な曜日順になります。


9.必要な列だけ残す

最後に、

analysis = analysis[
    [
        weekday_number_column,
        weekday_column,
        "取引回数",
        "勝ち数",
        "負け数",
        "勝率",
        "総損益",
        "平均損益",
        "PF"
    ]
]

として、必要な列だけを残します。

PFを計算するために使用した、

総利益
総損失

は、最終的な表から外しています。

そして、

return analysis

によって、完成した集計結果を関数の外へ返します。


10.エントリー曜日を分析する

作った関数を使います。

entry_weekday_analysis = analyze_by_weekday(
    trades,
    "エントリー曜日番号",
    "エントリー曜日"
)

これによって、

「何曜日にエントリーした取引が、その後最終的にどれだけ利益・損失を出したのか」

を分析できます。


11.決済曜日を分析する

同じ関数を使って、

exit_weekday_analysis = analyze_by_weekday(
    trades,
    "決済曜日番号",
    "決済曜日"
)

とします。

こちらは、

「何曜日に決済された取引が、どれだけ利益・損失を出したのか」

を見る分析です。


12.CSVとして保存する

それぞれの結果をCSVとして保存します。

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

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

これで、

results
├─ entry_weekday_analysis.csv
└─ exit_weekday_analysis.csv

が作成されます。


13.グラフを作る

エントリー曜日について、

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

plt.bar(
    entry_weekday_analysis[
        "エントリー曜日"
    ],
    entry_weekday_analysis[
        "総損益"
    ]
)

plt.title(
    "Profit by Entry Weekday"
)

plt.xlabel(
    "Entry Weekday"
)

plt.ylabel(
    "Profit"
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()

とします。

決済曜日も同様です。

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

plt.bar(
    exit_weekday_analysis[
        "決済曜日"
    ],
    exit_weekday_analysis[
        "総損益"
    ]
)

plt.title(
    "Profit by Exit Weekday"
)

plt.xlabel(
    "Exit Weekday"
)

plt.ylabel(
    "Profit"
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()

これで、

entry_weekday_profit.png
→ エントリー曜日別

exit_weekday_profit.png
→ 決済曜日別

という2つのグラフができます。

分析

エントリー曜日と決済曜日を分けて比較すると、まず大きな傾向として、平日はすべて総損益がプラスになっていました。

エントリー曜日別では、

Monday      +12,123円
Tuesday     +54,665円
Wednesday   +75,030円
Thursday    +44,938円
Friday      +33,823円

決済曜日別では、

Monday      +16,427円
Tuesday     +76,665円
Wednesday   +63,444円
Thursday    +41,614円
Friday      +29,795円

となっています。

時間帯別分析では、エントリーと決済で結果がほぼ逆になる時間帯もありました。

一方、曜日単位まで大きくまとめると、平日の入口と出口で極端に逆転するような傾向は見られませんでした。

ただし、細かく見ると興味深い違いがあります。

エントリーではWednesday、決済ではTuesdayが最も好成績

エントリー曜日で最も利益が大きかったのは、Wednesday(水曜日)でした。

Wednesdayエントリー
取引回数:386回
勝率:63.73%
総損益:+75,030円
平均損益:+194円
PF:1.53

一方、決済曜日で最も利益が大きかったのは、Tuesday(火曜日)です。

Tuesday決済
取引回数:356回
勝率:60.96%
総損益:+76,665円
平均損益:+215円
PF:1.70

つまり、

「水曜日に入った取引」が最も大きな利益を残し、「火曜日に終わった取引」が最も大きな利益を残した

という違いがあります。

曜日についても、入口と出口を分けて見る意味がありそうです。

Tuesdayは「決済曜日」として特に強い

Tuesdayを比較すると、

エントリー
総損益:+54,665円
PF:1.42

決済
総損益:+76,665円
PF:1.70

となっています。

エントリー曜日としても好成績ですが、決済曜日として見たときの方がさらに成績が良い結果です。

このことから、

火曜日に新しく入ること

よりも、

火曜日に利益を確定できた取引

に特徴がある可能性があります。

ただし、この段階では原因までは分かりません。

月曜日や前週から保有していたポジションが、火曜日にうまく伸びて決済されている可能性なども考えられます。

Wednesdayは入口として強い

逆にWednesdayは、

エントリー
総損益:+75,030円
PF:1.53

決済
総損益:+63,444円
PF:1.43

となっています。

どちらも十分プラスですが、エントリー曜日として見た方が成績が良い結果です。

特にWednesdayエントリーは386回と取引回数も多く、勝率63.73%、平均損益+194.38円となっています。

現段階では、

水曜日にエントリーした取引は、零号機にとって比較的相性が良かった

という観測結果として残しておきます。

Mondayは入口・出口ともに弱め

Mondayは、

エントリー
総損益:+12,123円
平均損益:+38円
PF:1.10

決済
総損益:+16,427円
平均損益:+51円
PF:1.14

でした。

どちらもプラスではありますが、他の平日と比べると利益はかなり小さくなっています。

特にPFは入口・出口ともに1.1前後です。

つまりMondayは、

エントリー曜日としても決済曜日としても、零号機の優位性が比較的小さい曜日

と見ることができそうです。

ただし、利益自体はプラスなので、

月曜日を取引禁止にすればよい

と結論づける段階ではありません。

Fridayもやや弱い

Fridayも、

エントリー
総損益:+33,823円
PF:1.23

決済
総損益:+29,795円
PF:1.20

となっています。

Mondayほどではありませんが、TuesdayやWednesdayと比較するとPFは低めです。

今回の曜日別分析を見る限り、

週前半から中盤にかけて利益が大きく、週末に近づくにつれてやや弱くなる

ようにも見えます。

ただし、曜日単体の因果関係を示しているわけではないので、ここも観測結果として扱います。

Saturdayは入口と出口で意味が全く違う

Saturdayはかなり特徴的でした。

エントリー曜日では、

取引回数:3回
勝率:100%
総損益:+2,916円

ですが、決済曜日では、

取引回数:5回
勝率:60%
総損益:-4,452円
平均損益:-890円
PF:0.40

となっています。

入口ではプラス、出口ではマイナスです。

ただし、取引回数がそれぞれ3件・5件しかないため、この結果から土曜日の良し悪しを判断することはできません。

時間帯ほど入口と出口で大きくは変わらなかった

前回の時間帯分析では、

23時台
エントリー:+7,344円
決済   :-59,526円

のように、入口と出口で全く逆の結果が出ました。

一方、曜日別では、

平日の大まかな強弱は入口と出口で比較的似ています。

Mondayはどちらも弱め。

Tuesday・Wednesdayはどちらも強め。

Thursday・Fridayはその中間。

という形です。

つまり現時点では、

時間帯の方が、曜日よりも入口と出口の違いが大きく表れている

ように見えます。

これは今後、零号機を改善するときにも重要な示唆になるかもしれません。

今回分かったこと

今回、曜日についても、

入口
→ エントリー曜日

出口
→ 決済曜日

に分けて分析しました。

その結果、

水曜日はエントリー側で最も好成績、火曜日は決済側で最も好成績

という違いが見つかりました。

一方で、Mondayは入口・出口ともに弱く、Tuesday・Wednesdayはどちらから見ても比較的強いなど、曜日全体の傾向はある程度共通しています。

つまり、曜日については、

「どの曜日に入ったか」と「どの曜日に出たか」で多少の違いはあるものの、時間帯ほど劇的な差はない

というのが今回の観測結果です。

まだ、

水曜日だけエントリーする
月曜日を停止する

といった改善策を実装する段階ではありません。

まずは、こうした特徴を積み上げていきます。

次回

ここまで、

時間帯
→ エントリー時間
→ 決済時間

曜日
→ エントリー曜日
→ 決済曜日

という形で、取引の「入口」と「出口」を観測してきました。

次に注目するのは、そのです。

エントリー
    ↓
    │
    │  ← 保有時間
    │
    ↓
決済

次回は、ポジションの保有時間別に成績を分析します。

短時間で決済された取引と、何時間もポジションを保有した取引では、勝率やPF、損益に違いはあるのでしょうか。

特に第6回では、23時台に決済された取引が大きなマイナスとなっていました。

その損失は、

長時間保有されたポジションが23時台まで残った結果なのか。

それとも、保有時間とは関係なく23時台の決済そのものに特徴があるのか。

次回は「入口」と「出口」の間にある時間を観測してみます。

ランダムウォークへの挑戦⑧ | Pythonでポジションの保有時間別に成績を分析する(2026/7/26)
最近大きな仕事が終わりました。なかなかに調整が大変でしたが、形として見えるものを世に送り出せたその一端を担えたので、いい経験でした。このあたりの話もいずれしたいと思います。では、今日も進めていきます。はじめに前回は、零号機の取引成績を、エン…

今回の完成コード

完成コードはこちら
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"
)


# ==============================
# エントリー行を抽出
# ==============================

# buy・sellがエントリー行
entries = df[
    df["取引種別"].isin(
        [
            "buy",
            "sell"
        ]
    )
].copy()


# エントリー分析に必要な列だけ残す
entries = entries[
    [
        "注文番号",
        "時間"
    ]
].copy()


# 「時間」を「エントリー時間」に変更
entries = entries.rename(
    columns={
        "時間": "エントリー時間"
    }
)


# ==============================
# 決済行を抽出
# ==============================

# closeが決済行
trades = df[
    df["取引種別"] == "close"
].copy()


# 「時間」を「決済時間」に変更
trades = trades.rename(
    columns={
        "時間": "決済時間"
    }
)


# ==============================
# エントリーと決済を結合
# ==============================

# 注文番号を使って1つの取引として結合
trades = trades.merge(
    entries,
    on="注文番号",
    how="left",
    validate="one_to_one"
)


# ==============================
# 日時型へ変換
# ==============================

date_columns = [
    "エントリー時間",
    "決済時間"
]


for col in date_columns:

    trades[col] = pd.to_datetime(
        trades[col],
        format="%Y.%m.%d %H:%M",
        errors="coerce"
    )


# 日時変換に失敗していないか確認
print(
    "\n===== 日時変換エラー確認 ====="
)

print(
    trades[
        [
            "エントリー時間",
            "決済時間"
        ]
    ].isna().sum()
)


# ==============================
# 年月列を作成
# ==============================

# 月別分析は決済した年月を使用
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(
    "\n===== 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.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()


# ==============================
# エントリー時間帯・決済時間帯を作成
# ==============================

trades["エントリー時間帯"] = (
    trades["エントリー時間"]
    .dt.hour
)


trades["決済時間帯"] = (
    trades["決済時間"]
    .dt.hour
)


# ==============================
# 時間帯別分析用の関数
# ==============================

def analyze_by_hour(
    data,
    hour_column
):

    analysis = (
        data
        .groupby(hour_column)
        .agg(
            取引回数=(
                "損益",
                "count"
            ),
            勝ち数=(
                "損益",
                lambda x: (x > 0).sum()
            ),
            負け数=(
                "損益",
                lambda x: (x < 0).sum()
            ),
            総損益=(
                "損益",
                "sum"
            ),
            平均損益=(
                "損益",
                "mean"
            ),
            総利益=(
                "損益",
                lambda x: x[x > 0].sum()
            ),
            総損失=(
                "損益",
                lambda x: x[x < 0].sum()
            )
        )
    )


    # 勝率
    analysis["勝率"] = (
        analysis["勝ち数"]
        / analysis["取引回数"]
        * 100
    )


    # PF
    analysis["PF"] = (
        analysis["総利益"]
        / analysis["総損失"]
        .abs()
        .replace(
            0,
            float("nan")
        )
    )


    # 必要な列だけ残す
    analysis = analysis[
        [
            "取引回数",
            "勝ち数",
            "負け数",
            "勝率",
            "総損益",
            "平均損益",
            "PF"
        ]
    ]


    return analysis


# ==============================
# エントリー時間帯別分析
# ==============================

entry_hour_analysis = analyze_by_hour(
    trades,
    "エントリー時間帯"
)


print(
    "\n===== エントリー時間帯別分析 ====="
)

print(
    entry_hour_analysis.round(2)
)


entry_hour_analysis.to_csv(
    "results/entry_hour_analysis.csv",
    encoding="utf-8-sig"
)


# ==============================
# 決済時間帯別分析
# ==============================

exit_hour_analysis = analyze_by_hour(
    trades,
    "決済時間帯"
)


print(
    "\n===== 決済時間帯別分析 ====="
)

print(
    exit_hour_analysis.round(2)
)


exit_hour_analysis.to_csv(
    "results/exit_hour_analysis.csv",
    encoding="utf-8-sig"
)


# ==============================
# エントリー時間帯別グラフ
# ==============================

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

plt.bar(
    entry_hour_analysis.index,
    entry_hour_analysis["総損益"]
)

plt.title(
    "Profit by Entry Hour"
)

plt.xlabel(
    "Entry Hour"
)

plt.ylabel(
    "Profit"
)

plt.xticks(
    range(24)
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()


# ==============================
# 決済時間帯別グラフ
# ==============================

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

plt.bar(
    exit_hour_analysis.index,
    exit_hour_analysis["総損益"]
)

plt.title(
    "Profit by Exit Hour"
)

plt.xlabel(
    "Exit Hour"
)

plt.ylabel(
    "Profit"
)

plt.xticks(
    range(24)
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()


# ==============================
# エントリー曜日・決済曜日を作成
# ==============================

# Monday=0 ~ Sunday=6
trades["エントリー曜日番号"] = (
    trades["エントリー時間"]
    .dt.dayofweek
)

trades["決済曜日番号"] = (
    trades["決済時間"]
    .dt.dayofweek
)


# 曜日番号と英語曜日名の対応表
weekday_names = {
    0: "Monday",
    1: "Tuesday",
    2: "Wednesday",
    3: "Thursday",
    4: "Friday",
    5: "Saturday",
    6: "Sunday"
}


# 曜日番号を曜日名へ変換
trades["エントリー曜日"] = (
    trades["エントリー曜日番号"]
    .map(weekday_names)
)

trades["決済曜日"] = (
    trades["決済曜日番号"]
    .map(weekday_names)
)


# ==============================
# 曜日別分析用の関数
# ==============================

def analyze_by_weekday(
    data,
    weekday_number_column,
    weekday_column
):

    # 曜日ごとに集計
    analysis = (
        data
        .groupby(
            [
                weekday_number_column,
                weekday_column
            ]
        )
        .agg(
            取引回数=(
                "損益",
                "count"
            ),
            勝ち数=(
                "損益",
                lambda x: (x > 0).sum()
            ),
            負け数=(
                "損益",
                lambda x: (x < 0).sum()
            ),
            総損益=(
                "損益",
                "sum"
            ),
            平均損益=(
                "損益",
                "mean"
            ),
            総利益=(
                "損益",
                lambda x: x[x > 0].sum()
            ),
            総損失=(
                "損益",
                lambda x: x[x < 0].sum()
            )
        )
    )


    # 勝率
    analysis["勝率"] = (
        analysis["勝ち数"]
        / analysis["取引回数"]
        * 100
    )


    # PF
    analysis["PF"] = (
        analysis["総利益"]
        / analysis["総損失"]
        .abs()
        .replace(
            0,
            float("nan")
        )
    )


    # groupbyでindexになった曜日を通常の列へ戻す
    analysis = (
        analysis
        .reset_index()
    )


    # Monday → Sunday の順番に並べる
    analysis = (
        analysis
        .sort_values(
            weekday_number_column
        )
    )


    # 必要な列だけ残す
    analysis = analysis[
        [
            weekday_number_column,
            weekday_column,
            "取引回数",
            "勝ち数",
            "負け数",
            "勝率",
            "総損益",
            "平均損益",
            "PF"
        ]
    ]


    return analysis


# ==============================
# エントリー曜日別分析
# ==============================

entry_weekday_analysis = analyze_by_weekday(
    trades,
    "エントリー曜日番号",
    "エントリー曜日"
)


print(
    "\n===== エントリー曜日別分析 ====="
)

print(
    entry_weekday_analysis[
        [
            "エントリー曜日",
            "取引回数",
            "勝ち数",
            "負け数",
            "勝率",
            "総損益",
            "平均損益",
            "PF"
        ]
    ].round(2)
)


# ==============================
# エントリー曜日別CSV
# ==============================

entry_weekday_analysis[
    [
        "エントリー曜日",
        "取引回数",
        "勝ち数",
        "負け数",
        "勝率",
        "総損益",
        "平均損益",
        "PF"
    ]
].to_csv(
    "results/entry_weekday_analysis.csv",
    index=False,
    encoding="utf-8-sig"
)


# ==============================
# 決済曜日別分析
# ==============================

exit_weekday_analysis = analyze_by_weekday(
    trades,
    "決済曜日番号",
    "決済曜日"
)


print(
    "\n===== 決済曜日別分析 ====="
)

print(
    exit_weekday_analysis[
        [
            "決済曜日",
            "取引回数",
            "勝ち数",
            "負け数",
            "勝率",
            "総損益",
            "平均損益",
            "PF"
        ]
    ].round(2)
)


# ==============================
# 決済曜日別CSV
# ==============================

exit_weekday_analysis[
    [
        "決済曜日",
        "取引回数",
        "勝ち数",
        "負け数",
        "勝率",
        "総損益",
        "平均損益",
        "PF"
    ]
].to_csv(
    "results/exit_weekday_analysis.csv",
    index=False,
    encoding="utf-8-sig"
)


# ==============================
# エントリー曜日別損益グラフ
# ==============================

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

plt.bar(
    entry_weekday_analysis[
        "エントリー曜日"
    ],
    entry_weekday_analysis[
        "総損益"
    ]
)

plt.title(
    "Profit by Entry Weekday"
)

plt.xlabel(
    "Entry Weekday"
)

plt.ylabel(
    "Profit"
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()


print(
    "エントリー曜日別損益グラフを "
    "results/entry_weekday_profit.png "
    "として保存しました。"
)


# ==============================
# 決済曜日別損益グラフ
# ==============================

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

plt.bar(
    exit_weekday_analysis[
        "決済曜日"
    ],
    exit_weekday_analysis[
        "総損益"
    ]
)

plt.title(
    "Profit by Exit Weekday"
)

plt.xlabel(
    "Exit Weekday"
)

plt.ylabel(
    "Profit"
)

plt.axhline(
    0,
    linewidth=1
)

plt.tight_layout()

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

plt.close()


print(
    "決済曜日別損益グラフを "
    "results/exit_weekday_profit.png "
    "として保存しました。"
)

おまけ

ちなみに私がテクニカル分析の際に一番勉強になった本はこちらです。

一つ一つのテクニカル指標の解説が丁寧で、株を始めた時に何度も何度も読み返しました。
この本を読んでストキャスティクスが一番のお気に入り指標になりました。

慣れた頃にこそ再度手に取りたい一冊です。

次の記事

ランダムウォークへの挑戦⑧ | Pythonでポジションの保有時間別に成績を分析する(2026/7/26)
最近大きな仕事が終わりました。なかなかに調整が大変でしたが、形として見えるものを世に送り出せたその一端を担えたので、いい経験でした。このあたりの話もいずれしたいと思います。では、今日も進めていきます。はじめに前回は、零号機の取引成績を、エン…

関連記事

ランダムウォークへの挑戦㉓|最高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をコピーしました