FX / EA Python

FXのMACDで最適な期間・設定値は?5通貨・3,100ケースで検証

本ページにはプロモーションが含まれています。

FXでよく使われるテクニカル指標の1つがMACDです。

MACDでは「12・26・9」が定番設定としてよく使われていますが、

  • 本当に12・26・9が使いやすいのか
  • 期間を変えると成績はどう変わるのか
  • 通貨によって相性の良い設定は違うのか
  • LONGとSHORTで同じ設定を使ってよいのか

と疑問に思う方も多いのではないでしょうか。

そこで今回は、USDJPY・EURJPY・GBPJPY・EURUSD・AUDUSDの5通貨を対象に、31種類のMACD設定と10種類のTP・SLをLONG・SHORTそれぞれで検証しました。

検証した組み合わせは、合計3,100ケースです。

定番のMACD12・26・9も含めて比較し、合計pipsやPF、最大ドローダウン、取引回数などから成績の良かった設定を確認していきます。

先に結果を確認したい方は、「5通貨のMACD検証結果」からご覧ください。

今回のバックテストでは、GMOコインFX APIから取得した15分足のCSVデータを使用しています。

ローソク足データの取得方法は、以下の記事で詳しく紹介しています。

また、今回使用したバックテストスクリプトは以下です

run_macd_backtest.py
"""5通貨・LONG/SHORTでMACD 31設定とTP/SL 10組を一括検証する。"""

import csv
import time
from datetime import date, datetime, timedelta
from pathlib import Path


# 使い方:
# 1. fetch_gmo_candles.pyで、5通貨の15分足を取得します。
# 2. GMO-APIフォルダで次のコマンドを実行します。
#    python backtest_research/macd/run_macd_backtest.py
# 3. 結果は backtest_research/macd/results に保存されます。

# MACDの計算、シグナル判定、売買には15分足だけを使います。
ENTRY_TIMEFRAME = "15min"
ENTRY_TIMEFRAME_DURATION = timedelta(minutes=15)
# 入力CSVに合わせてASK価格を読み込みます。
# BID価格、スプレッド、手数料、スリッページはこの検証に含みません。
PRICE_TYPE = "ASK"
# 開始日と終了日のローソク足も検証対象に含めます。
BACKTEST_START_DATE = date(2024, 1, 1)
BACKTEST_END_DATE = date(2026, 7, 31)

# 全通貨・全方向を同じ候補群で比較し、ブログのTOP5を公平に選べるようにする。
SYMBOLS = ("USD_JPY", "EUR_JPY", "GBP_JPY", "EUR_USD", "AUD_USD")
SIDES = ("LONG", "SHORT")

# 各要素は「(短期EMA期間, 長期EMA期間, シグナルEMA期間)」です。
# 例:定番の(12, 26, 9)は、短期EMA12、長期EMA26、シグナルEMA9を表します。
# MACD線 = 短期EMA - 長期EMA、シグナル線 = MACD線のEMAです。
MACD_SETTINGS = (
    (3, 15, 3),
    (3, 20, 3),
    (3, 40, 3),
    (3, 60, 3),
    (5, 15, 5),
    (5, 20, 5),
    (5, 26, 9),
    (6, 15, 9),
    (6, 18, 5),
    (6, 22, 4),
    (7, 12, 9),
    (7, 15, 7),
    (7, 18, 7),
    (8, 20, 9),
    (8, 22, 16),
    (8, 26, 14),
    (8, 35, 12),
    (10, 22, 14),
    (10, 26, 12),
    (12, 20, 9),
    (12, 26, 9),
    (12, 50, 12),
    (14, 22, 14),
    (16, 20, 12),
    (16, 21, 11),
    (16, 30, 7),
    (16, 38, 6),
    (20, 30, 6),
    (20, 35, 5),
    (22, 50, 7),
    (24, 50, 6),
)

# 各要素は「(利益確定幅, 損切り幅)」で、単位はpipsです。
# 例:(100, 20)なら、100pipsの利益または20pipsの損失で決済します。
TP_SL_PAIRS = (
    (80, 10),
    (100, 10),
    (120, 10),
    (150, 10),
    (180, 10),
    (100, 20),
    (120, 20),
    (120, 30),
    (150, 30),
    (150, 50),
)

# 5通貨 × LONG/SHORTの10実験へ、同じMACD・TP/SL候補を割り当てます。
# 1実験310ケース、全体では31 × 10 × 5 × 2 = 3,100ケースです。
EXPERIMENTS = {
    (symbol, side): {"settings": MACD_SETTINGS, "tp_sl": TP_SL_PAIRS}
    for symbol in SYMBOLS
    for side in SIDES
}

# 入力CSVと検証結果の保存場所です。
# __file__を基準にするため、実行時のカレントフォルダに依存しません。
SCRIPT_DIR = Path(__file__).resolve().parent
GMO_API_DIR = SCRIPT_DIR.parents[1]
DEFAULT_DATA_DIR = GMO_API_DIR / "data" / "candles"
DEFAULT_OUTPUT_DIR = SCRIPT_DIR / "results"

# 集計結果CSVへ出力する列と、その並び順です。
RESULT_COLUMNS = (
    "通貨ペア", "売買方向", "短期EMA", "長期EMA", "シグナルEMA",
    "TP(pips)", "SL(pips)", "取引回数", "勝ち取引数", "負け取引数",
    "勝率(%)", "合計損益(pips)", "平均損益(pips)",
    "プロフィットファクター", "最大ドローダウン(pips)",
    "平均保有時間(分)",
)


def load_candles(path):
    """fetch_gmo_candles.py形式のOHLC CSVを読み込む。"""
    candles = []
    # utf-8-sigを使うと、Excel向けのBOM付きCSVも読み込めます。
    with path.open("r", encoding="utf-8-sig", newline="") as file:
        for row in csv.DictReader(file):
            timestamp = datetime.fromisoformat(row["timestamp_jst"])
            # datetime同士ではなくdate同士で比較することで、終了日の夜の足も含めます。
            if BACKTEST_START_DATE <= timestamp.date() <= BACKTEST_END_DATE:
                candles.append({
                    # timestamp_jstは15分足が始まった時刻です。
                    "open_time": timestamp,
                    "close_time": timestamp + ENTRY_TIMEFRAME_DURATION,
                    # CSVの文字列を計算可能なfloatへ変換します。
                    "open": float(row["open"]),
                    "high": float(row["high"]),
                    "low": float(row["low"]),
                    "close": float(row["close"]),
                })
    return candles


def calculate_ema(values, period):
    """最初のperiod本の単純平均を初期値としてEMAを計算する。"""
    if period <= 0:
        raise ValueError("EMA期間は1以上である必要があります")
    # EMAをまだ計算できない位置にはNoneを入れます。
    # EMA3なら、最初の2本はNone、3本目が最初のEMAになります。
    result = [None] * len(values)
    if len(values) < period:
        return result
    # 最初のEMAだけは、最初のperiod本の単純平均を使います。
    result[period - 1] = sum(values[:period]) / period
    # EMAは新しい価格を強く反映します。期間が短いほどalphaは大きくなります。
    # EMA = 今回価格 × alpha + 前回EMA × (1 - alpha)
    alpha = 2.0 / (period + 1)
    for index in range(period, len(values)):
        result[index] = alpha * values[index] + (1.0 - alpha) * result[index - 1]
    return result


def calculate_macd(closes, fast_period, slow_period, signal_period):
    """MACD線、シグナル線、ヒストグラムを返す。"""
    if fast_period >= slow_period:
        raise ValueError("短期EMAは長期EMAより短くしてください")
    fast = calculate_ema(closes, fast_period)
    slow = calculate_ema(closes, slow_period)
    # MACD線は短期EMAから長期EMAを引いた値です。
    # プラスなら短期の価格傾向が長期より上、マイナスなら下にあります。
    macd = [
        fast_value - slow_value
        if fast_value is not None and slow_value is not None else None
        for fast_value, slow_value in zip(fast, slow)
    ]

    # MACD線を計算できる最初の位置から、そのEMAであるシグナル線を作ります。
    first = next((i for i, value in enumerate(macd) if value is not None), None)
    signal = [None] * len(closes)
    if first is not None:
        compact_signal = calculate_ema(macd[first:], signal_period)
        signal[first:] = compact_signal
    # ヒストグラム = MACD線 - シグナル線です。
    # 今回の売買判定では直接使いませんが、一般的なMACD値として返します。
    histogram = [
        macd_value - signal_value
        if macd_value is not None and signal_value is not None else None
        for macd_value, signal_value in zip(macd, signal)
    ]
    return macd, signal, histogram


def collect_trades(candles, macd, signal, side, tp_pips, sl_pips, pip_size):
    """MACDクロスの次足始値で入り、固定TP/SLで決済する。"""
    # positionがNoneなら、ポジションを持っていない状態です。
    # pendingには、現在足で確定した注文を次足始値で実行するための
    # MACD線とシグナル線を一時保存します。
    position = None
    pending = None
    trades = []
    for index in range(1, len(candles)):
        # 前の15分足でクロスが確定していれば、現在足の始値で入ります。
        # クロスした足の終値で入らないため、確定前の情報を使う未来参照を防げます。
        if position is None and pending is not None:
            position = {
                "time": candles[index]["open_time"],
                "price": candles[index]["open"],
                "macd": pending[0],
                "signal": pending[1],
            }
        pending = None

        # ポジション保有中は、現在足の高値・安値がTP/SLへ届いたか確認します。
        if position is not None:
            # pipsを実際の価格差へ変換します。
            # USDJPYなら100pips × 0.01円 = 1円です。
            tp_distance = tp_pips * pip_size
            sl_distance = sl_pips * pip_size
            if side == "LONG":
                # LONGは価格上昇で利益、価格下落で損失になります。
                tp_price = position["price"] + tp_distance
                sl_price = position["price"] - sl_distance
                tp_hit = candles[index]["high"] >= tp_price
                sl_hit = candles[index]["low"] <= sl_price
            else:
                # SHORTは価格下落で利益、価格上昇で損失になります。
                tp_price = position["price"] - tp_distance
                sl_price = position["price"] + sl_distance
                tp_hit = candles[index]["low"] <= tp_price
                sl_hit = candles[index]["high"] >= sl_price

            # 15分足だけでは、足の中でTPとSLのどちらへ先に届いたか分かりません。
            # 同一足で両方に届いた場合は、成績を楽観視しないようSLを優先します。
            if sl_hit:
                exit_price, reason = sl_price, f"SL {sl_pips}pips"
            elif tp_hit:
                exit_price, reason = tp_price, f"TP {tp_pips}pips"
            else:
                exit_price = reason = None
            if exit_price is not None:
                # 決済した1取引を記録し、再びポジションなしの状態へ戻します。
                trades.append(make_trade(
                    position, candles[index]["close_time"], exit_price,
                    reason, side, pip_size,
                ))
                position = None

        # 前足と現在足のMACD線・シグナル線を比べ、交差した瞬間を探します。
        current_macd, previous_macd = macd[index], macd[index - 1]
        current_signal, previous_signal = signal[index], signal[index - 1]
        if None in (current_macd, previous_macd, current_signal, previous_signal):
            continue
        if position is None:
            # LONGはMACD線がシグナル線を下から上へ抜けるゴールデンクロスです。
            if (
                side == "LONG"
                and previous_macd <= previous_signal
                and current_macd > current_signal
            ):
                pending = (current_macd, current_signal)
            # SHORTはMACD線がシグナル線を上から下へ抜けるデッドクロスです。
            elif (
                side == "SHORT"
                and previous_macd >= previous_signal
                and current_macd < current_signal
            ):
                pending = (current_macd, current_signal)

    # データの最後までポジションが残った場合は、最終足の終値で決済します。
    if position is not None:
        trades.append(make_trade(
            position, candles[-1]["close_time"], candles[-1]["close"],
            "データ終了", side, pip_size,
        ))
    return trades


def make_trade(position, exit_time, exit_price, reason, side, pip_size):
    """1取引の保有時間とpips損益を作る。"""
    # LONGは値上がり、SHORTは値下がりしたときに損益をプラスにします。
    direction = 1.0 if side == "LONG" else -1.0
    return {
        # 実際の日時差なので、土日やデータ欠損をまたぐ時間も含みます。
        "保有時間(分)": (exit_time - position["time"]).total_seconds() / 60,
        # 円通貨は1pips=0.01円、それ以外は1pips=0.0001として換算します。
        "損益(pips)": direction * (exit_price - position["price"]) / pip_size,
    }


def summarize(symbol, side, fast, slow, signal_period, tp, sl, trades):
    """取引ごとのpips損益から、1設定の成績をまとめる。"""
    profits = [trade["損益(pips)"] for trade in trades]
    # 0pipsは勝ちにも負けにも含めませんが、取引回数には含めます。
    wins = [value for value in profits if value > 0]
    losses = [value for value in profits if value < 0]
    # 累積損益の過去最高値から、最大で何pips下落したかを調べます。
    # 例:累積損益が100、300、150なら、300から150への150pipsがDDです。
    equity = peak = max_drawdown = 0.0
    for profit in profits:
        equity += profit
        peak = max(peak, equity)
        max_drawdown = max(max_drawdown, peak - equity)
    count = len(profits)
    # PF(プロフィットファクター)= 勝ち取引の総利益 ÷ 負け取引の総損失です。
    # PFが1より大きければ、取引コストを考えない総利益は総損失を上回ります。
    gross_loss = abs(sum(losses))
    durations = [trade["保有時間(分)"] for trade in trades]
    return {
        "通貨ペア": symbol, "売買方向": side, "短期EMA": fast,
        "長期EMA": slow, "シグナルEMA": signal_period, "TP(pips)": tp,
        "SL(pips)": sl, "取引回数": count, "勝ち取引数": len(wins),
        "負け取引数": len(losses),
        "勝率(%)": round(len(wins) / count * 100, 2) if count else 0.0,
        "合計損益(pips)": round(sum(profits), 2),
        "平均損益(pips)": round(sum(profits) / count, 2) if count else 0.0,
        "プロフィットファクター": round(sum(wins) / gross_loss, 4) if gross_loss else "",
        "最大ドローダウン(pips)": round(max_drawdown, 2),
        "平均保有時間(分)": round(sum(durations) / count, 2) if count else 0.0,
    }


def write_csv(path, rows):
    """集計結果をExcelでも開きやすいUTF-8 BOM付きCSVへ保存する。"""
    # resultsが存在しない初回実行時は、通貨・方向別フォルダも自動作成します。
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8-sig", newline="") as file:
        writer = csv.DictWriter(file, fieldnames=RESULT_COLUMNS)
        writer.writeheader()
        writer.writerows(rows)


def macd_settings(config):
    """実験設定から有効なMACD期間を重複なしで作る。"""
    # 現行設定のようにsettingsへ候補を直接列挙した場合はこちらを使います。
    if "settings" in config:
        settings = []
        for fast, slow, signal in config["settings"]:
            if fast >= slow:
                raise ValueError(f"短期EMA {fast}は長期EMA {slow}より短くしてください")
            setting = (fast, slow, signal)
            if setting not in settings:
                settings.append(setting)
        return settings

    # fast・slow・signalを別々に指定する過去形式にも対応しています。
    # fast < slowを満たす組み合わせだけを残します。
    settings = [
        (fast, slow, signal)
        for fast in config["fast"]
        for slow in config["slow"]
        for signal in config["signal"]
        if fast < slow
    ]

    for fast, slow in config.get("extra_fast_slow", ()):
        if fast >= slow:
            raise ValueError(f"短期EMA {fast}は長期EMA {slow}より短くしてください")
        for signal in config["signal"]:
            setting = (fast, slow, signal)
            if setting not in settings:
                settings.append(setting)
    return settings


def run_experiment(candles, symbol, side, config, output_dir):
    """1通貨・1売買方向のMACD・TP/SL候補を検証する。"""
    # 同じMACD設定をTP/SLごとに再計算しないよう、最初にまとめて計算します。
    closes = [candle["close"] for candle in candles]
    indicators = {
        setting: calculate_macd(closes, *setting)[:2]
        for setting in macd_settings(config)
    }
    # USDJPY・EURJPY・GBPJPYは1pips=0.01円、それ以外は0.0001です。
    pip_size = 0.01 if symbol.endswith("_JPY") else 0.0001
    rows = []
    for (fast, slow, signal_period), (macd, signal) in indicators.items():
        for tp, sl in config["tp_sl"]:
            trades = collect_trades(
                candles, macd, signal, side, tp, sl, pip_size,
            )
            rows.append(summarize(
                symbol, side, fast, slow, signal_period, tp, sl, trades,
            ))
    # 1通貨・1方向の310ケースを、1つのresult.csvへ保存します。
    write_csv(output_dir / symbol / side / "result.csv", rows)
    return rows


def main():
    """3,100ケースを実行し、通貨別結果と全件結果を保存する。"""
    # スクリプト全体の実行時間を測ります。
    started = time.perf_counter()
    if BACKTEST_START_DATE > BACKTEST_END_DATE:
        raise SystemExit("検証開始日は検証終了日以前に設定してください")
    # 設定から実際のケース数を計算するため、候補数を変えても表示が追従します。
    total_cases = sum(
        len(macd_settings(config)) * len(config["tp_sl"])
        for config in EXPERIMENTS.values()
    )
    print(
        f"MACD設定{len(MACD_SETTINGS)}種類 × TP/SL {len(TP_SL_PAIRS)}組 "
        f"× 5通貨 × 2方向 = {total_cases}ケース"
    )
    print(f"検証期間: {BACKTEST_START_DATE} ~ {BACKTEST_END_DATE}")
    all_rows = []
    candles_by_symbol = {}
    # 同じ通貨のCSVをLONGとSHORTで2回読まないよう、最初に1回だけ読み込みます。
    for symbol in dict.fromkeys(symbol for symbol, _side in EXPERIMENTS):
        path = DEFAULT_DATA_DIR / symbol / f"{ENTRY_TIMEFRAME}_{PRICE_TYPE}.csv"
        if not path.is_file():
            raise SystemExit(f"入力CSVが見つかりません: {path}")
        candles = load_candles(path)
        symbol_configs = [
            config
            for (experiment_symbol, _side), config in EXPERIMENTS.items()
            if experiment_symbol == symbol
        ]
        # 最長EMAとシグナルEMAを計算できるだけのローソク足があるか確認します。
        required = max(
            slow + signal
            for config in symbol_configs
            for _fast, slow, signal in macd_settings(config)
        )
        if len(candles) <= required:
            raise SystemExit(f"{symbol}のデータが不足しています: {len(candles)}本")
        candles_by_symbol[symbol] = candles
        print(f"[LOAD] {symbol}: {len(candles)}本")

    # 通貨・売買方向を1組ずつ検証し、全結果もall_rowsへ集めます。
    for (symbol, side), config in EXPERIMENTS.items():
        cases = len(macd_settings(config)) * len(config["tp_sl"])
        print(f"[RUN] {symbol} {side}: {cases}ケース")
        all_rows.extend(run_experiment(
            candles_by_symbol[symbol], symbol, side, config, DEFAULT_OUTPUT_DIR,
        ))
    # 通貨別CSVに加え、3,100ケースを一括比較できるCSVも保存します。
    write_csv(DEFAULT_OUTPUT_DIR / "all_results.csv", all_rows)
    print(f"完了: {DEFAULT_OUTPUT_DIR}")
    print(f"実行時間: {time.perf_counter() - started:.2f}秒")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

同じようにRSIを検証してみたい方は、参考にしてみてください。

FXのMACDとは

MACD(Moving Average Convergence Divergence)は、2本の移動平均線の差を使って、トレンドの方向や勢い、売買タイミングを判断するテクニカル指標です。

主にMACD線とシグナル線の動きを確認し、2本の線が交差するタイミングを売買判断に利用します。

一般的によく使われる設定は、短期EMA12・長期EMA26・シグナル9です。

今回の記事では、この定番設定を含む複数のMACD設定を5通貨で検証し、どの期間・設定値が成績良かったのか比較します。

MACD線・シグナル線・ヒストグラムとは

MACDは、主に以下の3つで構成されています。

  • MACD線:短期EMA − 長期EMA
  • シグナル線:MACD線を移動平均した線
  • ヒストグラム:MACD線とシグナル線の差

MACD線とシグナル線の位置関係を見ることで、相場の勢いや変化を判断しやすくなります。

ヒストグラムは2本の線の差を表し、差が大きくなるほど値動きの勢いが強まっていると判断する際に使われます。

MACDのゴールデンクロス・デッドクロスとは

MACDでは、MACD線とシグナル線のクロスが代表的な売買シグナルです。

  • MACD線がシグナル線を下から上へ抜ける → ゴールデンクロス
  • MACD線がシグナル線を上から下へ抜ける → デッドクロス

一般的には、ゴールデンクロスを買い、デッドクロスを売りの判断材料として使います。

今回のバックテストでも、このクロスをエントリー条件として使用します。

なお、MACDでは0ラインを使った判断方法もありますが、今回の検証では0ラインは条件に加えず、MACD線とシグナル線のクロスのみを使用します。

FXのMACDでよく使われる設定

MACDでは、一般的に12・26・9の設定がよく使われます。

ただし、期間を変えるとMACDの反応速度やクロスの回数も変わるため、定番設定がすべての通貨で最適とは限りません。

定番のMACD12・26・9とは

MACD12・26・9は、

  • 短期EMA:12
  • 長期EMA:26
  • シグナル:9

を使う定番設定です。

それぞれの数字には、次の意味があります。

短期EMA12は、直近12本のローソク足をもとに計算するEMAです。

長期EMA26は、直近26本のローソク足をもとに計算するEMAです。

MACD線は、この短期EMAと長期EMAの差から計算します。

MACD線 = 短期EMA12 − 長期EMA26

そのため、2本のEMAの差が広がるとMACD線も大きく動き、差が縮まるとMACD線も0に近づきます。

シグナル9は、MACD線をさらに9期間のEMAで平均したものです。

MACD線よりも少し遅れて動くため、MACD線とシグナル線が交差するタイミングを売買判断に利用できます。

つまり、12・26・9は、

  • 12:短期の値動きを見る期間
  • 26:長期の値動きを見る期間
  • 9:MACD線をなめらかにする期間

と考えると分かりやすいです。

今回の検証では、この定番の12・26・9も含めて複数のMACD設定を比較します。

MACDの期間を変えるとどうなる?

MACDの期間を短くすると、直近の値動きに反応しやすくなり、クロスの回数も増えやすくなります。

一方、期間を長くすると値動きがなめらかになり、小さな変動には反応しにくくなります。

そのため、短い期間・長い期間のどちらが良いとは一概に言えません。

今回の記事では複数のMACD期間を5通貨で検証し、定番の12・26・9を含め、どの設定が成績良かったのかを比較していきます。

今回のMACD検証条件

ここからは、複数のMACD設定を実際の15分足データで比較します。

今回は5通貨・31種類のMACD設定・10種類のTP/SL・LONG/SHORTを組み合わせ、合計3,100ケースを検証します。

検証する5通貨・15分足・期間

今回検証する通貨は以下の5つです。

  • USDJPY
  • EURJPY
  • GBPJPY
  • EURUSD
  • AUDUSD

すべて15分足を使用し、検証期間は2024年1月1日〜2026年7月31日とします。

価格データにはASKを使用し、スプレッド・スリッページ・手数料などの取引コストは含めません。

検証するTP・SL

TP・SLは以下の10種類です。

TPSL
80pips10pips
100pips10pips
120pips10pips
150pips10pips
180pips10pips
100pips20pips
120pips20pips
120pips30pips
150pips30pips
150pips50pips

MACD設定だけでなく、TP・SLによる成績の違いも確認します。

LONG・SHORTのエントリー条件

エントリーには、MACD線とシグナル線のクロスを使用します。

LONGのエントリー条件

  • MACD線がシグナル線を下から上へ抜ける
  • 次の15分足の始値でエントリー

SHORTのエントリー条件

  • MACD線がシグナル線を上から下へ抜ける
  • 次の15分足の始値でエントリー

クロスが確定した次の足でエントリーすることで、未来の値動きを使わないようにしています。

TP・SLの決済ルール

エントリー後は、設定したTPまたはSLに到達した時点で決済します。

同じ15分足の中でTPとSLの両方に到達した場合は、どちらが先だったのか判断できないため、SLを優先します。

また、検証期間の終了時点でポジションが残っている場合は、最終足の終値で決済します。

3,100ケースの組み合わせ

今回の組み合わせは、

  • MACD設定:31種類
  • TP・SL:10種類
  • 通貨:5通貨
  • 売買方向:LONG・SHORT

です。

つまり、

  • 31 × 10 × 5 × 2 = 3,100ケース

を検証します。

すべての通貨・売買方向で同じ設定を使用し、成績の違いを比較します。

成績が良かった設定の選び方

3,100ケースの中から成績が良かった設定を選ぶ際は、合計pipsだけでは判断しません。

まず、

  • 合計pipsがプラス
  • PFが1.05以上
  • 取引回数が100回以上

を目安に候補を絞ります。

そのうえで、

  • 合計pips
  • PF
  • 最大ドローダウン
  • 平均損益
  • 取引回数

を確認し、利益とリスクのバランスを見ながら比較します。スクリプトでもこれらの指標を設定ごとに出力しています。

単純に合計pipsが大きい設定だけではなく、十分な取引回数があり、PFや最大ドローダウンも含めて成績が良かった設定を優先します。

5通貨のMACD検証結果

ここからは、3,100ケースのバックテスト結果から、成績が良かったMACD設定を通貨別に確認します。

今回は、合計pipsがプラス・PF1.05以上・取引回数100回以上を満たした設定を候補とし、その中から合計pips・PF・最大ドローダウン・平均損益・取引回数を確認しながらTOP5を選びました。

なお、基準を満たす設定がなかった場合は、無理にTOP5を作らず「基準を満たす設定なし」としています。

USDJPYの検証結果

USDJPY LONG:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
18/2216150/101,160+5,200.01.4929770
28/221680/101,580+5,170.01.3838410
38/2614150/101,187+5,090.01.4709790
48/261480/101,596+5,100.01.3744400
58/209120/101,496+5,190.01.3870750

1位は短期EMA8・長期EMA22・シグナル16・TP150・SL10です。

1,160回の取引で+5,200pips、PF1.4929となり、今回のUSDJPY LONGでは特に高い成績となりました。

また、TOP5すべてで短期EMA8・SL10が共通しています。

TP80・SL10の設定では最大ドローダウンが400〜410pipsまで抑えられているため、USDJPYでは利益だけでなくリスクとのバランスが良い設定も見つかりました。

USDJPY SHORT:基準を満たす設定なし

USDJPY SHORTでは、今回の基準をすべて満たす設定はありませんでした。

LONGでは多くの設定が好成績だった一方、SHORTでは合計pips・PF・取引回数を含めて見ると、有力な設定は残りませんでした。

今回のMACDクロスでは、USDJPYはLONGの方が相性が良かったと考えられます。

EURJPYの検証結果

EURJPY LONG:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
16/15980/101,987+5,240.01.3068710
26/159100/101,741+5,030.01.3273760
37/129100/101,753+4,910.01.3170670
48/209100/201,148+5,120.01.2801800
57/157100/101,769+4,750.01.3035600

1位は短期EMA6・長期EMA15・シグナル9・TP80・SL10でした。

1,987回の取引で+5,240pipsとなり、取引回数も十分に確保されています。

TOP5を見ると、短期EMA6〜8・長期EMA12〜20と、比較的短いMACD期間が上位に多く入っています。

また、TP80〜100・SL10の設定も多く、EURJPYでは短めのMACDと狭いSLの組み合わせが良い結果となりました。

EURJPY SHORT:基準を満たす設定なし

EURJPY SHORTでは、今回の基準を満たす設定はありませんでした。

LONGでは多くの有力設定が見つかった一方、SHORTでは安定した優位性を確認できませんでした。

GBPJPYの検証結果

GBPJPY LONG:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
16/224100/201,801+6,580.01.2275680
25/205100/201,809+6,540.01.2251660
36/224120/201,584+6,540.01.2494880
45/205120/201,579+6,500.01.2487860
56/224100/102,535+6,440.01.28671,060

1位は短期EMA6・長期EMA22・シグナル4・TP100・SL20です。

1,801回の取引で+6,580pips、最大DD680pipsとなりました。

GBPJPYでは、6・22・4と5・20・5がTOP5の大半を占めています。

また、1位と2位はTP100・SL20で、利益を大きく残しながら最大DDも比較的小さく抑えられています。

今回の5通貨LONGの中でも、GBPJPYは合計pipsが特に大きい結果となりました。

GBPJPY SHORT:基準を満たす設定なし

GBPJPY SHORTでは、今回の基準を満たす設定はありませんでした。

USDJPY・EURJPYと同様に、GBPJPYでも今回のMACDクロスはLONG側の成績が明確に優勢です。

EURUSDの検証結果

EURUSD LONG:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
13/203150/30297+1,399.41.1952750
216/2012150/50168+1,200.01.2000650
316/2111150/50169+1,150.01.1901650
414/2214150/50175+1,050.01.1667700
57/187150/50177+1,003.31.1580750

1位は短期EMA3・長期EMA20・シグナル3・TP150・SL30でした。

297回の取引で+1,399.4pipsとなり、EURUSD LONGでは最も大きな利益となっています。

一方、2位以下ではTP150・SL50が共通しています。

EURUSDはJPY系3通貨と比べると取引回数が少なく、設定による成績差も大きいため、利益だけでなくPFや最大DDもあわせて確認する必要があります。

EURUSD SHORT:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
120/306150/10609+1,145.51.2035480
216/386150/10612+1,115.51.1971500
316/386180/10490+1,215.51.2660700
416/2111150/10615+1,058.81.1860580
58/2216150/10621+998.81.1737510

EURUSD SHORTでは、JPY系3通貨とは異なり複数の有力設定が見つかりました。

1位は短期EMA20・長期EMA30・シグナル6・TP150・SL10です。

+1,145.5pips、PF1.2035、最大DD480pipsとなっており、利益と最大DDのバランスが良い結果でした。

合計pipsとPFだけを見ると3位の16・38・6・TP180・SL10が最も高いものの、最大DDも700pipsまで増えています。

そのため今回は、総合的なバランスから20・30・6を1位としています。

AUDUSDの検証結果

AUDUSD LONG:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
13/153120/10614+983.31.1759590
23/203100/10751+977.01.1452570
33/153100/10751+977.01.1452590
43/403100/10743+947.01.1422570
58/351280/10779+850.01.1245620

1位は短期EMA3・長期EMA15・シグナル3・TP120・SL10です。

614回の取引で+983.3pips、PF1.1759となりました。

AUDUSD LONGでは、TOP4のうち4設定すべてで短期EMA3またはシグナル3が使われています。

また、TOP5すべてでSL10となっており、AUDUSDでは狭いSLが良い結果につながっています。

AUDUSD SHORT:TOP5

順位短期/長期
EMA
シグナルTP/SL取引回数合計pipsPF最大DD
116/2012180/10382+571.41.1596850
216/2111180/10389+496.41.1360880
310/2214180/10401+376.41.0998990
412/269180/10424+336.41.0843940
510/2612180/10388+316.41.0867960

AUDUSD SHORTでは、基準を満たした設定はあるものの、LONGと比べると全体的に成績は弱めです。

1位は短期EMA16・長期EMA20・シグナル12・TP180・SL10で、382回の取引で+571.4pips、PF1.1596となりました。

また、TOP5すべてでTP180・SL10が選ばれています。

一方で最大DDは850〜990pipsと大きく、合計pipsもLONGより小さいため、AUDUSD SHORTについては慎重に評価する必要があります。

今回の結果からも、同じMACD設定をLONG・SHORTの両方へそのまま使うのではなく、売買方向ごとに分けて検証することが重要だと分かります。

5通貨のMACD検証結果を比較

5通貨の結果を比較すると、すべての通貨・売買方向で共通して成績が良いMACD設定はありませんでした。

また、LONGとSHORTでも結果に大きな差があり、MACD期間だけでなく、売買方向やTP・SLも含めて検証する必要があると分かりました。

LONGとSHORTで成績に大きな差が出た

今回の検証では、全体的にLONGの方が成績が良い結果となりました。

特にUSDJPY・EURJPY・GBPJPYでは、LONGに複数の有力設定が見つかった一方、SHORTでは今回の基準を満たす設定がありませんでした。

一方で、EURUSD・AUDUSDではSHORTにも候補が残っています。

この結果から、LONGで成績が良かった設定をそのままSHORTにも使うのではなく、売買方向ごとに分けて検証することが重要だと分かります。

通貨ごとに成績が良いMACD設定は異なる

上位設定を比較すると、成績が良かったMACD期間は通貨ごとに異なりました。

短い期間のMACDが上位に入りやすい通貨もあれば、比較的長めの期間が良い結果となった通貨もあります。

また、定番の12・26・9も候補には入りましたが、すべての通貨で最も良い設定になったわけではありません。

そのため、12・26・9を基準にしつつ、実際に使用する通貨で複数の設定を比較するのがよさそうです。

TP・SLも通貨ごとに調整が必要

TP・SLについても、すべての通貨で共通する組み合わせはありませんでした。

USDJPY・EURJPY・AUDUSDではSL10の設定が上位に多かった一方、GBPJPYではSL20、EURUSD LONGではさらに広めのSLが上位に入っています。

つまり、MACD期間だけを変更しても十分ではなく、TP・SLまで含めて1つの売買ルールとして検証することが重要です。

今回の結果からも、MACDを使う際は「定番設定をそのまま使う」のではなく、通貨・売買方向・TP/SLを組み合わせて調整する必要があると考えられます。

MACD設定を検証するときのポイント

MACDのバックテストでは、単純に合計pipsが最も大きい設定だけを見るのではなく、その成績が安定しているかも確認することが重要です。

今回の検証でも、利益だけでなくPF・最大ドローダウン・取引回数などをあわせて確認しています。

1位の設定だけで判断しない

バックテストでは、特定の設定だけが偶然高い成績になることがあります。

そのため、1位の設定だけを見るのではなく、近いMACD期間やTP・SLでも同じように利益が出ているかを確認することが重要です。

周辺の設定でも成績が良ければ、特定の数値だけに依存している可能性を抑えやすくなります。

取引回数・PF・最大DDも確認する

合計pipsが大きくても、取引回数が少なかったり、最大ドローダウンが大きかったりする設定には注意が必要です。

今回の記事では、主に以下を確認しながら設定を比較しています。

  • 合計pips
  • PF
  • 最大ドローダウン
  • 平均損益
  • 取引回数

利益の大きさだけでなく、どの程度のリスクでその利益を出しているのかを見ることが重要です。

過去データに最適化しすぎない

複数のMACD期間やTP・SLを試すほど、過去の相場だけで偶然成績が良かった設定が見つかる可能性も高くなります。

そのため、高成績な設定が見つかっても、その結果だけで「最適」と判断するのは避けた方がよいです。

実際に使用する場合は、期間を分けた検証や別期間での再検証を行い、特定の相場だけで成績が良くなっていないか確認することが重要です。

MACDをFXで使う際の注意点

MACDはトレンドの方向や売買タイミングを確認する際に便利な指標ですが、MACDだけで安定して利益を出せるとは限りません。

実際にFXで使用する際は、以下の点に注意が必要です。

MACDだけでエントリーしない

ゴールデンクロスやデッドクロスが発生しても、必ずその方向へ価格が動くわけではありません。

実際に使用する場合は、トレンドの方向やローソク足の値動きなどもあわせて確認することが重要です。

今回の検証ではMACD設定ごとの違いを比較するため、MACD線とシグナル線のクロスを中心としたシンプルな条件を使用しています。

レンジ相場ではダマシに注意する

値動きの小さいレンジ相場では、MACD線とシグナル線が何度も交差することがあります。

そのため、ゴールデンクロス後にすぐ下落したり、デッドクロス後にすぐ上昇したりするダマシが発生する場合があります。

MACDを使う際は、現在の相場がトレンドなのかレンジなのかも確認した方がよいでしょう。

MACDはシグナルが遅れることがある

MACDはEMAをもとに計算するため、価格の動きに対してシグナルが遅れる場合があります。

特にMACDの期間を長くすると値動きがなめらかになる一方で、トレンドが始まってからクロスが発生するまで時間がかかることがあります

反対に期間を短くすると反応は速くなりますが、小さな値動きにも反応しやすくなります。

スプレッド・スリッページは今回の検証に含まない

今回のバックテストではASK価格を使用し、スプレッド・スリッページ・手数料などの取引コストは考慮していません。

そのため、実際の取引ではバックテストよりも成績が悪くなる可能性があります。

特に取引回数が多い設定や、1取引あたりの平均利益が小さい設定は取引コストの影響を受けやすいため、実運用前に確認することが重要です。

まとめ

今回は、USDJPY・EURJPY・GBPJPY・EURUSD・AUDUSDの5通貨を対象に、31種類のMACD設定 × 10種類のTP・SL × LONG/SHORTを組み合わせ、合計3,100ケースを検証しました。

今回の検証から分かった主なポイントは、以下のとおりです。

  • 定番のMACD12・26・9がすべての通貨で最適とは限らない
  • USDJPY・EURJPY・GBPJPYでは、LONGの方が良い結果になった
  • EURUSD・AUDUSDではSHORTにも候補が見つかった
  • 通貨によって成績が良いMACD期間は異なる
  • MACD期間だけでなく、TP・SLによっても成績が大きく変わる
  • 1位の設定だけでなく、PF・最大DD・取引回数なども確認する必要がある

MACDには12・26・9という定番設定がありますが、今回の結果を見る限り、実際に使用する通貨や売買方向に合わせて検証することが重要です。

また、バックテストで良い結果が出た設定をそのまま実運用するのではなく、取引コストや別期間での成績なども確認する必要があります。

今回の検証結果は、「MACDは何期間に設定すればいいのか」と悩んでいる方の一つの参考として活用してみてください。

-FX / EA, Python